]> granicus.if.org Git - imagemagick/blob - coders/png.c
...
[imagemagick] / coders / png.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            PPPP   N   N   GGGG                              %
7 %                            P   P  NN  N  G                                  %
8 %                            PPPP   N N N  G  GG                              %
9 %                            P      N  NN  G   G                              %
10 %                            P      N   N   GGG                               %
11 %                                                                             %
12 %                                                                             %
13 %              Read/Write Portable Network Graphics Image Format              %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                           Glenn Randers-Pehrson                             %
18 %                               November 1997                                 %
19 %                                                                             %
20 %                                                                             %
21 %  Copyright 1999-2018 ImageMagick Studio LLC, a non-profit organization      %
22 %  dedicated to making software imaging solutions freely available.           %
23 %                                                                             %
24 %  You may not use this file except in compliance with the License.  You may  %
25 %  obtain a copy of the License at                                            %
26 %                                                                             %
27 %    https://imagemagick.org/script/license.php                               %
28 %                                                                             %
29 %  Unless required by applicable law or agreed to in writing, software        %
30 %  distributed under the License is distributed on an "AS IS" BASIS,          %
31 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32 %  See the License for the specific language governing permissions and        %
33 %  limitations under the License.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39
40 #define IM
41
42 \f
43 /*
44   Include declarations.
45 */
46 #include "MagickCore/studio.h"
47 #include "MagickCore/artifact.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/blob.h"
50 #include "MagickCore/blob-private.h"
51 #include "MagickCore/cache.h"
52 #include "MagickCore/channel.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colormap.h"
56 #include "MagickCore/colormap-private.h"
57 #include "MagickCore/colorspace.h"
58 #include "MagickCore/colorspace-private.h"
59 #include "MagickCore/constitute.h"
60 #include "MagickCore/enhance.h"
61 #include "MagickCore/exception.h"
62 #include "MagickCore/exception-private.h"
63 #include "MagickCore/geometry.h"
64 #include "MagickCore/histogram.h"
65 #include "MagickCore/image.h"
66 #include "MagickCore/image-private.h"
67 #include "MagickCore/layer.h"
68 #include "MagickCore/list.h"
69 #include "MagickCore/log.h"
70 #include "MagickCore/magick.h"
71 #include "MagickCore/magick-private.h"
72 #include "MagickCore/memory_.h"
73 #include "MagickCore/memory-private.h"
74 #include "MagickCore/module.h"
75 #include "MagickCore/monitor.h"
76 #include "MagickCore/monitor-private.h"
77 #include "MagickCore/option.h"
78 #include "MagickCore/pixel.h"
79 #include "MagickCore/pixel-accessor.h"
80 #include "MagickCore/profile.h"
81 #include "MagickCore/property.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/resource_.h"
84 #include "MagickCore/semaphore.h"
85 #include "MagickCore/quantum-private.h"
86 #include "MagickCore/static.h"
87 #include "MagickCore/statistic.h"
88 #include "MagickCore/string_.h"
89 #include "MagickCore/string-private.h"
90 #include "MagickCore/transform.h"
91 #include "MagickCore/utility.h"
92 #if defined(MAGICKCORE_PNG_DELEGATE)
93
94 /* Suppress libpng pedantic warnings that were added in
95  * libpng-1.2.41 and libpng-1.4.0.  If you are working on
96  * migration to libpng-1.5, remove these defines and then
97  * fix any code that generates warnings.
98  */
99 /* #define PNG_DEPRECATED   Use of this function is deprecated */
100 /* #define PNG_USE_RESULT   The result of this function must be checked */
101 /* #define PNG_NORETURN     This function does not return */
102 /* #define PNG_ALLOCATED    The result of the function is new memory */
103 /* #define PNG_DEPSTRUCT    Access to this struct member is deprecated */
104
105 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
106 #define PNG_PTR_NORETURN
107
108 #include <png.h>
109 #include <zlib.h>
110 \f
111 /* ImageMagick differences */
112 #define first_scene scene
113
114 #if PNG_LIBPNG_VER > 10011
115 /*
116   Optional declarations. Define or undefine them as you like.
117 */
118 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
119
120 /*
121   Features under construction.  Define these to work on them.
122 */
123 #undef MNG_OBJECT_BUFFERS
124 #undef MNG_BASI_SUPPORTED
125 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
126 #define MNG_INSERT_LAYERS   /* Troublesome, but seem to work as of 5.4.4 */
127 #if defined(MAGICKCORE_JPEG_DELEGATE)
128 #  define JNG_SUPPORTED /* Not finished as of 5.5.2.  See "To do" comments. */
129 #endif
130 #if !defined(RGBColorMatchExact)
131 #define IsPNGColorEqual(color,target) \
132        (((color).red == (target).red) && \
133         ((color).green == (target).green) && \
134         ((color).blue == (target).blue))
135 #endif
136
137 /* Table of recognized sRGB ICC profiles */
138 struct sRGB_info_struct
139 {
140     png_uint_32 len;
141     png_uint_32 crc;
142     png_byte intent;
143 };
144
145 const struct sRGB_info_struct sRGB_info[] =
146 {
147     /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
148     { 3048, 0x3b8772b9UL, 0},
149
150     /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
151     { 3052, 0x427ebb21UL, 1},
152
153     /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
154     {60988, 0x306fd8aeUL, 0},
155
156     /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
157      {60960, 0xbbef7812UL, 0},
158
159     /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
160      { 3024, 0x5d5129ceUL, 1},
161
162      /* HP-Microsoft sRGB v2 perceptual */
163      { 3144, 0x182ea552UL, 0},
164
165      /* HP-Microsoft sRGB v2 media-relative */
166      { 3144, 0xf29e526dUL, 1},
167
168      /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
169      {  524, 0xd4938c39UL, 0},
170
171      /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
172      { 3212, 0x034af5a1UL, 0},
173
174      /* Not recognized */
175      {    0, 0x00000000UL, 0},
176 };
177
178 /* Macros for left-bit-replication to ensure that pixels
179  * and PixelInfos all have the same image->depth, and for use
180  * in PNG8 quantization.
181  */
182
183 /* LBR01: Replicate top bit */
184
185 #define LBR01PacketRed(pixelpacket) \
186      (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
187         0 : QuantumRange);
188
189 #define LBR01PacketGreen(pixelpacket) \
190      (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
191         0 : QuantumRange);
192
193 #define LBR01PacketBlue(pixelpacket) \
194      (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
195         0 : QuantumRange);
196
197 #define LBR01PacketAlpha(pixelpacket) \
198      (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
199         0 : QuantumRange);
200
201 #define LBR01PacketRGB(pixelpacket) \
202         { \
203         LBR01PacketRed((pixelpacket)); \
204         LBR01PacketGreen((pixelpacket)); \
205         LBR01PacketBlue((pixelpacket)); \
206         }
207
208 #define LBR01PacketRGBA(pixelpacket) \
209         { \
210         LBR01PacketRGB((pixelpacket)); \
211         LBR01PacketAlpha((pixelpacket)); \
212         }
213
214 #define LBR01PixelRed(pixel) \
215         (SetPixelRed(image, \
216         ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
217         0 : QuantumRange,(pixel)));
218
219 #define LBR01PixelGreen(pixel) \
220         (SetPixelGreen(image, \
221         ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
222         0 : QuantumRange,(pixel)));
223
224 #define LBR01PixelBlue(pixel) \
225         (SetPixelBlue(image, \
226         ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
227         0 : QuantumRange,(pixel)));
228
229 #define LBR01PixelAlpha(pixel) \
230         (SetPixelAlpha(image, \
231         ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
232         0 : QuantumRange,(pixel)));
233
234 #define LBR01PixelRGB(pixel) \
235         { \
236         LBR01PixelRed((pixel)); \
237         LBR01PixelGreen((pixel)); \
238         LBR01PixelBlue((pixel)); \
239         }
240
241 #define LBR01PixelRGBA(pixel) \
242         { \
243         LBR01PixelRGB((pixel)); \
244         LBR01PixelAlpha((pixel)); \
245         }
246
247 /* LBR02: Replicate top 2 bits */
248
249 #define LBR02PacketRed(pixelpacket) \
250    { \
251      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
252      (pixelpacket).red=ScaleCharToQuantum( \
253        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
254    }
255 #define LBR02PacketGreen(pixelpacket) \
256    { \
257      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
258      (pixelpacket).green=ScaleCharToQuantum( \
259        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
260    }
261 #define LBR02PacketBlue(pixelpacket) \
262    { \
263      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
264      (pixelpacket).blue=ScaleCharToQuantum( \
265        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
266    }
267 #define LBR02PacketAlpha(pixelpacket) \
268    { \
269      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
270      (pixelpacket).alpha=ScaleCharToQuantum( \
271        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
272    }
273
274 #define LBR02PacketRGB(pixelpacket) \
275         { \
276         LBR02PacketRed((pixelpacket)); \
277         LBR02PacketGreen((pixelpacket)); \
278         LBR02PacketBlue((pixelpacket)); \
279         }
280
281 #define LBR02PacketRGBA(pixelpacket) \
282         { \
283         LBR02PacketRGB((pixelpacket)); \
284         LBR02PacketAlpha((pixelpacket)); \
285         }
286
287 #define LBR02PixelRed(pixel) \
288    { \
289      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
290        & 0xc0; \
291      SetPixelRed(image, ScaleCharToQuantum( \
292        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
293        (pixel)); \
294    }
295 #define LBR02PixelGreen(pixel) \
296    { \
297      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
298        & 0xc0; \
299      SetPixelGreen(image, ScaleCharToQuantum( \
300        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
301        (pixel)); \
302    }
303 #define LBR02PixelBlue(pixel) \
304    { \
305      unsigned char lbr_bits= \
306        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
307      SetPixelBlue(image, ScaleCharToQuantum( \
308        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
309        (pixel)); \
310    }
311 #define LBR02PixelAlpha(pixel) \
312    { \
313      unsigned char lbr_bits= \
314        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
315      SetPixelAlpha(image, ScaleCharToQuantum( \
316        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
317        (pixel) ); \
318    }
319
320 #define LBR02PixelRGB(pixel) \
321         { \
322         LBR02PixelRed((pixel)); \
323         LBR02PixelGreen((pixel)); \
324         LBR02PixelBlue((pixel)); \
325         }
326
327 #define LBR02PixelRGBA(pixel) \
328         { \
329         LBR02PixelRGB((pixel)); \
330         LBR02PixelAlpha((pixel)); \
331         }
332
333 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
334    PNG8 quantization) */
335
336 #define LBR03PacketRed(pixelpacket) \
337    { \
338      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
339      (pixelpacket).red=ScaleCharToQuantum( \
340        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
341    }
342 #define LBR03PacketGreen(pixelpacket) \
343    { \
344      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
345      (pixelpacket).green=ScaleCharToQuantum( \
346        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
347    }
348 #define LBR03PacketBlue(pixelpacket) \
349    { \
350      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
351      (pixelpacket).blue=ScaleCharToQuantum( \
352        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
353    }
354
355 #define LBR03PacketRGB(pixelpacket) \
356         { \
357         LBR03PacketRed((pixelpacket)); \
358         LBR03PacketGreen((pixelpacket)); \
359         LBR03PacketBlue((pixelpacket)); \
360         }
361
362 #define LBR03PixelRed(pixel) \
363    { \
364      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
365        & 0xe0; \
366      SetPixelRed(image, ScaleCharToQuantum( \
367        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
368    }
369 #define LBR03Green(pixel) \
370    { \
371      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
372        & 0xe0; \
373      SetPixelGreen(image, ScaleCharToQuantum( \
374        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
375    }
376 #define LBR03Blue(pixel) \
377    { \
378      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
379        & 0xe0; \
380      SetPixelBlue(image, ScaleCharToQuantum( \
381        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
382    }
383
384 #define LBR03RGB(pixel) \
385         { \
386         LBR03PixelRed((pixel)); \
387         LBR03Green((pixel)); \
388         LBR03Blue((pixel)); \
389         }
390
391 /* LBR04: Replicate top 4 bits */
392
393 #define LBR04PacketRed(pixelpacket) \
394    { \
395      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
396      (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
397    }
398 #define LBR04PacketGreen(pixelpacket) \
399    { \
400      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
401      (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
402    }
403 #define LBR04PacketBlue(pixelpacket) \
404    { \
405      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
406      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
407    }
408 #define LBR04PacketAlpha(pixelpacket) \
409    { \
410      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
411      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
412    }
413
414 #define LBR04PacketRGB(pixelpacket) \
415         { \
416         LBR04PacketRed((pixelpacket)); \
417         LBR04PacketGreen((pixelpacket)); \
418         LBR04PacketBlue((pixelpacket)); \
419         }
420
421 #define LBR04PacketRGBA(pixelpacket) \
422         { \
423         LBR04PacketRGB((pixelpacket)); \
424         LBR04PacketAlpha((pixelpacket)); \
425         }
426
427 #define LBR04PixelRed(pixel) \
428    { \
429      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
430        & 0xf0; \
431      SetPixelRed(image,\
432        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
433    }
434 #define LBR04PixelGreen(pixel) \
435    { \
436      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
437        & 0xf0; \
438      SetPixelGreen(image,\
439        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
440    }
441 #define LBR04PixelBlue(pixel) \
442    { \
443      unsigned char lbr_bits= \
444        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
445      SetPixelBlue(image,\
446        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
447    }
448 #define LBR04PixelAlpha(pixel) \
449    { \
450      unsigned char lbr_bits= \
451        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
452      SetPixelAlpha(image,\
453        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
454    }
455
456 #define LBR04PixelRGB(pixel) \
457         { \
458         LBR04PixelRed((pixel)); \
459         LBR04PixelGreen((pixel)); \
460         LBR04PixelBlue((pixel)); \
461         }
462
463 #define LBR04PixelRGBA(pixel) \
464         { \
465         LBR04PixelRGB((pixel)); \
466         LBR04PixelAlpha((pixel)); \
467         }
468
469 /*
470   Establish thread safety.
471   setjmp/longjmp is claimed to be safe on these platforms:
472   setjmp/longjmp is alleged to be unsafe on these platforms:
473 */
474 #ifdef PNG_SETJMP_SUPPORTED
475 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
476 #   define IMPNG_SETJMP_NOT_THREAD_SAFE
477 # endif
478
479 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
480 static SemaphoreInfo
481   *ping_semaphore = (SemaphoreInfo *) NULL;
482 # endif
483 #endif
484
485 /*
486   This temporary until I set up malloc'ed object attributes array.
487   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
488   waste more memory.
489 */
490 #define MNG_MAX_OBJECTS 256
491
492 /*
493   If this not defined, spec is interpreted strictly.  If it is
494   defined, an attempt will be made to recover from some errors,
495   including
496       o global PLTE too short
497 */
498 #undef MNG_LOOSE
499
500 /*
501   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
502   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
503   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
504   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
505   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
506   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
507   will be enabled by default in libpng-1.2.0.
508 */
509 #ifdef PNG_MNG_FEATURES_SUPPORTED
510 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
511 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
512 #  endif
513 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
514 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
515 #  endif
516 #endif
517
518 /*
519   Maximum valid size_t in PNG/MNG chunks is (2^31)-1
520   This macro is only defined in libpng-1.0.3 and later.
521   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
522 */
523 #ifndef PNG_UINT_31_MAX
524 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
525 #endif
526
527 /*
528   Constant strings for known chunk types.  If you need to add a chunk,
529   add a string holding the name here.   To make the code more
530   portable, we use ASCII numbers like this, not characters.
531 */
532
533 static const png_byte mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
534 static const png_byte mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
535 static const png_byte mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
536 static const png_byte mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
537 static const png_byte mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
538 static const png_byte mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
539 static const png_byte mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
540 static const png_byte mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
541 static const png_byte mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
542 static const png_byte mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
543 static const png_byte mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
544 static const png_byte mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
545 static const png_byte mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
546 static const png_byte mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
547 static const png_byte mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
548 static const png_byte mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
549 static const png_byte mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
550 static const png_byte mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
551 static const png_byte mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
552 static const png_byte mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
553 static const png_byte mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
554 static const png_byte mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
555 static const png_byte mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
556 static const png_byte mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
557 static const png_byte mng_caNv[5]={ 99,  97,  78, 118, (png_byte) '\0'};
558 static const png_byte mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
559 static const png_byte mng_eXIf[5]={101,  88,  73, 102, (png_byte) '\0'};
560 static const png_byte mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
561 static const png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
562 static const png_byte mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
563 static const png_byte mng_orNT[5]={111, 114,  78,  84, (png_byte) '\0'};
564 static const png_byte mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
565 static const png_byte mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
566 static const png_byte mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
567 static const png_byte mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
568 static const png_byte mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
569 static const png_byte mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
570
571 #if defined(JNG_SUPPORTED)
572 static const png_byte mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
573 static const png_byte mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
574 static const png_byte mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
575 static const png_byte mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
576 static const png_byte mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
577 static const png_byte mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
578 #endif
579
580 #if 0
581 /* Other known chunks that are not yet supported by ImageMagick: */
582 static const png_byte mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
583 static const png_byte mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
584 static const png_byte mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
585 static const png_byte mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
586 static const png_byte mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
587 static const png_byte mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
588 static const png_byte mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
589 #endif
590
591 typedef struct _MngBox
592 {
593   long
594     left,
595     right,
596     top,
597     bottom;
598 } MngBox;
599
600 typedef struct _MngPair
601 {
602   volatile long
603     a,
604     b;
605 } MngPair;
606
607 #ifdef MNG_OBJECT_BUFFERS
608 typedef struct _MngBuffer
609 {
610
611   size_t
612     height,
613     width;
614
615   Image
616     *image;
617
618   png_color
619     plte[256];
620
621   int
622     reference_count;
623
624   unsigned char
625     alpha_sample_depth,
626     compression_method,
627     color_type,
628     concrete,
629     filter_method,
630     frozen,
631     image_type,
632     interlace_method,
633     pixel_sample_depth,
634     plte_length,
635     sample_depth,
636     viewable;
637 } MngBuffer;
638 #endif
639
640 typedef struct _MngInfo
641 {
642
643 #ifdef MNG_OBJECT_BUFFERS
644   MngBuffer
645     *ob[MNG_MAX_OBJECTS];
646 #endif
647
648   Image *
649     image;
650
651   RectangleInfo
652     page;
653
654   int
655     adjoin,
656 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
657     bytes_in_read_buffer,
658     found_empty_plte,
659 #endif
660     equal_backgrounds,
661     equal_chrms,
662     equal_gammas,
663 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
664     defined(PNG_MNG_FEATURES_SUPPORTED)
665     equal_palettes,
666 #endif
667     equal_physs,
668     equal_srgbs,
669     framing_mode,
670     have_global_bkgd,
671     have_global_chrm,
672     have_global_gama,
673     have_global_phys,
674     have_global_sbit,
675     have_global_srgb,
676     have_saved_bkgd_index,
677     have_write_global_chrm,
678     have_write_global_gama,
679     have_write_global_plte,
680     have_write_global_srgb,
681     need_fram,
682     object_id,
683     old_framing_mode,
684     saved_bkgd_index;
685
686   int
687     new_number_colors;
688
689   ssize_t
690     image_found,
691     loop_count[256],
692     loop_iteration[256],
693     scenes_found,
694     x_off[MNG_MAX_OBJECTS],
695     y_off[MNG_MAX_OBJECTS];
696
697   MngBox
698     clip,
699     frame,
700     image_box,
701     object_clip[MNG_MAX_OBJECTS];
702
703   unsigned char
704     /* These flags could be combined into one byte */
705     exists[MNG_MAX_OBJECTS],
706     frozen[MNG_MAX_OBJECTS],
707     loop_active[256],
708     invisible[MNG_MAX_OBJECTS],
709     viewable[MNG_MAX_OBJECTS];
710
711   MagickOffsetType
712     loop_jump[256];
713
714   png_colorp
715     global_plte;
716
717   png_color_8
718     global_sbit;
719
720   png_byte
721 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
722     read_buffer[8],
723 #endif
724     global_trns[256];
725
726   float
727     global_gamma;
728
729   ChromaticityInfo
730     global_chrm;
731
732   RenderingIntent
733     global_srgb_intent;
734
735   unsigned long
736     delay,
737     global_plte_length,
738     global_trns_length,
739     global_x_pixels_per_unit,
740     global_y_pixels_per_unit,
741     mng_width,
742     mng_height,
743     ticks_per_second;
744
745   MagickBooleanType
746     need_blob;
747
748   unsigned int
749     IsPalette,
750     global_phys_unit_type,
751     basi_warning,
752     clon_warning,
753     dhdr_warning,
754     jhdr_warning,
755     magn_warning,
756     past_warning,
757     phyg_warning,
758     phys_warning,
759     sbit_warning,
760     show_warning,
761     mng_type,
762     write_mng,
763     write_png_colortype,
764     write_png_depth,
765     write_png_compression_level,
766     write_png_compression_strategy,
767     write_png_compression_filter,
768     write_png8,
769     write_png24,
770     write_png32,
771     write_png48,
772     write_png64;
773
774 #ifdef MNG_BASI_SUPPORTED
775   unsigned long
776     basi_width,
777     basi_height;
778
779   unsigned int
780     basi_depth,
781     basi_color_type,
782     basi_compression_method,
783     basi_filter_type,
784     basi_interlace_method,
785     basi_red,
786     basi_green,
787     basi_blue,
788     basi_alpha,
789     basi_viewable;
790 #endif
791
792   png_uint_16
793     magn_first,
794     magn_last,
795     magn_mb,
796     magn_ml,
797     magn_mr,
798     magn_mt,
799     magn_mx,
800     magn_my,
801     magn_methx,
802     magn_methy;
803
804   PixelInfo
805     mng_global_bkgd;
806
807   /* Added at version 6.6.6-7 */
808   MagickBooleanType
809     ping_exclude_bKGD,
810     ping_exclude_cHRM,
811     ping_exclude_date,
812     ping_exclude_eXIf,
813     ping_exclude_EXIF,
814     ping_exclude_gAMA,
815     ping_exclude_iCCP,
816     /* ping_exclude_iTXt, */
817     ping_exclude_oFFs,
818     ping_exclude_pHYs,
819     ping_exclude_sRGB,
820     ping_exclude_tEXt,
821     ping_exclude_tRNS,
822     ping_exclude_caNv,
823     ping_exclude_zCCP, /* hex-encoded iCCP */
824     ping_exclude_zTXt,
825     ping_preserve_colormap,
826   /* Added at version 6.8.5-7 */
827     ping_preserve_iCCP,
828   /* Added at version 6.8.9-9 */
829     ping_exclude_tIME;
830
831 } MngInfo;
832 #endif /* VER */
833 \f
834 /*
835   Forward declarations.
836 */
837 static MagickBooleanType
838   WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
839
840 static MagickBooleanType
841   WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
842
843 #if defined(JNG_SUPPORTED)
844 static MagickBooleanType
845   WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
846 #endif
847
848 #if PNG_LIBPNG_VER > 10011
849
850
851 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
852 static MagickBooleanType
853 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
854 {
855     /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
856      *
857      * This is true if the high byte and the next highest byte of
858      * each sample of the image, the colormap, and the background color
859      * are equal to each other.  We check this by seeing if the samples
860      * are unchanged when we scale them down to 8 and back up to Quantum.
861      *
862      * We don't use the method GetImageDepth() because it doesn't check
863      * background and doesn't handle PseudoClass specially.
864      */
865
866 #define QuantumToCharToQuantumEqQuantum(quantum) \
867  ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
868
869     MagickBooleanType
870       ok_to_reduce=MagickFalse;
871
872     if (image->depth >= 16)
873       {
874
875         const Quantum
876           *p;
877
878         ok_to_reduce=
879            QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
880            QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
881            QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
882            MagickTrue : MagickFalse;
883
884         if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
885           {
886             int indx;
887
888             for (indx=0; indx < (ssize_t) image->colors; indx++)
889               {
890                 ok_to_reduce=(
891                    QuantumToCharToQuantumEqQuantum(
892                    image->colormap[indx].red) &&
893                    QuantumToCharToQuantumEqQuantum(
894                    image->colormap[indx].green) &&
895                    QuantumToCharToQuantumEqQuantum(
896                    image->colormap[indx].blue)) ?
897                    MagickTrue : MagickFalse;
898
899                 if (ok_to_reduce == MagickFalse)
900                    break;
901               }
902           }
903
904         if ((ok_to_reduce != MagickFalse) &&
905             (image->storage_class != PseudoClass))
906           {
907             ssize_t
908               y;
909
910             register ssize_t
911               x;
912
913             for (y=0; y < (ssize_t) image->rows; y++)
914             {
915               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
916
917               if (p == (const Quantum *) NULL)
918                 {
919                   ok_to_reduce = MagickFalse;
920                   break;
921                 }
922
923               for (x=(ssize_t) image->columns-1; x >= 0; x--)
924               {
925                 ok_to_reduce=
926                    QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
927                    QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
928                    QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
929                    MagickTrue : MagickFalse;
930
931                 if (ok_to_reduce == MagickFalse)
932                   break;
933
934                 p+=GetPixelChannels(image);
935               }
936               if (x >= 0)
937                 break;
938             }
939           }
940
941         if (ok_to_reduce != MagickFalse)
942           {
943             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
944                 "    OK to reduce PNG bit depth to 8 without loss of info");
945           }
946         else
947           {
948             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
949                 "    Not OK to reduce PNG bit depth to 8 without losing info");
950           }
951       }
952
953     return ok_to_reduce;
954 }
955 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
956
957 static const char* PngColorTypeToString(const unsigned int color_type)
958 {
959   const char
960     *result = "Unknown";
961
962   switch (color_type)
963     {
964     case PNG_COLOR_TYPE_GRAY:
965       result = "Gray";
966       break;
967     case PNG_COLOR_TYPE_GRAY_ALPHA:
968       result = "Gray+Alpha";
969       break;
970     case PNG_COLOR_TYPE_PALETTE:
971       result = "Palette";
972       break;
973     case PNG_COLOR_TYPE_RGB:
974       result = "RGB";
975       break;
976     case PNG_COLOR_TYPE_RGB_ALPHA:
977       result = "RGB+Alpha";
978       break;
979     }
980
981   return result;
982 }
983
984 static int
985 Magick_Orientation_to_Exif_Orientation(const OrientationType orientation)
986 {
987   switch (orientation)
988   {
989     /* Convert to Exif orientations as defined in "Exchangeable image file
990      * format for digital still cameras: Exif Version 2.31",
991      * http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
992      */
993
994     case TopLeftOrientation:
995        return 1;
996     case TopRightOrientation:
997        return 2;
998     case BottomRightOrientation:
999        return 3;
1000     case BottomLeftOrientation:
1001        return 4;
1002     case LeftTopOrientation:
1003        return 5;
1004     case RightTopOrientation:
1005        return 6;
1006     case RightBottomOrientation:
1007        return 7;
1008     case LeftBottomOrientation:
1009        return 8;
1010     case UndefinedOrientation:
1011     default:
1012        return 0;
1013   }
1014 }
1015 static OrientationType
1016 Magick_Orientation_from_Exif_Orientation(const int orientation)
1017 {
1018   switch (orientation)
1019   {
1020     case 1:
1021       return TopLeftOrientation;
1022     case 2:
1023       return TopRightOrientation;
1024     case 3:
1025       return BottomRightOrientation;
1026     case 4:
1027       return BottomLeftOrientation;
1028     case 5:
1029       return LeftTopOrientation;
1030     case 6:
1031       return RightTopOrientation;
1032     case 7:
1033       return RightBottomOrientation;
1034     case 8:
1035       return LeftBottomOrientation;
1036     case 0:
1037     default:
1038       return UndefinedOrientation;
1039   }
1040 }
1041
1042 static int
1043 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1044 {
1045   switch (intent)
1046   {
1047     case PerceptualIntent:
1048        return 0;
1049
1050     case RelativeIntent:
1051        return 1;
1052
1053     case SaturationIntent:
1054        return 2;
1055
1056     case AbsoluteIntent:
1057        return 3;
1058
1059     default:
1060        return -1;
1061   }
1062 }
1063
1064 static RenderingIntent
1065 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1066 {
1067   switch (ping_intent)
1068   {
1069     case 0:
1070       return PerceptualIntent;
1071
1072     case 1:
1073       return RelativeIntent;
1074
1075     case 2:
1076       return SaturationIntent;
1077
1078     case 3:
1079       return AbsoluteIntent;
1080
1081     default:
1082       return UndefinedIntent;
1083     }
1084 }
1085
1086 static const char *
1087 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1088 {
1089   switch (ping_intent)
1090   {
1091     case 0:
1092       return "Perceptual Intent";
1093
1094     case 1:
1095       return "Relative Intent";
1096
1097     case 2:
1098       return "Saturation Intent";
1099
1100     case 3:
1101       return "Absolute Intent";
1102
1103     default:
1104       return "Undefined Intent";
1105     }
1106 }
1107
1108 static const char *
1109 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1110 {
1111   switch (ping_colortype)
1112   {
1113     case 0:
1114       return "Grayscale";
1115
1116     case 2:
1117       return "Truecolor";
1118
1119     case 3:
1120       return "Indexed";
1121
1122     case 4:
1123       return "GrayAlpha";
1124
1125     case 6:
1126       return "RGBA";
1127
1128     default:
1129       return "UndefinedColorType";
1130     }
1131 }
1132
1133 #endif /* PNG_LIBPNG_VER > 10011 */
1134 #endif /* MAGICKCORE_PNG_DELEGATE */
1135 \f
1136 /*
1137 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1138 %                                                                             %
1139 %                                                                             %
1140 %                                                                             %
1141 %   I s M N G                                                                 %
1142 %                                                                             %
1143 %                                                                             %
1144 %                                                                             %
1145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1146 %
1147 %  IsMNG() returns MagickTrue if the image format type, identified by the
1148 %  magick string, is MNG.
1149 %
1150 %  The format of the IsMNG method is:
1151 %
1152 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1153 %
1154 %  A description of each parameter follows:
1155 %
1156 %    o magick: compare image format pattern against these bytes.
1157 %
1158 %    o length: Specifies the length of the magick string.
1159 %
1160 %
1161 */
1162 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1163 {
1164   if (length < 8)
1165     return(MagickFalse);
1166
1167   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1168     return(MagickTrue);
1169
1170   return(MagickFalse);
1171 }
1172 \f
1173 /*
1174 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1175 %                                                                             %
1176 %                                                                             %
1177 %                                                                             %
1178 %   I s J N G                                                                 %
1179 %                                                                             %
1180 %                                                                             %
1181 %                                                                             %
1182 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1183 %
1184 %  IsJNG() returns MagickTrue if the image format type, identified by the
1185 %  magick string, is JNG.
1186 %
1187 %  The format of the IsJNG method is:
1188 %
1189 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1190 %
1191 %  A description of each parameter follows:
1192 %
1193 %    o magick: compare image format pattern against these bytes.
1194 %
1195 %    o length: Specifies the length of the magick string.
1196 %
1197 %
1198 */
1199 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1200 {
1201   if (length < 8)
1202     return(MagickFalse);
1203
1204   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1205     return(MagickTrue);
1206
1207   return(MagickFalse);
1208 }
1209 \f
1210 /*
1211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1212 %                                                                             %
1213 %                                                                             %
1214 %                                                                             %
1215 %   I s P N G                                                                 %
1216 %                                                                             %
1217 %                                                                             %
1218 %                                                                             %
1219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1220 %
1221 %  IsPNG() returns MagickTrue if the image format type, identified by the
1222 %  magick string, is PNG.
1223 %
1224 %  The format of the IsPNG method is:
1225 %
1226 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1227 %
1228 %  A description of each parameter follows:
1229 %
1230 %    o magick: compare image format pattern against these bytes.
1231 %
1232 %    o length: Specifies the length of the magick string.
1233 %
1234 */
1235 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1236 {
1237   if (length < 8)
1238     return(MagickFalse);
1239
1240   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1241     return(MagickTrue);
1242
1243   return(MagickFalse);
1244 }
1245 \f
1246 #if defined(MAGICKCORE_PNG_DELEGATE)
1247 #if defined(__cplusplus) || defined(c_plusplus)
1248 extern "C" {
1249 #endif
1250
1251 #if (PNG_LIBPNG_VER > 10011)
1252 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1253 {
1254   unsigned char
1255     buffer[4];
1256
1257   assert(image != (Image *) NULL);
1258   assert(image->signature == MagickCoreSignature);
1259   buffer[0]=(unsigned char) (value >> 24);
1260   buffer[1]=(unsigned char) (value >> 16);
1261   buffer[2]=(unsigned char) (value >> 8);
1262   buffer[3]=(unsigned char) value;
1263   return((size_t) WriteBlob(image,4,buffer));
1264 }
1265
1266 static void PNGLong(png_bytep p,png_uint_32 value)
1267 {
1268   *p++=(png_byte) ((value >> 24) & 0xff);
1269   *p++=(png_byte) ((value >> 16) & 0xff);
1270   *p++=(png_byte) ((value >> 8) & 0xff);
1271   *p++=(png_byte) (value & 0xff);
1272 }
1273
1274 static void PNGsLong(png_bytep p,png_int_32 value)
1275 {
1276   *p++=(png_byte) ((value >> 24) & 0xff);
1277   *p++=(png_byte) ((value >> 16) & 0xff);
1278   *p++=(png_byte) ((value >> 8) & 0xff);
1279   *p++=(png_byte) (value & 0xff);
1280 }
1281
1282 static void PNGShort(png_bytep p,png_uint_16 value)
1283 {
1284   *p++=(png_byte) ((value >> 8) & 0xff);
1285   *p++=(png_byte) (value & 0xff);
1286 }
1287
1288 static void PNGType(png_bytep p,const png_byte *type)
1289 {
1290   (void) memcpy(p,type,4*sizeof(png_byte));
1291 }
1292
1293 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1294    size_t length)
1295 {
1296   if (logging != MagickFalse)
1297     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1298       "  Writing %c%c%c%c chunk, length: %.20g",
1299       type[0],type[1],type[2],type[3],(double) length);
1300 }
1301 #endif /* PNG_LIBPNG_VER > 10011 */
1302
1303 #if defined(__cplusplus) || defined(c_plusplus)
1304 }
1305 #endif
1306
1307 #if PNG_LIBPNG_VER > 10011
1308 /*
1309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1310 %                                                                             %
1311 %                                                                             %
1312 %                                                                             %
1313 %   R e a d P N G I m a g e                                                   %
1314 %                                                                             %
1315 %                                                                             %
1316 %                                                                             %
1317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1318 %
1319 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1320 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1321 %  allocates the memory necessary for the new Image structure and returns a
1322 %  pointer to the new image or set of images.
1323 %
1324 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1325 %
1326 %  The format of the ReadPNGImage method is:
1327 %
1328 %     Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1329 %
1330 %  A description of each parameter follows:
1331 %
1332 %    o image_info: the image info.
1333 %
1334 %    o exception: return any errors or warnings in this structure.
1335 %
1336 %  To do, more or less in chronological order (as of version 5.5.2,
1337 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1338 %
1339 %    Get 16-bit cheap transparency working.
1340 %
1341 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1342 %
1343 %    Preserve all unknown and not-yet-handled known chunks found in input
1344 %    PNG file and copy them into output PNG files according to the PNG
1345 %    copying rules.
1346 %
1347 %    (At this point, PNG encoding should be in full MNG compliance)
1348 %
1349 %    Provide options for choice of background to use when the MNG BACK
1350 %    chunk is not present or is not mandatory (i.e., leave transparent,
1351 %    user specified, MNG BACK, PNG bKGD)
1352 %
1353 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1354 %    efficiently by linking in the duplicate frames.].
1355 %
1356 %    Decode and act on the MHDR simplicity profile (offer option to reject
1357 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1358 %
1359 %    Upgrade to full MNG without Delta-PNG.
1360 %
1361 %        o  BACK [done a while ago except for background image ID]
1362 %        o  MOVE [done 15 May 1999]
1363 %        o  CLIP [done 15 May 1999]
1364 %        o  DISC [done 19 May 1999]
1365 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1366 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1367 %        o  SHOW
1368 %        o  PAST
1369 %        o  BASI
1370 %        o  MNG-level tEXt/iTXt/zTXt
1371 %        o  pHYg
1372 %        o  pHYs
1373 %        o  sBIT
1374 %        o  bKGD
1375 %        o  iTXt (wait for libpng implementation).
1376 %
1377 %    Use the scene signature to discover when an identical scene is
1378 %    being reused, and just point to the original image->exception instead
1379 %    of storing another set of pixels.  This not specific to MNG
1380 %    but could be applied generally.
1381 %
1382 %    Upgrade to full MNG with Delta-PNG.
1383 %
1384 %    JNG tEXt/iTXt/zTXt
1385 %
1386 %    We will not attempt to read files containing the CgBI chunk.
1387 %    They are really Xcode files meant for display on the iPhone.
1388 %    These are not valid PNG files and it is impossible to recover
1389 %    the original PNG from files that have been converted to Xcode-PNG,
1390 %    since irretrievable loss of color data has occurred due to the
1391 %    use of premultiplied alpha.
1392 */
1393
1394 #if defined(__cplusplus) || defined(c_plusplus)
1395 extern "C" {
1396 #endif
1397
1398 /*
1399   This the function that does the actual reading of data.  It is
1400   the same as the one supplied in libpng, except that it receives the
1401   datastream from the ReadBlob() function instead of standard input.
1402 */
1403 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1404 {
1405   Image
1406     *image;
1407
1408   image=(Image *) png_get_io_ptr(png_ptr);
1409   if (length != 0)
1410     {
1411       png_size_t
1412         check;
1413
1414       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1415       if (check != length)
1416         {
1417           char
1418             msg[MagickPathExtent];
1419
1420           (void) FormatLocaleString(msg,MagickPathExtent,
1421             "Expected %.20g bytes; found %.20g bytes",(double) length,
1422             (double) check);
1423           png_warning(png_ptr,msg);
1424           png_error(png_ptr,"Read Exception");
1425         }
1426     }
1427 }
1428
1429 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1430     !defined(PNG_MNG_FEATURES_SUPPORTED)
1431 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1432  * older than libpng-1.0.3a, which was the first to allow the empty
1433  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1434  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1435  * encountered after an empty PLTE, so we have to look ahead for bKGD
1436  * chunks and remove them from the datastream that is passed to libpng,
1437  * and store their contents for later use.
1438  */
1439 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1440 {
1441   MngInfo
1442     *mng_info;
1443
1444   Image
1445     *image;
1446
1447   png_size_t
1448     check;
1449
1450   register ssize_t
1451     i;
1452
1453   i=0;
1454   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1455   image=(Image *) mng_info->image;
1456   while (mng_info->bytes_in_read_buffer && length)
1457   {
1458     data[i]=mng_info->read_buffer[i];
1459     mng_info->bytes_in_read_buffer--;
1460     length--;
1461     i++;
1462   }
1463   if (length != 0)
1464     {
1465       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1466
1467       if (check != length)
1468         png_error(png_ptr,"Read Exception");
1469
1470       if (length == 4)
1471         {
1472           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1473               (data[3] == 0))
1474             {
1475               check=(png_size_t) ReadBlob(image,(size_t) length,
1476                 (char *) mng_info->read_buffer);
1477               mng_info->read_buffer[4]=0;
1478               mng_info->bytes_in_read_buffer=4;
1479               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1480                 mng_info->found_empty_plte=MagickTrue;
1481               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1482                 {
1483                   mng_info->found_empty_plte=MagickFalse;
1484                   mng_info->have_saved_bkgd_index=MagickFalse;
1485                 }
1486             }
1487
1488           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1489               (data[3] == 1))
1490             {
1491               check=(png_size_t) ReadBlob(image,(size_t) length,
1492                 (char *) mng_info->read_buffer);
1493               mng_info->read_buffer[4]=0;
1494               mng_info->bytes_in_read_buffer=4;
1495               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1496                 if (mng_info->found_empty_plte)
1497                   {
1498                     /*
1499                       Skip the bKGD data byte and CRC.
1500                     */
1501                     check=(png_size_t)
1502                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1503                     check=(png_size_t) ReadBlob(image,(size_t) length,
1504                       (char *) mng_info->read_buffer);
1505                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1506                     mng_info->have_saved_bkgd_index=MagickTrue;
1507                     mng_info->bytes_in_read_buffer=0;
1508                   }
1509             }
1510         }
1511     }
1512 }
1513 #endif
1514
1515 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1516 {
1517   Image
1518     *image;
1519
1520   image=(Image *) png_get_io_ptr(png_ptr);
1521   if (length != 0)
1522     {
1523       png_size_t
1524         check;
1525
1526       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1527
1528       if (check != length)
1529         png_error(png_ptr,"WriteBlob Failed");
1530     }
1531 }
1532
1533 static void png_flush_data(png_structp png_ptr)
1534 {
1535   (void) png_ptr;
1536 }
1537
1538 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1539 static int PalettesAreEqual(Image *a,Image *b)
1540 {
1541   ssize_t
1542     i;
1543
1544   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1545     return((int) MagickFalse);
1546
1547   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1548     return((int) MagickFalse);
1549
1550   if (a->colors != b->colors)
1551     return((int) MagickFalse);
1552
1553   for (i=0; i < (ssize_t) a->colors; i++)
1554   {
1555     if ((a->colormap[i].red != b->colormap[i].red) ||
1556         (a->colormap[i].green != b->colormap[i].green) ||
1557         (a->colormap[i].blue != b->colormap[i].blue))
1558       return((int) MagickFalse);
1559   }
1560
1561   return((int) MagickTrue);
1562 }
1563 #endif
1564
1565 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1566 {
1567   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1568       mng_info->exists[i] && !mng_info->frozen[i])
1569     {
1570 #ifdef MNG_OBJECT_BUFFERS
1571       if (mng_info->ob[i] != (MngBuffer *) NULL)
1572         {
1573           if (mng_info->ob[i]->reference_count > 0)
1574             mng_info->ob[i]->reference_count--;
1575
1576           if (mng_info->ob[i]->reference_count == 0)
1577             {
1578               if (mng_info->ob[i]->image != (Image *) NULL)
1579                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1580
1581               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1582             }
1583         }
1584       mng_info->ob[i]=(MngBuffer *) NULL;
1585 #endif
1586       mng_info->exists[i]=MagickFalse;
1587       mng_info->invisible[i]=MagickFalse;
1588       mng_info->viewable[i]=MagickFalse;
1589       mng_info->frozen[i]=MagickFalse;
1590       mng_info->x_off[i]=0;
1591       mng_info->y_off[i]=0;
1592       mng_info->object_clip[i].left=0;
1593       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1594       mng_info->object_clip[i].top=0;
1595       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1596     }
1597 }
1598
1599 static MngInfo *MngInfoFreeStruct(MngInfo *mng_info)
1600 {
1601   register ssize_t
1602     i;
1603
1604   if (mng_info == (MngInfo *) NULL)
1605     return((MngInfo *) NULL);
1606
1607   for (i=1; i < MNG_MAX_OBJECTS; i++)
1608     MngInfoDiscardObject(mng_info,i);
1609
1610   mng_info->global_plte=(png_colorp)
1611     RelinquishMagickMemory(mng_info->global_plte);
1612
1613   return((MngInfo *) RelinquishMagickMemory(mng_info));
1614 }
1615
1616 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1617 {
1618   MngBox
1619     box;
1620
1621   box=box1;
1622   if (box.left < box2.left)
1623     box.left=box2.left;
1624
1625   if (box.top < box2.top)
1626     box.top=box2.top;
1627
1628   if (box.right > box2.right)
1629     box.right=box2.right;
1630
1631   if (box.bottom > box2.bottom)
1632     box.bottom=box2.bottom;
1633
1634   return box;
1635 }
1636
1637 static long mng_get_long(unsigned char *p)
1638 {
1639   return ((long) (((png_uint_32) p[0] << 24) | ((png_uint_32) p[1] << 16) |
1640     ((png_uint_32) p[2] << 8) | (png_uint_32) p[3]));
1641 }
1642
1643 static MngBox mng_read_box(MngBox previous_box,char delta_type,
1644   unsigned char *p)
1645 {
1646    MngBox
1647       box;
1648
1649   /*
1650     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1651   */
1652   box.left=mng_get_long(p);
1653   box.right=mng_get_long(&p[4]);
1654   box.top=mng_get_long(&p[8]);
1655   box.bottom=mng_get_long(&p[12]);
1656   if (delta_type != 0)
1657     {
1658       box.left+=previous_box.left;
1659       box.right+=previous_box.right;
1660       box.top+=previous_box.top;
1661       box.bottom+=previous_box.bottom;
1662     }
1663
1664   return(box);
1665 }
1666
1667 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1668   unsigned char *p)
1669 {
1670   MngPair
1671     pair;
1672
1673   /*
1674     Read two ssize_t's from CLON, MOVE or PAST chunk
1675   */
1676   pair.a=mng_get_long(p);
1677   pair.b=mng_get_long(&p[4]);
1678   if (delta_type != 0)
1679     {
1680       pair.a+=previous_pair.a;
1681       pair.b+=previous_pair.b;
1682     }
1683
1684   return(pair);
1685 }
1686
1687 typedef struct _PNGErrorInfo
1688 {
1689   Image
1690     *image;
1691
1692   ExceptionInfo
1693     *exception;
1694 } PNGErrorInfo;
1695
1696 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1697 {
1698   ExceptionInfo
1699     *exception;
1700
1701   Image
1702     *image;
1703
1704   PNGErrorInfo
1705     *error_info;
1706
1707   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1708   image=error_info->image;
1709   exception=error_info->exception;
1710
1711   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1712     "  libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1713
1714   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1715     "`%s'",image->filename);
1716
1717 #if (PNG_LIBPNG_VER < 10500)
1718   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1719    * are building with libpng-1.4.x and can be ignored.
1720    */
1721   longjmp(ping->jmpbuf,1);
1722 #else
1723   png_longjmp(ping,1);
1724 #endif
1725 }
1726
1727 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1728 {
1729   ExceptionInfo
1730     *exception;
1731
1732   Image
1733     *image;
1734
1735   PNGErrorInfo
1736     *error_info;
1737
1738   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1739     png_error(ping, message);
1740
1741   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1742   image=error_info->image;
1743   exception=error_info->exception;
1744   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1745     "  libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1746
1747   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1748     message,"`%s'",image->filename);
1749 }
1750
1751 #ifdef PNG_USER_MEM_SUPPORTED
1752 #if PNG_LIBPNG_VER >= 10400
1753 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1754 #else
1755 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1756 #endif
1757 {
1758   (void) png_ptr;
1759   return((png_voidp) AcquireMagickMemory((size_t) size));
1760 }
1761
1762 /*
1763   Free a pointer.  It is removed from the list at the same time.
1764 */
1765 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1766 {
1767   (void) png_ptr;
1768   ptr=RelinquishMagickMemory(ptr);
1769   return((png_free_ptr) NULL);
1770 }
1771 #endif
1772
1773 #if defined(__cplusplus) || defined(c_plusplus)
1774 }
1775 #endif
1776
1777 static int
1778 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1779    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1780 {
1781   register ssize_t
1782     i;
1783
1784   register unsigned char
1785     *dp;
1786
1787   register png_charp
1788     sp;
1789
1790   size_t
1791     extent,
1792     length,
1793     nibbles;
1794
1795   StringInfo
1796     *profile;
1797
1798   static const unsigned char
1799     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1800                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1801                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1802                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1803                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1804                  13,14,15};
1805
1806   sp=text[ii].text+1;
1807   extent=text[ii].text_length;
1808   /* look for newline */
1809   while ((*sp != '\n') && extent--)
1810     sp++;
1811
1812   /* look for length */
1813   while (((*sp == '\0' || *sp == ' ' || *sp == '\n')) && extent--)
1814      sp++;
1815
1816   if (extent == 0)
1817     {
1818       png_warning(ping,"invalid profile length");
1819       return(MagickFalse);
1820     }
1821
1822   length=StringToLong(sp);
1823
1824   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1825        "      length: %lu",(unsigned long) length);
1826
1827   while ((*sp != ' ' && *sp != '\n') && extent--)
1828     sp++;
1829
1830   if (extent == 0)
1831     {
1832       png_warning(ping,"missing profile length");
1833       return(MagickFalse);
1834     }
1835
1836   /* allocate space */
1837   if (length == 0)
1838   {
1839     png_warning(ping,"invalid profile length");
1840     return(MagickFalse);
1841   }
1842
1843   profile=BlobToStringInfo((const void *) NULL,length);
1844
1845   if (profile == (StringInfo *) NULL)
1846   {
1847     png_warning(ping, "unable to copy profile");
1848     return(MagickFalse);
1849   }
1850
1851   /* copy profile, skipping white space and column 1 "=" signs */
1852   dp=GetStringInfoDatum(profile);
1853   nibbles=length*2;
1854
1855   for (i=0; i < (ssize_t) nibbles; i++)
1856   {
1857     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1858     {
1859       if (*sp == '\0')
1860         {
1861           png_warning(ping, "ran out of profile data");
1862           profile=DestroyStringInfo(profile);
1863           return(MagickFalse);
1864         }
1865       sp++;
1866     }
1867
1868     if (i%2 == 0)
1869       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1870
1871     else
1872       (*dp++)+=unhex[(int) *sp++];
1873   }
1874   /*
1875     We have already read "Raw profile type.
1876   */
1877   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1878   profile=DestroyStringInfo(profile);
1879
1880   if (image_info->verbose)
1881     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1882
1883   return MagickTrue;
1884 }
1885
1886 static int PNGSetExifProfile(Image *image,png_size_t size,png_byte *data,
1887   ExceptionInfo *exception)
1888 {
1889   StringInfo
1890     *profile;
1891
1892   unsigned char
1893     *p;
1894
1895   png_byte
1896     *s;
1897
1898   size_t
1899     i;
1900
1901   profile=BlobToStringInfo((const void *) NULL,size+6);
1902
1903   if (profile == (StringInfo *) NULL)
1904     {
1905       (void) ThrowMagickException(exception,GetMagickModule(),
1906         ResourceLimitError,"MemoryAllocationFailed","`%s'",
1907         image->filename);
1908       return(-1);
1909     }
1910   p=GetStringInfoDatum(profile);
1911
1912   /* Initialize profile with "Exif\0\0" */
1913   *p++ ='E';
1914   *p++ ='x';
1915   *p++ ='i';
1916   *p++ ='f';
1917   *p++ ='\0';
1918   *p++ ='\0';
1919
1920   s=data;
1921   i=0;
1922   if (size > 6)
1923     {
1924       /* Skip first 6 bytes if "Exif\0\0" is
1925           already present by accident
1926       */
1927       if (s[0] == 'E' && s[1] == 'x'  && s[2] == 'i' &&
1928           s[3] == 'f' && s[4] == '\0' && s[5] == '\0')
1929       {
1930         s+=6;
1931         i=6;
1932         SetStringInfoLength(profile,size);
1933         p=GetStringInfoDatum(profile);
1934       }
1935     }
1936
1937   /* copy chunk->data to profile */
1938   for (; i<size; i++)
1939     *p++ = *s++;
1940
1941   (void) SetImageProfile(image,"exif",profile,exception);
1942
1943   profile=DestroyStringInfo(profile);
1944
1945   return(1);
1946 }
1947
1948 #if defined(PNG_READ_eXIf_SUPPORTED)
1949 static void read_eXIf_chunk(Image *image,png_struct *ping,png_info *info,
1950   ExceptionInfo *exception)
1951 {
1952   png_uint_32
1953     size;
1954
1955   png_bytep
1956     data;
1957
1958 #if PNG_LIBPNG_VER > 10631
1959   if (png_get_eXIf_1(ping,info,&size,&data))
1960     (void) PNGSetExifProfile(image,size,data,exception);
1961 #endif
1962 }
1963 #endif
1964
1965 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1966
1967 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1968 {
1969   Image
1970     *image;
1971
1972
1973   /* The unknown chunk structure contains the chunk data:
1974      png_byte name[5];
1975      png_byte *data;
1976      png_size_t size;
1977
1978      Note that libpng has already taken care of the CRC handling.
1979
1980      Returns one of the following:
1981          return(-n);  chunk had an error
1982          return(0);  did not recognize
1983          return(n);  success
1984   */
1985
1986   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1987      "    read_user_chunk: found %c%c%c%c chunk",
1988        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1989
1990   if (chunk->name[0]  == 101 &&
1991       (chunk->name[1] ==  88 || chunk->name[1] == 120 ) &&
1992       chunk->name[2] ==   73 &&
1993       chunk-> name[3] == 102)
1994     {
1995       /* process eXIf or exIf chunk */
1996
1997       PNGErrorInfo
1998         *error_info;
1999
2000       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2001         " recognized eXIf chunk");
2002
2003       image=(Image *) png_get_user_chunk_ptr(ping);
2004
2005       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2006
2007       return(PNGSetExifProfile(image,chunk->size,chunk->data,
2008         error_info->exception));
2009     }
2010
2011   /* orNT */
2012   if (chunk->name[0] == 111 &&
2013       chunk->name[1] == 114 &&
2014       chunk->name[2] ==  78 &&
2015       chunk->name[3] ==  84)
2016     {
2017      /* recognized orNT */
2018      if (chunk->size != 1)
2019        return(-1); /* Error return */
2020
2021      image=(Image *) png_get_user_chunk_ptr(ping);
2022
2023      image->orientation=
2024        Magick_Orientation_from_Exif_Orientation((int) chunk->data[0]);
2025
2026      return(1);
2027     }
2028
2029   /* vpAg (deprecated, replaced by caNv) */
2030   if (chunk->name[0] == 118 &&
2031       chunk->name[1] == 112 &&
2032       chunk->name[2] ==  65 &&
2033       chunk->name[3] == 103)
2034     {
2035       /* recognized vpAg */
2036
2037       if (chunk->size != 9)
2038         return(-1); /* Error return */
2039
2040       if (chunk->data[8] != 0)
2041         return(0);  /* ImageMagick requires pixel units */
2042
2043       image=(Image *) png_get_user_chunk_ptr(ping);
2044
2045       image->page.width=(size_t)mng_get_long(chunk->data);
2046       image->page.height=(size_t)mng_get_long(&chunk->data[4]);
2047
2048       return(1);
2049     }
2050
2051   /* caNv */
2052   if (chunk->name[0] ==  99 &&
2053       chunk->name[1] ==  97 &&
2054       chunk->name[2] ==  78 &&
2055       chunk->name[3] == 118)
2056     {
2057       /* recognized caNv */
2058
2059       if (chunk->size != 16)
2060         return(-1); /* Error return */
2061
2062       image=(Image *) png_get_user_chunk_ptr(ping);
2063
2064       image->page.width=(size_t) mng_get_long(chunk->data);
2065       image->page.height=(size_t) mng_get_long(&chunk->data[4]);
2066       image->page.x=(ssize_t) ((int) mng_get_long(&chunk->data[8]));
2067       image->page.y=(ssize_t) ((int) mng_get_long(&chunk->data[12]));
2068
2069       return(1);
2070     }
2071
2072   return(0); /* Did not recognize */
2073 }
2074 #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
2075
2076 #if defined(PNG_tIME_SUPPORTED)
2077 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
2078   ExceptionInfo *exception)
2079 {
2080   png_timep
2081     time;
2082
2083   if (png_get_tIME(ping,info,&time))
2084     {
2085       char
2086         timestamp[21];
2087
2088       FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
2089         time->year,time->month,time->day,time->hour,time->minute,time->second);
2090       SetImageProperty(image,"png:tIME",timestamp,exception);
2091     }
2092 }
2093 #endif
2094
2095 /*
2096 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2097 %                                                                             %
2098 %                                                                             %
2099 %                                                                             %
2100 %   R e a d O n e P N G I m a g e                                             %
2101 %                                                                             %
2102 %                                                                             %
2103 %                                                                             %
2104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2105 %
2106 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2107 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
2108 %  necessary for the new Image structure and returns a pointer to the new
2109 %  image.
2110 %
2111 %  The format of the ReadOnePNGImage method is:
2112 %
2113 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2114 %         ExceptionInfo *exception)
2115 %
2116 %  A description of each parameter follows:
2117 %
2118 %    o mng_info: Specifies a pointer to a MngInfo structure.
2119 %
2120 %    o image_info: the image info.
2121 %
2122 %    o exception: return any errors or warnings in this structure.
2123 %
2124 */
2125 static Image *ReadOnePNGImage(MngInfo *mng_info,
2126     const ImageInfo *image_info, ExceptionInfo *exception)
2127 {
2128   /* Read one PNG image */
2129
2130   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2131
2132   Image
2133     *image;
2134
2135   char
2136     im_vers[32],
2137     libpng_runv[32],
2138     libpng_vers[32],
2139     zlib_runv[32],
2140     zlib_vers[32];
2141
2142   int
2143     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2144     num_raw_profiles,
2145     num_text,
2146     num_text_total,
2147     num_passes,
2148     number_colors,
2149     pass,
2150     ping_bit_depth,
2151     ping_color_type,
2152     ping_file_depth,
2153     ping_interlace_method,
2154     ping_compression_method,
2155     ping_filter_method,
2156     ping_num_trans,
2157     unit_type;
2158
2159   double
2160     file_gamma;
2161
2162   MagickBooleanType
2163     logging,
2164     ping_found_cHRM,
2165     ping_found_gAMA,
2166     ping_found_iCCP,
2167     ping_found_sRGB,
2168     ping_found_sRGB_cHRM,
2169     ping_preserve_iCCP,
2170     status;
2171
2172   MemoryInfo
2173     *volatile pixel_info;
2174
2175   PixelInfo
2176     transparent_color;
2177
2178   PNGErrorInfo
2179     error_info;
2180
2181   png_bytep
2182      ping_trans_alpha;
2183
2184   png_color_16p
2185      ping_background,
2186      ping_trans_color;
2187
2188   png_info
2189     *end_info,
2190     *ping_info;
2191
2192   png_struct
2193     *ping;
2194
2195   png_textp
2196     text;
2197
2198   png_uint_32
2199     ping_height,
2200     ping_width,
2201     x_resolution,
2202     y_resolution;
2203
2204   QuantumInfo
2205     *volatile quantum_info;
2206
2207   Quantum
2208     *volatile quantum_scanline;
2209
2210   ssize_t
2211     ping_rowbytes,
2212     y;
2213
2214   register unsigned char
2215     *p;
2216
2217   register ssize_t
2218     i,
2219     x;
2220
2221   register Quantum
2222     *q;
2223
2224   size_t
2225     length,
2226     row_offset;
2227
2228   ssize_t
2229     j;
2230
2231   unsigned char
2232     *ping_pixels;
2233
2234 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2235   png_byte unused_chunks[]=
2236   {
2237     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2238     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2239     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2240     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2241     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2242 #if !defined(PNG_tIME_SUPPORTED)
2243     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2244 #endif
2245 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2246                           /* ignore the APNG chunks */
2247      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2248     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2249     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2250 #endif
2251   };
2252 #endif
2253
2254   /* Define these outside of the following "if logging()" block so they will
2255    * show in debuggers.
2256    */
2257   *im_vers='\0';
2258   (void) ConcatenateMagickString(im_vers,
2259          MagickLibVersionText,32);
2260   (void) ConcatenateMagickString(im_vers,
2261          MagickLibAddendum,32);
2262
2263   *libpng_vers='\0';
2264   (void) ConcatenateMagickString(libpng_vers,
2265          PNG_LIBPNG_VER_STRING,32);
2266   *libpng_runv='\0';
2267   (void) ConcatenateMagickString(libpng_runv,
2268          png_get_libpng_ver(NULL),32);
2269
2270   *zlib_vers='\0';
2271   (void) ConcatenateMagickString(zlib_vers,
2272          ZLIB_VERSION,32);
2273   *zlib_runv='\0';
2274   (void) ConcatenateMagickString(zlib_runv,
2275          zlib_version,32);
2276
2277   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2278        "  Enter ReadOnePNGImage()\n"
2279        "    IM version     = %s\n"
2280        "    Libpng version = %s",
2281        im_vers, libpng_vers);
2282
2283   if (logging != MagickFalse)
2284   {
2285     if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2286     {
2287    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2288       "      running with   %s", libpng_runv);
2289     }
2290     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2291         "    Zlib version   = %s", zlib_vers);
2292     if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2293     {
2294     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2295         "      running with   %s", zlib_runv);
2296     }
2297   }
2298
2299 #if (PNG_LIBPNG_VER < 10200)
2300   if (image_info->verbose)
2301     printf("Your PNG library (libpng-%s) is rather old.\n",
2302        PNG_LIBPNG_VER_STRING);
2303 #endif
2304
2305 #if (PNG_LIBPNG_VER >= 10400)
2306 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
2307   if (image_info->verbose)
2308     {
2309       printf("Your PNG library (libpng-%s) is an old beta version.\n",
2310            PNG_LIBPNG_VER_STRING);
2311       printf("Please update it.\n");
2312     }
2313 #  endif
2314 #endif
2315
2316
2317   quantum_info = (QuantumInfo *) NULL;
2318   image=mng_info->image;
2319
2320   if (logging != MagickFalse)
2321   {
2322     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2323        "    Before reading:\n"
2324        "      image->alpha_trait=%d\n"
2325        "      image->rendering_intent=%d\n"
2326        "      image->colorspace=%d\n"
2327        "      image->gamma=%f",
2328        (int) image->alpha_trait, (int) image->rendering_intent,
2329        (int) image->colorspace, image->gamma);
2330   }
2331   intent=
2332     Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2333
2334   /* Set to an out-of-range color unless tRNS chunk is present */
2335   transparent_color.red=65537;
2336   transparent_color.green=65537;
2337   transparent_color.blue=65537;
2338   transparent_color.alpha=65537;
2339
2340   number_colors=0;
2341   num_text = 0;
2342   num_text_total = 0;
2343   num_raw_profiles = 0;
2344
2345   ping_found_cHRM = MagickFalse;
2346   ping_found_gAMA = MagickFalse;
2347   ping_found_iCCP = MagickFalse;
2348   ping_found_sRGB = MagickFalse;
2349   ping_found_sRGB_cHRM = MagickFalse;
2350   ping_preserve_iCCP = MagickFalse;
2351
2352
2353   /*
2354     Allocate the PNG structures
2355   */
2356 #ifdef PNG_USER_MEM_SUPPORTED
2357  error_info.image=image;
2358  error_info.exception=exception;
2359  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2360    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2361    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2362 #else
2363   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2364     MagickPNGErrorHandler,MagickPNGWarningHandler);
2365 #endif
2366   if (ping == (png_struct *) NULL)
2367     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2368
2369   ping_info=png_create_info_struct(ping);
2370
2371   if (ping_info == (png_info *) NULL)
2372     {
2373       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2374       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2375     }
2376
2377   end_info=png_create_info_struct(ping);
2378
2379   if (end_info == (png_info *) NULL)
2380     {
2381       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2382       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2383     }
2384
2385   pixel_info=(MemoryInfo *) NULL;
2386   quantum_scanline = (Quantum *) NULL;
2387   quantum_info = (QuantumInfo *) NULL;
2388
2389   if (setjmp(png_jmpbuf(ping)))
2390     {
2391       /*
2392         PNG image is corrupt.
2393       */
2394       png_destroy_read_struct(&ping,&ping_info,&end_info);
2395
2396       if (pixel_info != (MemoryInfo *) NULL)
2397         pixel_info=RelinquishVirtualMemory(pixel_info);
2398
2399       if (quantum_info != (QuantumInfo *) NULL)
2400         quantum_info=DestroyQuantumInfo(quantum_info);
2401
2402       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2403
2404 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2405       UnlockSemaphoreInfo(ping_semaphore);
2406 #endif
2407
2408       if (logging != MagickFalse)
2409         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2410           "  exit ReadOnePNGImage() with error.");
2411
2412       if (image != (Image *) NULL)
2413         image=DestroyImageList(image);
2414       return(image);
2415     }
2416
2417   /* {  For navigation to end of SETJMP-protected block.  Within this
2418    *    block, use png_error() instead of Throwing an Exception, to ensure
2419    *    that libpng is able to clean up, and that the semaphore is unlocked.
2420    */
2421
2422 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2423   LockSemaphoreInfo(ping_semaphore);
2424 #endif
2425
2426 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2427   /* Allow benign errors */
2428   png_set_benign_errors(ping, 1);
2429 #endif
2430
2431 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2432   {
2433     const char
2434       *option;
2435
2436     /* Reject images with too many rows or columns */
2437     png_set_user_limits(ping,(png_uint_32) MagickMin(PNG_UINT_31_MAX,
2438       GetMagickResourceLimit(WidthResource)),(png_uint_32)
2439       MagickMin(PNG_UINT_31_MAX,GetMagickResourceLimit(HeightResource)));
2440
2441 #if (PNG_LIBPNG_VER >= 10400)
2442     option=GetImageOption(image_info,"png:chunk-cache-max");
2443     if (option != (const char *) NULL)
2444       png_set_chunk_cache_max(ping,(png_uint_32) MagickMin(PNG_UINT_32_MAX,
2445         StringToLong(option)));
2446     else
2447       png_set_chunk_cache_max(ping,32767);
2448 #endif
2449
2450 #if (PNG_LIBPNG_VER >= 10401)
2451     option=GetImageOption(image_info,"png:chunk-malloc-max");
2452     if (option != (const char *) NULL)
2453       png_set_chunk_malloc_max(ping,(png_alloc_size_t) MagickMin(PNG_SIZE_MAX,
2454         (size_t) StringToLong(option)));
2455     else
2456       png_set_chunk_malloc_max(ping,(png_alloc_size_t) GetMaxMemoryRequest());
2457 #endif
2458   }
2459 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2460
2461   /*
2462     Prepare PNG for reading.
2463   */
2464
2465   mng_info->image_found++;
2466   png_set_sig_bytes(ping,8);
2467
2468   if (LocaleCompare(image_info->magick,"MNG") == 0)
2469     {
2470 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2471       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2472       png_set_read_fn(ping,image,png_get_data);
2473 #else
2474 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2475       png_permit_empty_plte(ping,MagickTrue);
2476       png_set_read_fn(ping,image,png_get_data);
2477 #else
2478       mng_info->image=image;
2479       mng_info->bytes_in_read_buffer=0;
2480       mng_info->found_empty_plte=MagickFalse;
2481       mng_info->have_saved_bkgd_index=MagickFalse;
2482       png_set_read_fn(ping,mng_info,mng_get_data);
2483 #endif
2484 #endif
2485     }
2486
2487   else
2488     png_set_read_fn(ping,image,png_get_data);
2489
2490   {
2491     const char
2492       *value;
2493
2494     value=GetImageOption(image_info,"png:ignore-crc");
2495     if (value != NULL)
2496     {
2497        /* Turn off CRC checking while reading */
2498        png_set_crc_action(ping, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
2499 #ifdef PNG_IGNORE_ADLER32
2500        /* Turn off ADLER32 checking while reading */
2501        png_set_option(ping, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
2502 #endif
2503      }
2504
2505     value=GetImageOption(image_info,"profile:skip");
2506
2507     if (IsOptionMember("ICC",value) == MagickFalse)
2508     {
2509
2510        value=GetImageOption(image_info,"png:preserve-iCCP");
2511
2512        if (value == NULL)
2513           value=GetImageArtifact(image,"png:preserve-iCCP");
2514
2515        if (value != NULL)
2516           ping_preserve_iCCP=MagickTrue;
2517
2518 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2519        /* Don't let libpng check for ICC/sRGB profile because we're going
2520         * to do that anyway.  This feature was added at libpng-1.6.12.
2521         * If logging, go ahead and check and issue a warning as appropriate.
2522         */
2523        if (logging == MagickFalse)
2524           png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2525 #endif
2526     }
2527 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2528     else
2529     {
2530        png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
2531     }
2532 #endif
2533   }
2534 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2535   /* Ignore unused chunks and all unknown chunks except for caNv and vpAg */
2536 # if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2537   png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2538 # else
2539   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2540 # endif
2541   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
2542   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
2543   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2544      (int)sizeof(unused_chunks)/5);
2545   /* Callback for other unknown chunks */
2546   png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
2547 #endif
2548
2549 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2550     /* Disable new libpng-1.5.10 feature */
2551     png_set_check_for_invalid_index (ping, 0);
2552 #endif
2553
2554 #if (PNG_LIBPNG_VER < 10400)
2555 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2556    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2557   /* Disable thread-unsafe features of pnggccrd */
2558   if (png_access_version_number() >= 10200)
2559   {
2560     png_uint_32 mmx_disable_mask=0;
2561     png_uint_32 asm_flags;
2562
2563     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2564                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2565                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2566                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2567     asm_flags=png_get_asm_flags(ping);
2568     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2569   }
2570 #  endif
2571 #endif
2572
2573   png_read_info(ping,ping_info);
2574
2575   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2576                &ping_bit_depth,&ping_color_type,
2577                &ping_interlace_method,&ping_compression_method,
2578                &ping_filter_method);
2579
2580   ping_file_depth = ping_bit_depth;
2581
2582   /* Swap bytes if requested */
2583   if (ping_file_depth == 16)
2584   {
2585      const char
2586        *value;
2587
2588      value=GetImageOption(image_info,"png:swap-bytes");
2589
2590      if (value == NULL)
2591         value=GetImageArtifact(image,"png:swap-bytes");
2592
2593      if (value != NULL)
2594         png_set_swap(ping);
2595   }
2596
2597   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2598   {
2599       char
2600         msg[MagickPathExtent];
2601
2602       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2603          (int) ping_color_type);
2604       (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2605
2606       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2607          (int) ping_bit_depth);
2608       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2609   }
2610
2611   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2612                       &ping_trans_color);
2613
2614   (void) png_get_bKGD(ping, ping_info, &ping_background);
2615
2616   if (ping_bit_depth < 8)
2617     {
2618        png_set_packing(ping);
2619        ping_bit_depth = 8;
2620     }
2621
2622   image->depth=ping_bit_depth;
2623   image->depth=GetImageQuantumDepth(image,MagickFalse);
2624   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2625
2626   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2627       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2628     {
2629       image->rendering_intent=UndefinedIntent;
2630       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2631       (void) memset(&image->chromaticity,0,
2632         sizeof(image->chromaticity));
2633     }
2634
2635   if (logging != MagickFalse)
2636     {
2637       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2638         "    PNG width: %.20g, height: %.20g\n"
2639         "    PNG color_type: %d, bit_depth: %d\n"
2640         "    PNG compression_method: %d\n"
2641         "    PNG interlace_method: %d, filter_method: %d",
2642         (double) ping_width, (double) ping_height,
2643         ping_color_type, ping_bit_depth,
2644         ping_compression_method,
2645         ping_interlace_method,ping_filter_method);
2646
2647     }
2648
2649   if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2650     {
2651       ping_found_iCCP=MagickTrue;
2652       if (logging != MagickFalse)
2653         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2654           "    Found PNG iCCP chunk.");
2655     }
2656
2657   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2658     {
2659       ping_found_gAMA=MagickTrue;
2660       if (logging != MagickFalse)
2661         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2662           "    Found PNG gAMA chunk.");
2663     }
2664
2665   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2666     {
2667       ping_found_cHRM=MagickTrue;
2668       if (logging != MagickFalse)
2669         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2670           "    Found PNG cHRM chunk.");
2671     }
2672
2673   if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2674       PNG_INFO_sRGB))
2675     {
2676       ping_found_sRGB=MagickTrue;
2677       if (logging != MagickFalse)
2678         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2679           "    Found PNG sRGB chunk.");
2680     }
2681
2682 #ifdef PNG_READ_iCCP_SUPPORTED
2683     if (ping_found_iCCP !=MagickTrue &&
2684       ping_found_sRGB != MagickTrue &&
2685       png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2686     {
2687       ping_found_iCCP=MagickTrue;
2688       if (logging != MagickFalse)
2689         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2690           "    Found PNG iCCP chunk.");
2691     }
2692
2693   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2694     {
2695       int
2696         compression;
2697
2698 #if (PNG_LIBPNG_VER < 10500)
2699       png_charp
2700         info;
2701 #else
2702       png_bytep
2703         info;
2704 #endif
2705
2706       png_charp
2707         name;
2708
2709       png_uint_32
2710         profile_length;
2711
2712       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2713         &profile_length);
2714
2715       if (profile_length != 0)
2716         {
2717           StringInfo
2718             *profile;
2719
2720           if (logging != MagickFalse)
2721             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2722               "    Reading PNG iCCP chunk.");
2723
2724           profile=BlobToStringInfo(info,(const size_t) profile_length);
2725
2726           if (profile == (StringInfo *) NULL)
2727           {
2728             png_warning(ping, "ICC profile is NULL");
2729             profile=DestroyStringInfo(profile);
2730           }
2731           else
2732           {
2733             if (ping_preserve_iCCP == MagickFalse)
2734             {
2735                  int
2736                    icheck,
2737                    got_crc=0;
2738
2739
2740                  png_uint_32
2741                    profile_crc=0;
2742
2743                  unsigned char
2744                    *data;
2745
2746                  profile_length=(png_uint_32) GetStringInfoLength(profile);
2747
2748                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2749                  {
2750                    if (profile_length == sRGB_info[icheck].len)
2751                    {
2752                      if (got_crc == 0)
2753                      {
2754                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2755                          "    Got a %lu-byte ICC profile (potentially sRGB)",
2756                          (unsigned long) profile_length);
2757
2758                        data=GetStringInfoDatum(profile);
2759                        profile_crc=crc32(0,data,profile_length);
2760
2761                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2762                            "      with crc=%8x",(unsigned int) profile_crc);
2763                        got_crc++;
2764                      }
2765
2766                      if (profile_crc == sRGB_info[icheck].crc)
2767                      {
2768                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2769                             "      It is sRGB with rendering intent = %s",
2770                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
2771                              sRGB_info[icheck].intent));
2772                         if (image->rendering_intent==UndefinedIntent)
2773                         {
2774                           image->rendering_intent=
2775                           Magick_RenderingIntent_from_PNG_RenderingIntent(
2776                              sRGB_info[icheck].intent);
2777                         }
2778                         break;
2779                      }
2780                    }
2781                  }
2782                  if (sRGB_info[icheck].len == 0)
2783                  {
2784                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2785                         "    Got %lu-byte ICC profile not recognized as sRGB",
2786                         (unsigned long) profile_length);
2787                     (void) SetImageProfile(image,"icc",profile,exception);
2788                  }
2789             }
2790             else /* Preserve-iCCP */
2791             {
2792                     (void) SetImageProfile(image,"icc",profile,exception);
2793             }
2794
2795             profile=DestroyStringInfo(profile);
2796           }
2797       }
2798     }
2799 #endif
2800
2801 #if defined(PNG_READ_sRGB_SUPPORTED)
2802   {
2803     if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2804         PNG_INFO_sRGB))
2805     {
2806       if (png_get_sRGB(ping,ping_info,&intent))
2807       {
2808         if (image->rendering_intent == UndefinedIntent)
2809           image->rendering_intent=
2810              Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2811
2812         if (logging != MagickFalse)
2813           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2814             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2815       }
2816     }
2817
2818     else if (mng_info->have_global_srgb)
2819       {
2820         if (image->rendering_intent == UndefinedIntent)
2821           image->rendering_intent=
2822             Magick_RenderingIntent_from_PNG_RenderingIntent
2823             (mng_info->global_srgb_intent);
2824       }
2825   }
2826 #endif
2827
2828
2829   {
2830      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2831        if (mng_info->have_global_gama)
2832          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2833
2834      if (png_get_gAMA(ping,ping_info,&file_gamma))
2835        {
2836          image->gamma=(float) file_gamma;
2837          if (logging != MagickFalse)
2838            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2839              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2840        }
2841   }
2842
2843   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2844     {
2845       if (mng_info->have_global_chrm != MagickFalse)
2846         {
2847           (void) png_set_cHRM(ping,ping_info,
2848             mng_info->global_chrm.white_point.x,
2849             mng_info->global_chrm.white_point.y,
2850             mng_info->global_chrm.red_primary.x,
2851             mng_info->global_chrm.red_primary.y,
2852             mng_info->global_chrm.green_primary.x,
2853             mng_info->global_chrm.green_primary.y,
2854             mng_info->global_chrm.blue_primary.x,
2855             mng_info->global_chrm.blue_primary.y);
2856         }
2857     }
2858
2859   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2860     {
2861       (void) png_get_cHRM(ping,ping_info,
2862         &image->chromaticity.white_point.x,
2863         &image->chromaticity.white_point.y,
2864         &image->chromaticity.red_primary.x,
2865         &image->chromaticity.red_primary.y,
2866         &image->chromaticity.green_primary.x,
2867         &image->chromaticity.green_primary.y,
2868         &image->chromaticity.blue_primary.x,
2869         &image->chromaticity.blue_primary.y);
2870
2871        ping_found_cHRM=MagickTrue;
2872
2873        if (image->chromaticity.red_primary.x>0.6399f &&
2874            image->chromaticity.red_primary.x<0.6401f &&
2875            image->chromaticity.red_primary.y>0.3299f &&
2876            image->chromaticity.red_primary.y<0.3301f &&
2877            image->chromaticity.green_primary.x>0.2999f &&
2878            image->chromaticity.green_primary.x<0.3001f &&
2879            image->chromaticity.green_primary.y>0.5999f &&
2880            image->chromaticity.green_primary.y<0.6001f &&
2881            image->chromaticity.blue_primary.x>0.1499f &&
2882            image->chromaticity.blue_primary.x<0.1501f &&
2883            image->chromaticity.blue_primary.y>0.0599f &&
2884            image->chromaticity.blue_primary.y<0.0601f &&
2885            image->chromaticity.white_point.x>0.3126f &&
2886            image->chromaticity.white_point.x<0.3128f &&
2887            image->chromaticity.white_point.y>0.3289f &&
2888            image->chromaticity.white_point.y<0.3291f)
2889           ping_found_sRGB_cHRM=MagickTrue;
2890     }
2891
2892   if (image->rendering_intent != UndefinedIntent)
2893     {
2894       if (ping_found_sRGB != MagickTrue &&
2895           (ping_found_gAMA != MagickTrue ||
2896           (image->gamma > .45 && image->gamma < .46)) &&
2897           (ping_found_cHRM != MagickTrue ||
2898           ping_found_sRGB_cHRM != MagickFalse) &&
2899           ping_found_iCCP != MagickTrue)
2900       {
2901          png_set_sRGB(ping,ping_info,
2902             Magick_RenderingIntent_to_PNG_RenderingIntent
2903             (image->rendering_intent));
2904          file_gamma=0.45455f;
2905          ping_found_sRGB=MagickTrue;
2906          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2907            "    Setting sRGB as if in input");
2908       }
2909     }
2910
2911 #if defined(PNG_oFFs_SUPPORTED)
2912   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2913     {
2914       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2915       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2916
2917       if (logging != MagickFalse)
2918         if (image->page.x || image->page.y)
2919           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2920             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2921             image->page.x,(double) image->page.y);
2922     }
2923 #endif
2924 #if defined(PNG_pHYs_SUPPORTED)
2925   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2926     {
2927       if (mng_info->have_global_phys)
2928         {
2929           png_set_pHYs(ping,ping_info,
2930                        mng_info->global_x_pixels_per_unit,
2931                        mng_info->global_y_pixels_per_unit,
2932                        mng_info->global_phys_unit_type);
2933         }
2934     }
2935
2936   x_resolution=0;
2937   y_resolution=0;
2938   unit_type=0;
2939   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2940     {
2941       /*
2942         Set image resolution.
2943       */
2944       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2945         &unit_type);
2946       image->resolution.x=(double) x_resolution;
2947       image->resolution.y=(double) y_resolution;
2948
2949       if (unit_type == PNG_RESOLUTION_METER)
2950         {
2951           image->units=PixelsPerCentimeterResolution;
2952           image->resolution.x=(double) x_resolution/100.0;
2953           image->resolution.y=(double) y_resolution/100.0;
2954         }
2955
2956       if (logging != MagickFalse)
2957         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2958           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2959           (double) x_resolution,(double) y_resolution,unit_type);
2960     }
2961 #endif
2962
2963   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2964     {
2965       png_colorp
2966         palette;
2967
2968       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2969
2970       if ((number_colors == 0) &&
2971           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2972         {
2973           if (mng_info->global_plte_length)
2974             {
2975               png_set_PLTE(ping,ping_info,mng_info->global_plte,
2976                 (int) mng_info->global_plte_length);
2977
2978               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2979               {
2980                 if (mng_info->global_trns_length)
2981                   {
2982                     png_warning(ping,
2983                       "global tRNS has more entries than global PLTE");
2984                   }
2985                 else
2986                   {
2987                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
2988                        (int) mng_info->global_trns_length,NULL);
2989                   }
2990                }
2991 #ifdef PNG_READ_bKGD_SUPPORTED
2992               if (
2993 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2994                    mng_info->have_saved_bkgd_index ||
2995 #endif
2996                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2997                     {
2998                       png_color_16
2999                          background;
3000
3001 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3002                       if (mng_info->have_saved_bkgd_index)
3003                         background.index=mng_info->saved_bkgd_index;
3004 #endif
3005                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
3006                         background.index=ping_background->index;
3007
3008                       background.red=(png_uint_16)
3009                         mng_info->global_plte[background.index].red;
3010
3011                       background.green=(png_uint_16)
3012                         mng_info->global_plte[background.index].green;
3013
3014                       background.blue=(png_uint_16)
3015                         mng_info->global_plte[background.index].blue;
3016
3017                       background.gray=(png_uint_16)
3018                         mng_info->global_plte[background.index].green;
3019
3020                       png_set_bKGD(ping,ping_info,&background);
3021                     }
3022 #endif
3023                 }
3024               else
3025                 png_error(ping,"No global PLTE in file");
3026             }
3027         }
3028
3029 #ifdef PNG_READ_bKGD_SUPPORTED
3030   if (mng_info->have_global_bkgd &&
3031           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
3032       image->background_color=mng_info->mng_global_bkgd;
3033
3034   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3035     {
3036       unsigned int
3037         bkgd_scale;
3038
3039       /* Set image background color.
3040        * Scale background components to 16-bit, then scale
3041        * to quantum depth
3042        */
3043
3044         bkgd_scale = 1;
3045
3046         if (ping_file_depth == 1)
3047            bkgd_scale = 255;
3048
3049         else if (ping_file_depth == 2)
3050            bkgd_scale = 85;
3051
3052         else if (ping_file_depth == 4)
3053            bkgd_scale = 17;
3054
3055         if (ping_file_depth <= 8)
3056            bkgd_scale *= 257;
3057
3058         ping_background->red *= bkgd_scale;
3059         ping_background->green *= bkgd_scale;
3060         ping_background->blue *= bkgd_scale;
3061
3062         if (logging != MagickFalse)
3063           {
3064             if (logging != MagickFalse)
3065               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3066                  "    Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
3067                  "    bkgd_scale=%d.  ping_background=(%d,%d,%d)",
3068                  ping_background->red,ping_background->green,
3069                  ping_background->blue,
3070                  bkgd_scale,ping_background->red,
3071                  ping_background->green,ping_background->blue);
3072           }
3073
3074         image->background_color.red=
3075             ScaleShortToQuantum(ping_background->red);
3076
3077         image->background_color.green=
3078             ScaleShortToQuantum(ping_background->green);
3079
3080         image->background_color.blue=
3081           ScaleShortToQuantum(ping_background->blue);
3082
3083         image->background_color.alpha=OpaqueAlpha;
3084
3085         if (logging != MagickFalse)
3086           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3087             "    image->background_color=(%.20g,%.20g,%.20g).",
3088             (double) image->background_color.red,
3089             (double) image->background_color.green,
3090             (double) image->background_color.blue);
3091     }
3092 #endif /* PNG_READ_bKGD_SUPPORTED */
3093
3094   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3095     {
3096       /*
3097         Image has a tRNS chunk.
3098       */
3099       int
3100         max_sample;
3101
3102       size_t
3103         one = 1;
3104
3105       if (logging != MagickFalse)
3106         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3107           "    Reading PNG tRNS chunk.");
3108
3109       max_sample = (int) ((one << ping_file_depth) - 1);
3110
3111       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3112           (int)ping_trans_color->gray > max_sample) ||
3113           (ping_color_type == PNG_COLOR_TYPE_RGB &&
3114           ((int)ping_trans_color->red > max_sample ||
3115           (int)ping_trans_color->green > max_sample ||
3116           (int)ping_trans_color->blue > max_sample)))
3117         {
3118           if (logging != MagickFalse)
3119             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3120               "    Ignoring PNG tRNS chunk with out-of-range sample.");
3121           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3122           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3123           image->alpha_trait=UndefinedPixelTrait;
3124         }
3125       else
3126         {
3127           int
3128             scale_to_short;
3129
3130           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3131
3132           /* Scale transparent_color to short */
3133           transparent_color.red= scale_to_short*ping_trans_color->red;
3134           transparent_color.green= scale_to_short*ping_trans_color->green;
3135           transparent_color.blue= scale_to_short*ping_trans_color->blue;
3136           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3137
3138           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3139             {
3140               if (logging != MagickFalse)
3141               {
3142                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3143                   "    Raw tRNS graylevel = %d, scaled graylevel = %d.",
3144                   (int) ping_trans_color->gray,(int) transparent_color.alpha);
3145
3146               }
3147               transparent_color.red=transparent_color.alpha;
3148               transparent_color.green=transparent_color.alpha;
3149               transparent_color.blue=transparent_color.alpha;
3150             }
3151         }
3152     }
3153 #if defined(PNG_READ_sBIT_SUPPORTED)
3154   if (mng_info->have_global_sbit)
3155     {
3156       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3157         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3158     }
3159 #endif
3160   num_passes=png_set_interlace_handling(ping);
3161
3162   png_read_update_info(ping,ping_info);
3163
3164   ping_rowbytes=png_get_rowbytes(ping,ping_info);
3165
3166   /*
3167     Initialize image structure.
3168   */
3169   mng_info->image_box.left=0;
3170   mng_info->image_box.right=(ssize_t) ping_width;
3171   mng_info->image_box.top=0;
3172   mng_info->image_box.bottom=(ssize_t) ping_height;
3173   if (mng_info->mng_type == 0)
3174     {
3175       mng_info->mng_width=ping_width;
3176       mng_info->mng_height=ping_height;
3177       mng_info->frame=mng_info->image_box;
3178       mng_info->clip=mng_info->image_box;
3179     }
3180
3181   else
3182     {
3183       image->page.y=mng_info->y_off[mng_info->object_id];
3184     }
3185
3186   image->compression=ZipCompression;
3187   image->columns=ping_width;
3188   image->rows=ping_height;
3189
3190   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3191       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3192     {
3193       double
3194         image_gamma = image->gamma;
3195
3196       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3197          "    image->gamma=%f",(float) image_gamma);
3198
3199       if (image_gamma > 0.75)
3200         {
3201           /* Set image->rendering_intent to Undefined,
3202            * image->colorspace to GRAY, and reset image->chromaticity.
3203            */
3204           image->intensity = Rec709LuminancePixelIntensityMethod;
3205           SetImageColorspace(image,LinearGRAYColorspace,exception);
3206         }
3207       else
3208         {
3209           RenderingIntent
3210             save_rendering_intent = image->rendering_intent;
3211           ChromaticityInfo
3212             save_chromaticity = image->chromaticity;
3213
3214           SetImageColorspace(image,GRAYColorspace,exception);
3215           image->rendering_intent = save_rendering_intent;
3216           image->chromaticity = save_chromaticity;
3217         }
3218
3219       image->gamma = image_gamma;
3220     }
3221   else
3222     {
3223       double
3224         image_gamma = image->gamma;
3225
3226       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3227          "    image->gamma=%f",(float) image_gamma);
3228
3229       if (image_gamma > 0.75)
3230         {
3231           /* Set image->rendering_intent to Undefined,
3232            * image->colorspace to GRAY, and reset image->chromaticity.
3233            */
3234           image->intensity = Rec709LuminancePixelIntensityMethod;
3235           SetImageColorspace(image,RGBColorspace,exception);
3236         }
3237       else
3238         {
3239           RenderingIntent
3240             save_rendering_intent = image->rendering_intent;
3241           ChromaticityInfo
3242             save_chromaticity = image->chromaticity;
3243
3244           SetImageColorspace(image,sRGBColorspace,exception);
3245           image->rendering_intent = save_rendering_intent;
3246           image->chromaticity = save_chromaticity;
3247         }
3248
3249       image->gamma = image_gamma;
3250     }
3251
3252   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3253       "    image->colorspace=%d",(int) image->colorspace);
3254
3255   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3256       ((int) ping_bit_depth < 16 &&
3257       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3258     {
3259       size_t
3260         one;
3261
3262       image->storage_class=PseudoClass;
3263       one=1;
3264       image->colors=one << ping_file_depth;
3265 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3266       if (image->colors > 256)
3267         image->colors=256;
3268 #else
3269       if (image->colors > 65536L)
3270         image->colors=65536L;
3271 #endif
3272       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3273         {
3274           png_colorp
3275             palette;
3276
3277           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3278           image->colors=(size_t) number_colors;
3279
3280           if (logging != MagickFalse)
3281             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3282               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3283         }
3284     }
3285
3286   if (image->storage_class == PseudoClass)
3287     {
3288       /*
3289         Initialize image colormap.
3290       */
3291       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3292         png_error(ping,"Memory allocation failed");
3293
3294       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3295         {
3296           png_colorp
3297             palette;
3298
3299           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3300
3301           for (i=0; i < (ssize_t) number_colors; i++)
3302           {
3303             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3304             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3305             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3306           }
3307
3308           for ( ; i < (ssize_t) image->colors; i++)
3309           {
3310             image->colormap[i].red=0;
3311             image->colormap[i].green=0;
3312             image->colormap[i].blue=0;
3313           }
3314         }
3315     }
3316
3317    /* Set some properties for reporting by "identify" */
3318     {
3319       char
3320         msg[MagickPathExtent];
3321
3322      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3323         ping_interlace_method in value */
3324
3325      (void) FormatLocaleString(msg,MagickPathExtent,
3326          "%d, %d",(int) ping_width, (int) ping_height);
3327      (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3328
3329      (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3330         (int) ping_file_depth);
3331      (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3332
3333      (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3334          (int) ping_color_type,
3335          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3336      (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3337
3338      if (ping_interlace_method == 0)
3339        {
3340          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3341             (int) ping_interlace_method);
3342        }
3343      else if (ping_interlace_method == 1)
3344        {
3345          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3346             (int) ping_interlace_method);
3347        }
3348      else
3349        {
3350          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3351             (int) ping_interlace_method);
3352        }
3353        (void) SetImageProperty(image,"png:IHDR.interlace_method",
3354          msg,exception);
3355
3356      if (number_colors != 0)
3357        {
3358          (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3359             (int) number_colors);
3360          (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3361             exception);
3362        }
3363    }
3364 #if defined(PNG_tIME_SUPPORTED)
3365    read_tIME_chunk(image,ping,ping_info,exception);
3366 #endif
3367 #if defined(PNG_READ_eXIf_SUPPORTED)
3368   read_eXIf_chunk(image,ping,ping_info,exception);
3369 #endif
3370
3371
3372   /*
3373     Read image scanlines.
3374   */
3375   if (image->delay != 0)
3376     mng_info->scenes_found++;
3377
3378   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3379       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3380       (image_info->first_scene+image_info->number_scenes))))
3381     {
3382       /* This happens later in non-ping decodes */
3383       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3384         image->storage_class=DirectClass;
3385       image->alpha_trait=
3386         (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3387          ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3388          (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3389         BlendPixelTrait : UndefinedPixelTrait;
3390
3391       if (logging != MagickFalse)
3392         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3393           "    Skipping PNG image data for scene %.20g",(double)
3394           mng_info->scenes_found-1);
3395       png_destroy_read_struct(&ping,&ping_info,&end_info);
3396
3397 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3398       UnlockSemaphoreInfo(ping_semaphore);
3399 #endif
3400
3401       if (logging != MagickFalse)
3402         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3403           "  exit ReadOnePNGImage().");
3404
3405       return(image);
3406     }
3407
3408   if (logging != MagickFalse)
3409     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3410       "    Reading PNG IDAT chunk(s)");
3411
3412   status=SetImageExtent(image,image->columns,image->rows,exception);
3413   if (status != MagickFalse)
3414     status=ResetImagePixels(image,exception);
3415   if (status == MagickFalse)
3416     {
3417       png_destroy_read_struct(&ping,&ping_info,&end_info);
3418 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3419       UnlockSemaphoreInfo(ping_semaphore);
3420 #endif
3421       return(DestroyImageList(image));
3422     }
3423
3424   if (num_passes > 1)
3425     pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3426       sizeof(*ping_pixels));
3427   else
3428     pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3429
3430   if (pixel_info == (MemoryInfo *) NULL)
3431     png_error(ping,"Memory allocation failed");
3432   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3433
3434   if (logging != MagickFalse)
3435     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3436       "    Converting PNG pixels to pixel packets");
3437   /*
3438     Convert PNG pixels to pixel packets.
3439   */
3440   quantum_info=AcquireQuantumInfo(image_info,image);
3441
3442   if (quantum_info == (QuantumInfo *) NULL)
3443      png_error(ping,"Failed to allocate quantum_info");
3444
3445   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3446
3447   {
3448
3449    MagickBooleanType
3450      found_transparent_pixel;
3451
3452   found_transparent_pixel=MagickFalse;
3453
3454   if (image->storage_class == DirectClass)
3455     {
3456       for (pass=0; pass < num_passes; pass++)
3457       {
3458         /*
3459           Convert image to DirectClass pixel packets.
3460         */
3461         image->alpha_trait=
3462             (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3463             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3464             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3465             BlendPixelTrait : UndefinedPixelTrait;
3466
3467         for (y=0; y < (ssize_t) image->rows; y++)
3468         {
3469           if (num_passes > 1)
3470             row_offset=ping_rowbytes*y;
3471
3472           else
3473             row_offset=0;
3474
3475           png_read_row(ping,ping_pixels+row_offset,NULL);
3476
3477           if (pass < num_passes-1)
3478             continue;
3479
3480           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3481
3482           if (q == (Quantum *) NULL)
3483             break;
3484
3485           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3486             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3487               GrayQuantum,ping_pixels+row_offset,exception);
3488
3489           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3490             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3491               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3492
3493           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3494             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3495               RGBAQuantum,ping_pixels+row_offset,exception);
3496
3497           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3498             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3499               IndexQuantum,ping_pixels+row_offset,exception);
3500
3501           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3502             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3503               RGBQuantum,ping_pixels+row_offset,exception);
3504
3505           if (found_transparent_pixel == MagickFalse)
3506             {
3507               /* Is there a transparent pixel in the row? */
3508               if (y== 0 && logging != MagickFalse)
3509                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3510                    "    Looking for cheap transparent pixel");
3511
3512               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3513               {
3514                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3515                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3516                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3517                   {
3518                     if (logging != MagickFalse)
3519                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3520                         "    ...got one.");
3521
3522                     found_transparent_pixel = MagickTrue;
3523                     break;
3524                   }
3525                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3526                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3527                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3528                     transparent_color.red &&
3529                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3530                     transparent_color.green &&
3531                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3532                     transparent_color.blue))
3533                   {
3534                     if (logging != MagickFalse)
3535                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3536                         "    ...got one.");
3537                     found_transparent_pixel = MagickTrue;
3538                     break;
3539                   }
3540                 q+=GetPixelChannels(image);
3541               }
3542             }
3543
3544           if (num_passes == 1)
3545             {
3546               status=SetImageProgress(image,LoadImageTag,
3547                   (MagickOffsetType) y, image->rows);
3548
3549               if (status == MagickFalse)
3550                 break;
3551             }
3552           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3553             break;
3554         }
3555         if (y < (long) image->rows)
3556           break;
3557
3558         if (num_passes != 1)
3559           {
3560             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3561             if (status == MagickFalse)
3562               break;
3563           }
3564       }
3565     }
3566
3567   else /* image->storage_class != DirectClass */
3568
3569     for (pass=0; pass < num_passes; pass++)
3570     {
3571       register Quantum
3572         *r;
3573
3574       /*
3575         Convert grayscale image to PseudoClass pixel packets.
3576       */
3577       if (logging != MagickFalse)
3578         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3579           "    Converting grayscale pixels to pixel packets");
3580
3581       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3582         BlendPixelTrait : UndefinedPixelTrait;
3583
3584       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3585         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3586         sizeof(*quantum_scanline));
3587
3588       if (quantum_scanline == (Quantum *) NULL)
3589         png_error(ping,"Memory allocation failed");
3590
3591       for (y=0; y < (ssize_t) image->rows; y++)
3592       {
3593         Quantum
3594            alpha;
3595
3596         if (num_passes > 1)
3597           row_offset=ping_rowbytes*y;
3598
3599         else
3600           row_offset=0;
3601
3602         png_read_row(ping,ping_pixels+row_offset,NULL);
3603
3604         if (pass < num_passes-1)
3605           continue;
3606
3607         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3608
3609         if (q == (Quantum *) NULL)
3610           break;
3611
3612         p=ping_pixels+row_offset;
3613         r=quantum_scanline;
3614
3615         switch (ping_bit_depth)
3616         {
3617           case 8:
3618           {
3619
3620             if (ping_color_type == 4)
3621               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3622               {
3623                 *r++=*p++;
3624
3625                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3626
3627                 SetPixelAlpha(image,alpha,q);
3628
3629                 if (alpha != OpaqueAlpha)
3630                   found_transparent_pixel = MagickTrue;
3631
3632                 q+=GetPixelChannels(image);
3633               }
3634
3635             else
3636               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3637                 *r++=*p++;
3638
3639             break;
3640           }
3641
3642           case 16:
3643           {
3644             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3645             {
3646 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3647               unsigned long
3648                 quantum;
3649
3650               if (image->colors > 256)
3651                 quantum=(((unsigned int) *p++) << 8);
3652
3653               else
3654                 quantum=0;
3655
3656               quantum|=(*p++);
3657               *r=ScaleShortToQuantum(quantum);
3658               r++;
3659
3660               if (ping_color_type == 4)
3661                 {
3662                   if (image->colors > 256)
3663                     quantum=(((unsigned int) *p++) << 8);
3664                   else
3665                     quantum=0;
3666
3667                   quantum|=(*p++);
3668
3669                   alpha=ScaleShortToQuantum(quantum);
3670                   SetPixelAlpha(image,alpha,q);
3671
3672                   if (alpha != OpaqueAlpha)
3673                     found_transparent_pixel = MagickTrue;
3674
3675                   q+=GetPixelChannels(image);
3676                 }
3677
3678 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3679               *r++=(*p++);
3680               p++; /* strip low byte */
3681
3682               if (ping_color_type == 4)
3683                 {
3684                   SetPixelAlpha(image,*p++,q);
3685
3686                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3687                     found_transparent_pixel = MagickTrue;
3688
3689                   p++;
3690                   q+=GetPixelChannels(image);
3691                 }
3692 #endif
3693             }
3694
3695             break;
3696           }
3697
3698           default:
3699             break;
3700         }
3701
3702         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3703           break;
3704
3705         /*
3706           Transfer image scanline.
3707         */
3708         r=quantum_scanline;
3709
3710         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3711
3712         if (q == (Quantum *) NULL)
3713           break;
3714         for (x=0; x < (ssize_t) image->columns; x++)
3715         {
3716           ssize_t index=ConstrainColormapIndex(image,(ssize_t) *r,exception);
3717           SetPixelRed(image,ClampToQuantum(image->colormap[index].red),q);
3718           SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),q);
3719           SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),q);
3720           SetPixelIndex(image,index,q);
3721           r++;
3722           q+=GetPixelChannels(image);
3723         }
3724
3725         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3726           break;
3727
3728         if (num_passes == 1)
3729           {
3730             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3731               image->rows);
3732
3733             if (status == MagickFalse)
3734               break;
3735           }
3736       }
3737       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3738       if (y < (long) image->rows)
3739         break;
3740       if (num_passes != 1)
3741         {
3742           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3743
3744           if (status == MagickFalse)
3745             break;
3746         }
3747     }
3748
3749     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3750       UndefinedPixelTrait;
3751
3752     if (logging != MagickFalse)
3753       {
3754         if (found_transparent_pixel != MagickFalse)
3755           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3756             "    Found transparent pixel");
3757         else
3758           {
3759             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3760               "    No transparent pixel was found");
3761
3762             ping_color_type&=0x03;
3763           }
3764       }
3765   }
3766   quantum_info=DestroyQuantumInfo(quantum_info);
3767
3768   png_read_end(ping,end_info);
3769
3770   if (logging != MagickFalse)
3771   {
3772     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3773        "  image->storage_class=%d\n",(int) image->storage_class);
3774   }
3775
3776   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3777       (ssize_t) image_info->first_scene && image->delay != 0)
3778     {
3779       png_destroy_read_struct(&ping,&ping_info,&end_info);
3780       pixel_info=RelinquishVirtualMemory(pixel_info);
3781       image->colors=2;
3782       (void) SetImageBackgroundColor(image,exception);
3783 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3784       UnlockSemaphoreInfo(ping_semaphore);
3785 #endif
3786       if (logging != MagickFalse)
3787         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3788           "  exit ReadOnePNGImage() early.");
3789       return(image);
3790     }
3791
3792   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3793     {
3794       ClassType
3795         storage_class;
3796
3797       /*
3798         Image has a transparent background.
3799       */
3800       storage_class=image->storage_class;
3801       image->alpha_trait=BlendPixelTrait;
3802
3803 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3804
3805       if (storage_class == PseudoClass)
3806         {
3807           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3808             {
3809               for (x=0; x < ping_num_trans; x++)
3810               {
3811                  image->colormap[x].alpha_trait=BlendPixelTrait;
3812                  image->colormap[x].alpha =
3813                    ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3814               }
3815             }
3816
3817           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3818             {
3819               for (x=0; x < (int) image->colors; x++)
3820               {
3821                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3822                      transparent_color.alpha)
3823                  {
3824                     image->colormap[x].alpha_trait=BlendPixelTrait;
3825                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3826                  }
3827               }
3828             }
3829           (void) SyncImage(image,exception);
3830         }
3831
3832 #if 1 /* Should have already been done above, but glennrp problem P10
3833        * needs this.
3834        */
3835       else
3836         {
3837           for (y=0; y < (ssize_t) image->rows; y++)
3838           {
3839             image->storage_class=storage_class;
3840             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3841
3842             if (q == (Quantum *) NULL)
3843               break;
3844
3845
3846             /* Caution: on a Q8 build, this does not distinguish between
3847              * 16-bit colors that differ only in the low byte
3848              */
3849             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3850             {
3851               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3852                   transparent_color.red &&
3853                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3854                   transparent_color.green &&
3855                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3856                   transparent_color.blue)
3857                 {
3858                   SetPixelAlpha(image,TransparentAlpha,q);
3859                 }
3860               else
3861                 {
3862                   SetPixelAlpha(image,OpaqueAlpha,q);
3863                 }
3864
3865               q+=GetPixelChannels(image);
3866             }
3867
3868             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3869                break;
3870           }
3871         }
3872 #endif
3873
3874       image->storage_class=DirectClass;
3875     }
3876
3877   for (j = 0; j < 2; j++)
3878   {
3879     if (j == 0)
3880       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3881           MagickTrue : MagickFalse;
3882     else
3883       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3884           MagickTrue : MagickFalse;
3885
3886     if (status != MagickFalse)
3887       for (i=0; i < (ssize_t) num_text; i++)
3888       {
3889         /* Check for a profile */
3890
3891         if (logging != MagickFalse)
3892           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3893             "    Reading PNG text chunk");
3894
3895         if (strlen(text[i].key) > 16 &&
3896             memcmp(text[i].key, "Raw profile type ",17) == 0)
3897           {
3898             const char
3899               *value;
3900
3901             value=GetImageOption(image_info,"profile:skip");
3902
3903             if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3904             {
3905                (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3906                   (int) i,exception);
3907                num_raw_profiles++;
3908                if (logging != MagickFalse)
3909                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3910                    "    Read raw profile %s",text[i].key+17);
3911             }
3912             else
3913             {
3914                if (logging != MagickFalse)
3915                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3916                    "    Skipping raw profile %s",text[i].key+17);
3917             }
3918           }
3919
3920         else
3921           {
3922             char
3923               *value;
3924
3925             length=text[i].text_length;
3926             value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
3927               sizeof(*value));
3928             if (value == (char *) NULL)
3929               {
3930                 png_error(ping,"Memory allocation failed");
3931                 break;
3932               }
3933             *value='\0';
3934             (void) ConcatenateMagickString(value,text[i].text,length+2);
3935
3936             /* Don't save "density" or "units" property if we have a pHYs
3937              * chunk
3938              */
3939             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3940                 (LocaleCompare(text[i].key,"density") != 0 &&
3941                 LocaleCompare(text[i].key,"units") != 0))
3942                (void) SetImageProperty(image,text[i].key,value,exception);
3943
3944             if (logging != MagickFalse)
3945             {
3946               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3947                 "      length: %lu\n"
3948                 "      Keyword: %s",
3949                 (unsigned long) length,
3950                 text[i].key);
3951             }
3952
3953             value=DestroyString(value);
3954           }
3955       }
3956     num_text_total += num_text;
3957   }
3958
3959 #ifdef MNG_OBJECT_BUFFERS
3960   /*
3961     Store the object if necessary.
3962   */
3963   if (object_id && !mng_info->frozen[object_id])
3964     {
3965       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3966         {
3967           /*
3968             create a new object buffer.
3969           */
3970           mng_info->ob[object_id]=(MngBuffer *)
3971             AcquireMagickMemory(sizeof(MngBuffer));
3972
3973           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3974             {
3975               mng_info->ob[object_id]->image=(Image *) NULL;
3976               mng_info->ob[object_id]->reference_count=1;
3977             }
3978         }
3979
3980       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3981           mng_info->ob[object_id]->frozen)
3982         {
3983           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3984              png_error(ping,"Memory allocation failed");
3985
3986           if (mng_info->ob[object_id]->frozen)
3987             png_error(ping,"Cannot overwrite frozen MNG object buffer");
3988         }
3989
3990       else
3991         {
3992
3993           if (mng_info->ob[object_id]->image != (Image *) NULL)
3994             mng_info->ob[object_id]->image=DestroyImage
3995                 (mng_info->ob[object_id]->image);
3996
3997           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3998             exception);
3999
4000           if (mng_info->ob[object_id]->image != (Image *) NULL)
4001             mng_info->ob[object_id]->image->file=(FILE *) NULL;
4002
4003           else
4004             png_error(ping, "Cloning image for object buffer failed");
4005
4006           if (ping_width > 250000L || ping_height > 250000L)
4007              png_error(ping,"PNG Image dimensions are too large.");
4008
4009           mng_info->ob[object_id]->width=ping_width;
4010           mng_info->ob[object_id]->height=ping_height;
4011           mng_info->ob[object_id]->color_type=ping_color_type;
4012           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
4013           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
4014           mng_info->ob[object_id]->compression_method=
4015              ping_compression_method;
4016           mng_info->ob[object_id]->filter_method=ping_filter_method;
4017
4018           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
4019             {
4020               png_colorp
4021                 plte;
4022
4023               /*
4024                 Copy the PLTE to the object buffer.
4025               */
4026               png_get_PLTE(ping,ping_info,&plte,&number_colors);
4027               mng_info->ob[object_id]->plte_length=number_colors;
4028
4029               for (i=0; i < number_colors; i++)
4030               {
4031                 mng_info->ob[object_id]->plte[i]=plte[i];
4032               }
4033             }
4034
4035           else
4036               mng_info->ob[object_id]->plte_length=0;
4037         }
4038     }
4039 #endif
4040
4041    /* Set image->alpha_trait to MagickTrue if the input colortype supports
4042     * alpha or if a valid tRNS chunk is present, no matter whether there
4043     * is actual transparency present.
4044     */
4045     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
4046         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
4047         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
4048         BlendPixelTrait : UndefinedPixelTrait;
4049     if (image->alpha_trait == BlendPixelTrait)
4050       (void) SetImageStorageClass(image,DirectClass,exception);
4051
4052 #if 0  /* I'm not sure what's wrong here but it does not work. */
4053     if (image->alpha_trait != UndefinedPixelTrait)
4054     {
4055       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
4056         (void) SetImageType(image,GrayscaleAlphaType,exception);
4057
4058       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4059         (void) SetImageType(image,PaletteAlphaType,exception);
4060
4061       else
4062         (void) SetImageType(image,TrueColorAlphaType,exception);
4063     }
4064
4065     else
4066     {
4067       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
4068         (void) SetImageType(image,GrayscaleType,exception);
4069
4070       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4071         (void) SetImageType(image,PaletteType,exception);
4072
4073       else
4074         (void) SetImageType(image,TrueColorType,exception);
4075     }
4076 #endif
4077
4078    /* Set more properties for identify to retrieve */
4079    {
4080      char
4081        msg[MagickPathExtent];
4082
4083      if (num_text_total != 0)
4084        {
4085          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
4086          (void) FormatLocaleString(msg,MagickPathExtent,
4087             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
4088          (void) SetImageProperty(image,"png:text",msg,
4089                 exception);
4090        }
4091
4092      if (num_raw_profiles != 0)
4093        {
4094          (void) FormatLocaleString(msg,MagickPathExtent,
4095             "%d were found", num_raw_profiles);
4096          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
4097                 exception);
4098        }
4099
4100      /* cHRM chunk: */
4101      if (ping_found_cHRM != MagickFalse)
4102        {
4103          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4104             "chunk was found (see Chromaticity, above)");
4105          (void) SetImageProperty(image,"png:cHRM",msg,
4106                 exception);
4107        }
4108
4109      /* bKGD chunk: */
4110      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
4111        {
4112          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4113             "chunk was found (see Background color, above)");
4114          (void) SetImageProperty(image,"png:bKGD",msg,
4115                 exception);
4116        }
4117
4118      (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4119         "chunk was found");
4120
4121 #if defined(PNG_iCCP_SUPPORTED)
4122      /* iCCP chunk: */
4123      if (ping_found_iCCP != MagickFalse)
4124         (void) SetImageProperty(image,"png:iCCP",msg,
4125                 exception);
4126 #endif
4127
4128      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
4129         (void) SetImageProperty(image,"png:tRNS",msg,
4130                 exception);
4131
4132 #if defined(PNG_sRGB_SUPPORTED)
4133      /* sRGB chunk: */
4134      if (ping_found_sRGB != MagickFalse)
4135        {
4136          (void) FormatLocaleString(msg,MagickPathExtent,
4137             "intent=%d (%s)",
4138             (int) intent,
4139             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
4140          (void) SetImageProperty(image,"png:sRGB",msg,
4141                  exception);
4142        }
4143 #endif
4144
4145      /* gAMA chunk: */
4146      if (ping_found_gAMA != MagickFalse)
4147        {
4148          (void) FormatLocaleString(msg,MagickPathExtent,
4149             "gamma=%.8g (See Gamma, above)",
4150             file_gamma);
4151          (void) SetImageProperty(image,"png:gAMA",msg,
4152                 exception);
4153        }
4154
4155 #if defined(PNG_pHYs_SUPPORTED)
4156      /* pHYs chunk: */
4157      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
4158        {
4159          (void) FormatLocaleString(msg,MagickPathExtent,
4160             "x_res=%.10g, y_res=%.10g, units=%d",
4161             (double) x_resolution,(double) y_resolution, unit_type);
4162          (void) SetImageProperty(image,"png:pHYs",msg,
4163                 exception);
4164        }
4165 #endif
4166
4167 #if defined(PNG_oFFs_SUPPORTED)
4168      /* oFFs chunk: */
4169      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4170        {
4171          (void) FormatLocaleString(msg,MagickPathExtent,
4172             "x_off=%.20g, y_off=%.20g",
4173             (double) image->page.x,(double) image->page.y);
4174          (void) SetImageProperty(image,"png:oFFs",msg,
4175                 exception);
4176        }
4177 #endif
4178
4179 #if defined(PNG_tIME_SUPPORTED)
4180      read_tIME_chunk(image,ping,end_info,exception);
4181 #endif
4182 #if defined(PNG_READ_eXIf_SUPPORTED)
4183     read_eXIf_chunk(image,ping,end_info,exception);
4184 #endif
4185
4186      /* caNv chunk: */
4187      if ((image->page.width != 0 && image->page.width != image->columns) ||
4188          (image->page.height != 0 && image->page.height != image->rows) ||
4189          (image->page.x != 0 || image->page.y != 0))
4190        {
4191          (void) FormatLocaleString(msg,MagickPathExtent,
4192             "width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
4193             (double) image->page.width,(double) image->page.height,
4194             (double) image->page.x,(double) image->page.y);
4195          (void) SetImageProperty(image,"png:caNv",msg,
4196                 exception);
4197        }
4198    }
4199
4200   /*
4201     Relinquish resources.
4202   */
4203   png_destroy_read_struct(&ping,&ping_info,&end_info);
4204
4205   pixel_info=RelinquishVirtualMemory(pixel_info);
4206
4207   if (logging != MagickFalse)
4208     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4209       "  exit ReadOnePNGImage()");
4210
4211 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4212   UnlockSemaphoreInfo(ping_semaphore);
4213 #endif
4214
4215   /* }  for navigation to beginning of SETJMP-protected block, revert to
4216    *    Throwing an Exception when an error occurs.
4217    */
4218
4219   return(image);
4220
4221 /* end of reading one PNG image */
4222 }
4223
4224 static Image *ReadPNGImage(const ImageInfo *image_info,
4225   ExceptionInfo *exception)
4226 {
4227   Image
4228     *image;
4229
4230   MagickBooleanType
4231     logging,
4232     status;
4233
4234   MngInfo
4235     *mng_info;
4236
4237   char
4238     magic_number[MagickPathExtent];
4239
4240   ssize_t
4241     count;
4242
4243   /*
4244     Open image file.
4245   */
4246   assert(image_info != (const ImageInfo *) NULL);
4247   assert(image_info->signature == MagickCoreSignature);
4248
4249   if (image_info->debug != MagickFalse)
4250     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4251       image_info->filename);
4252
4253   assert(exception != (ExceptionInfo *) NULL);
4254   assert(exception->signature == MagickCoreSignature);
4255   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4256   image=AcquireImage(image_info,exception);
4257   mng_info=(MngInfo *) NULL;
4258   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4259
4260   if (status == MagickFalse)
4261     return(DestroyImageList(image));
4262
4263   /*
4264     Verify PNG signature.
4265   */
4266   count=ReadBlob(image,8,(unsigned char *) magic_number);
4267
4268   if ((count < 8) || (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0))
4269     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4270
4271   /*
4272      Verify that file size large enough to contain a PNG datastream.
4273   */
4274   if (GetBlobSize(image) < 61)
4275     ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
4276
4277   /*
4278     Allocate a MngInfo structure.
4279   */
4280   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4281
4282   if (mng_info == (MngInfo *) NULL)
4283     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4284
4285   /*
4286     Initialize members of the MngInfo structure.
4287   */
4288   (void) memset(mng_info,0,sizeof(MngInfo));
4289   mng_info->image=image;
4290
4291   image=ReadOnePNGImage(mng_info,image_info,exception);
4292   mng_info=MngInfoFreeStruct(mng_info);
4293
4294   if (image == (Image *) NULL)
4295     {
4296       if (logging != MagickFalse)
4297         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4298           "exit ReadPNGImage() with error");
4299
4300       return((Image *) NULL);
4301     }
4302
4303   (void) CloseBlob(image);
4304
4305   if ((image->columns == 0) || (image->rows == 0))
4306     {
4307       if (logging != MagickFalse)
4308         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4309           "exit ReadPNGImage() with error.");
4310
4311       ThrowReaderException(CorruptImageError,"CorruptImage");
4312     }
4313
4314   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4315       ((image->gamma < .45) || (image->gamma > .46)) &&
4316            !(image->chromaticity.red_primary.x>0.6399f &&
4317            image->chromaticity.red_primary.x<0.6401f &&
4318            image->chromaticity.red_primary.y>0.3299f &&
4319            image->chromaticity.red_primary.y<0.3301f &&
4320            image->chromaticity.green_primary.x>0.2999f &&
4321            image->chromaticity.green_primary.x<0.3001f &&
4322            image->chromaticity.green_primary.y>0.5999f &&
4323            image->chromaticity.green_primary.y<0.6001f &&
4324            image->chromaticity.blue_primary.x>0.1499f &&
4325            image->chromaticity.blue_primary.x<0.1501f &&
4326            image->chromaticity.blue_primary.y>0.0599f &&
4327            image->chromaticity.blue_primary.y<0.0601f &&
4328            image->chromaticity.white_point.x>0.3126f &&
4329            image->chromaticity.white_point.x<0.3128f &&
4330            image->chromaticity.white_point.y>0.3289f &&
4331            image->chromaticity.white_point.y<0.3291f))
4332     {
4333        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4334           "SetImageColorspace to RGBColorspace");
4335        SetImageColorspace(image,RGBColorspace,exception);
4336     }
4337
4338   if (logging != MagickFalse)
4339     {
4340        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4341            "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4342                (double) image->page.width,(double) image->page.height,
4343                (double) image->page.x,(double) image->page.y);
4344        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4345            "  image->colorspace: %d", (int) image->colorspace);
4346     }
4347
4348   if (logging != MagickFalse)
4349     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4350
4351   return(image);
4352 }
4353
4354
4355
4356 #if defined(JNG_SUPPORTED)
4357 /*
4358 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4359 %                                                                             %
4360 %                                                                             %
4361 %                                                                             %
4362 %   R e a d O n e J N G I m a g e                                             %
4363 %                                                                             %
4364 %                                                                             %
4365 %                                                                             %
4366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4367 %
4368 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4369 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
4370 %  necessary for the new Image structure and returns a pointer to the new
4371 %  image.
4372 %
4373 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4374 %
4375 %  The format of the ReadOneJNGImage method is:
4376 %
4377 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4378 %         ExceptionInfo *exception)
4379 %
4380 %  A description of each parameter follows:
4381 %
4382 %    o mng_info: Specifies a pointer to a MngInfo structure.
4383 %
4384 %    o image_info: the image info.
4385 %
4386 %    o exception: return any errors or warnings in this structure.
4387 %
4388 */
4389 static void
4390 DestroyJNG(unsigned char *chunk,Image **color_image,
4391   ImageInfo **color_image_info,Image **alpha_image,
4392   ImageInfo **alpha_image_info)
4393 {
4394   (void) RelinquishMagickMemory(chunk);
4395   if (color_image_info && *color_image_info)
4396   {
4397     DestroyImageInfo(*color_image_info);
4398     *color_image_info = (ImageInfo *)NULL;
4399   }
4400   if (alpha_image_info && *alpha_image_info)
4401   {
4402     DestroyImageInfo(*alpha_image_info);
4403     *alpha_image_info = (ImageInfo *)NULL;
4404   }
4405   if (color_image && *color_image)
4406   {
4407     DestroyImage(*color_image);
4408     *color_image = (Image *)NULL;
4409   }
4410   if (alpha_image && *alpha_image)
4411   {
4412     DestroyImage(*alpha_image);
4413     *alpha_image = (Image *)NULL;
4414   }
4415 }
4416 static Image *ReadOneJNGImage(MngInfo *mng_info,
4417     const ImageInfo *image_info, ExceptionInfo *exception)
4418 {
4419   Image
4420     *alpha_image,
4421     *color_image,
4422     *image,
4423     *jng_image;
4424
4425   ImageInfo
4426     *alpha_image_info,
4427     *color_image_info;
4428
4429   MagickBooleanType
4430     logging;
4431
4432   ssize_t
4433     y;
4434
4435   MagickBooleanType
4436     status;
4437
4438   png_uint_32
4439     jng_height,
4440     jng_width;
4441
4442   png_byte
4443     jng_color_type,
4444     jng_image_sample_depth,
4445     jng_image_compression_method,
4446     jng_image_interlace_method,
4447     jng_alpha_sample_depth,
4448     jng_alpha_compression_method,
4449     jng_alpha_filter_method,
4450     jng_alpha_interlace_method;
4451
4452   register const Quantum
4453     *s;
4454
4455   register ssize_t
4456     i,
4457     x;
4458
4459   register Quantum
4460     *q;
4461
4462   register unsigned char
4463     *p;
4464
4465   unsigned int
4466     read_JSEP,
4467     reading_idat;
4468
4469   size_t
4470     length;
4471
4472   jng_alpha_compression_method=0;
4473   jng_alpha_sample_depth=8;
4474   jng_color_type=0;
4475   jng_height=0;
4476   jng_width=0;
4477   alpha_image=(Image *) NULL;
4478   color_image=(Image *) NULL;
4479   alpha_image_info=(ImageInfo *) NULL;
4480   color_image_info=(ImageInfo *) NULL;
4481
4482   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4483     "  Enter ReadOneJNGImage()");
4484
4485   image=mng_info->image;
4486
4487   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4488     {
4489       /*
4490         Allocate next image structure.
4491       */
4492       if (logging != MagickFalse)
4493         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4494            "  AcquireNextImage()");
4495
4496       AcquireNextImage(image_info,image,exception);
4497
4498       if (GetNextImageInList(image) == (Image *) NULL)
4499         return(DestroyImageList(image));
4500
4501       image=SyncNextImageInList(image);
4502     }
4503   mng_info->image=image;
4504
4505   /*
4506     Signature bytes have already been read.
4507   */
4508
4509   read_JSEP=MagickFalse;
4510   reading_idat=MagickFalse;
4511   for (;;)
4512   {
4513     char
4514       type[MagickPathExtent];
4515
4516     unsigned char
4517       *chunk;
4518
4519     unsigned int
4520       count;
4521
4522     /*
4523       Read a new JNG chunk.
4524     */
4525     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4526       2*GetBlobSize(image));
4527
4528     if (status == MagickFalse)
4529       break;
4530
4531     type[0]='\0';
4532     (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4533     length=(size_t) ReadBlobMSBLong(image);
4534     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4535
4536     if (logging != MagickFalse)
4537       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4538         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4539         type[0],type[1],type[2],type[3],(double) length);
4540
4541     if (length > PNG_UINT_31_MAX || count == 0)
4542       {
4543         DestroyJNG(NULL,&color_image,&color_image_info,
4544           &alpha_image,&alpha_image_info);
4545         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4546       }
4547     if (length > GetBlobSize(image))
4548       {
4549         DestroyJNG(NULL,&color_image,&color_image_info,
4550           &alpha_image,&alpha_image_info);
4551         ThrowReaderException(CorruptImageError,
4552           "InsufficientImageDataInFile");
4553       }
4554
4555     p=NULL;
4556     chunk=(unsigned char *) NULL;
4557
4558     if (length != 0)
4559       {
4560         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4561
4562         if (chunk == (unsigned char *) NULL)
4563           {
4564             DestroyJNG(NULL,&color_image,&color_image_info,
4565               &alpha_image,&alpha_image_info);
4566             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4567           }
4568
4569         for (i=0; i < (ssize_t) length; i++)
4570         {
4571           int
4572             c;
4573
4574           c=ReadBlobByte(image);
4575           if (c == EOF)
4576             break;
4577           chunk[i]=(unsigned char) c;
4578         }
4579         for ( ; i < (ssize_t) length; i++)
4580           chunk[i]='\0';
4581
4582         p=chunk;
4583       }
4584
4585     (void) ReadBlobMSBLong(image);  /* read crc word */
4586
4587     if (memcmp(type,mng_JHDR,4) == 0)
4588       {
4589         if (length == 16)
4590           {
4591             jng_width=(png_uint_32)mng_get_long(p);
4592             jng_height=(png_uint_32)mng_get_long(&p[4]);
4593             if ((jng_width == 0) || (jng_height == 0))
4594               {
4595                 DestroyJNG(chunk,&color_image,&color_image_info,
4596                   &alpha_image,&alpha_image_info);
4597                 ThrowReaderException(CorruptImageError,
4598                   "NegativeOrZeroImageSize");
4599               }
4600             jng_color_type=p[8];
4601             jng_image_sample_depth=p[9];
4602             jng_image_compression_method=p[10];
4603             jng_image_interlace_method=p[11];
4604
4605             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4606               NoInterlace;
4607
4608             jng_alpha_sample_depth=p[12];
4609             jng_alpha_compression_method=p[13];
4610             jng_alpha_filter_method=p[14];
4611             jng_alpha_interlace_method=p[15];
4612
4613             if (logging != MagickFalse)
4614               {
4615                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4616                   "    jng_width:      %16lu,    jng_height:     %16lu\n"
4617                   "    jng_color_type: %16d,     jng_image_sample_depth: %3d\n"
4618                   "    jng_image_compression_method:%3d",
4619                   (unsigned long) jng_width, (unsigned long) jng_height,
4620                   jng_color_type, jng_image_sample_depth,
4621                   jng_image_compression_method);
4622
4623                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4624                   "    jng_image_interlace_method:  %3d"
4625                   "    jng_alpha_sample_depth:      %3d",
4626                   jng_image_interlace_method,
4627                   jng_alpha_sample_depth);
4628
4629                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4630                   "    jng_alpha_compression_method:%3d\n"
4631                   "    jng_alpha_filter_method:     %3d\n"
4632                   "    jng_alpha_interlace_method:  %3d",
4633                   jng_alpha_compression_method,
4634                   jng_alpha_filter_method,
4635                   jng_alpha_interlace_method);
4636               }
4637           }
4638
4639         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4640
4641         if ((jng_width > 65535) || (jng_height > 65535) ||
4642             (MagickSizeType) jng_width > GetMagickResourceLimit(WidthResource) ||
4643             (MagickSizeType) jng_height > GetMagickResourceLimit(HeightResource))
4644           {
4645             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4646                "    JNG width or height too large: (%lu x %lu)",
4647                 (long) jng_width, (long) jng_height);
4648             DestroyJNG(chunk,&color_image,&color_image_info,
4649               &alpha_image,&alpha_image_info);
4650             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4651           }
4652
4653         continue;
4654       }
4655
4656
4657     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4658         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4659          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4660       {
4661         /*
4662            o create color_image
4663            o open color_blob, attached to color_image
4664            o if (color type has alpha)
4665                open alpha_blob, attached to alpha_image
4666         */
4667
4668         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4669
4670         if (color_image_info == (ImageInfo *) NULL)
4671         {
4672           DestroyJNG(chunk,&color_image,&color_image_info,
4673               &alpha_image,&alpha_image_info);
4674           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4675         }
4676
4677         GetImageInfo(color_image_info);
4678         color_image=AcquireImage(color_image_info,exception);
4679
4680         if (color_image == (Image *) NULL)
4681         {
4682           DestroyJNG(chunk,&color_image,&color_image_info,
4683               &alpha_image,&alpha_image_info);
4684           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4685         }
4686
4687         if (logging != MagickFalse)
4688           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4689             "    Creating color_blob.");
4690
4691         (void) AcquireUniqueFilename(color_image->filename);
4692         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4693           exception);
4694
4695         if (status == MagickFalse)
4696           {
4697             DestroyJNG(chunk,&color_image,&color_image_info,
4698               &alpha_image,&alpha_image_info);
4699             return(DestroyImageList(image));
4700           }
4701
4702         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4703           {
4704             alpha_image_info=(ImageInfo *)
4705               AcquireMagickMemory(sizeof(ImageInfo));
4706
4707             if (alpha_image_info == (ImageInfo *) NULL)
4708               {
4709                 DestroyJNG(chunk,&color_image,&color_image_info,
4710                   &alpha_image,&alpha_image_info);
4711                 ThrowReaderException(ResourceLimitError,
4712                   "MemoryAllocationFailed");
4713               }
4714
4715             GetImageInfo(alpha_image_info);
4716             alpha_image=AcquireImage(alpha_image_info,exception);
4717
4718             if (alpha_image == (Image *) NULL)
4719               {
4720                 DestroyJNG(chunk,&color_image,&color_image_info,
4721                   &alpha_image,&alpha_image_info);
4722                 ThrowReaderException(ResourceLimitError,
4723                   "MemoryAllocationFailed");
4724               }
4725
4726             if (logging != MagickFalse)
4727               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4728                 "    Creating alpha_blob.");
4729
4730             (void) AcquireUniqueFilename(alpha_image->filename);
4731             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4732               exception);
4733
4734             if (status == MagickFalse)
4735               {
4736                 DestroyJNG(chunk,&color_image,&color_image_info,
4737                   &alpha_image,&alpha_image_info);
4738                 return(DestroyImageList(image));
4739               }
4740
4741             if (jng_alpha_compression_method == 0)
4742               {
4743                 unsigned char
4744                   data[18];
4745
4746                 if (logging != MagickFalse)
4747                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4748                     "    Writing IHDR chunk to alpha_blob.");
4749
4750                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4751                   "\211PNG\r\n\032\n");
4752
4753                 (void) WriteBlobMSBULong(alpha_image,13L);
4754                 PNGType(data,mng_IHDR);
4755                 LogPNGChunk(logging,mng_IHDR,13L);
4756                 PNGLong(data+4,jng_width);
4757                 PNGLong(data+8,jng_height);
4758                 data[12]=jng_alpha_sample_depth;
4759                 data[13]=0; /* color_type gray */
4760                 data[14]=0; /* compression method 0 */
4761                 data[15]=0; /* filter_method 0 */
4762                 data[16]=0; /* interlace_method 0 */
4763                 (void) WriteBlob(alpha_image,17,data);
4764                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4765               }
4766           }
4767         reading_idat=MagickTrue;
4768       }
4769
4770     if (memcmp(type,mng_JDAT,4) == 0)
4771       {
4772         /* Copy chunk to color_image->blob */
4773
4774         if (logging != MagickFalse)
4775           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4776             "    Copying JDAT chunk data to color_blob.");
4777
4778         if ((length != 0) && (color_image != (Image *) NULL))
4779           (void) WriteBlob(color_image,length,chunk);
4780         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4781         continue;
4782       }
4783
4784     if (memcmp(type,mng_IDAT,4) == 0)
4785       {
4786         png_byte
4787            data[5];
4788
4789         /* Copy IDAT header and chunk data to alpha_image->blob */
4790
4791         if (alpha_image != NULL && image_info->ping == MagickFalse)
4792           {
4793             if (logging != MagickFalse)
4794               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4795                 "    Copying IDAT chunk data to alpha_blob.");
4796
4797             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4798             PNGType(data,mng_IDAT);
4799             LogPNGChunk(logging,mng_IDAT,length);
4800             (void) WriteBlob(alpha_image,4,data);
4801             (void) WriteBlob(alpha_image,length,chunk);
4802             (void) WriteBlobMSBULong(alpha_image,
4803               crc32(crc32(0,data,4),chunk,(uInt) length));
4804           }
4805
4806         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4807
4808         continue;
4809       }
4810
4811     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4812       {
4813         /* Copy chunk data to alpha_image->blob */
4814
4815         if ((alpha_image != NULL) && (image_info->ping == MagickFalse) &&
4816             (length != 0))
4817           {
4818             if (logging != MagickFalse)
4819               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4820                 "    Copying JDAA chunk data to alpha_blob.");
4821
4822             (void) WriteBlob(alpha_image,length,chunk);
4823           }
4824
4825         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4826
4827         continue;
4828       }
4829
4830     if (memcmp(type,mng_JSEP,4) == 0)
4831       {
4832         read_JSEP=MagickTrue;
4833
4834         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4835
4836         continue;
4837       }
4838
4839     if (memcmp(type,mng_bKGD,4) == 0)
4840       {
4841         if (length == 2)
4842           {
4843             image->background_color.red=ScaleCharToQuantum(p[1]);
4844             image->background_color.green=image->background_color.red;
4845             image->background_color.blue=image->background_color.red;
4846           }
4847
4848         if (length == 6)
4849           {
4850             image->background_color.red=ScaleCharToQuantum(p[1]);
4851             image->background_color.green=ScaleCharToQuantum(p[3]);
4852             image->background_color.blue=ScaleCharToQuantum(p[5]);
4853           }
4854
4855         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4856         continue;
4857       }
4858
4859     if (memcmp(type,mng_gAMA,4) == 0)
4860       {
4861         if (length == 4)
4862           image->gamma=((float) mng_get_long(p))*0.00001;
4863
4864         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4865         continue;
4866       }
4867
4868     if (memcmp(type,mng_cHRM,4) == 0)
4869       {
4870         if (length == 32)
4871           {
4872             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4873             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4874             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4875             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4876             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4877             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4878             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4879             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4880           }
4881
4882         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4883         continue;
4884       }
4885
4886     if (memcmp(type,mng_sRGB,4) == 0)
4887       {
4888         if (length == 1)
4889           {
4890             image->rendering_intent=
4891               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4892             image->gamma=0.45455f;
4893             image->chromaticity.red_primary.x=0.6400f;
4894             image->chromaticity.red_primary.y=0.3300f;
4895             image->chromaticity.green_primary.x=0.3000f;
4896             image->chromaticity.green_primary.y=0.6000f;
4897             image->chromaticity.blue_primary.x=0.1500f;
4898             image->chromaticity.blue_primary.y=0.0600f;
4899             image->chromaticity.white_point.x=0.3127f;
4900             image->chromaticity.white_point.y=0.3290f;
4901           }
4902
4903         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4904         continue;
4905       }
4906
4907     if (memcmp(type,mng_oFFs,4) == 0)
4908       {
4909         if (length > 8)
4910           {
4911             image->page.x=(ssize_t) mng_get_long(p);
4912             image->page.y=(ssize_t) mng_get_long(&p[4]);
4913
4914             if ((int) p[8] != 0)
4915               {
4916                 image->page.x/=10000;
4917                 image->page.y/=10000;
4918               }
4919           }
4920
4921         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4922
4923         continue;
4924       }
4925
4926     if (memcmp(type,mng_pHYs,4) == 0)
4927       {
4928         if (length > 8)
4929           {
4930             image->resolution.x=(double) mng_get_long(p);
4931             image->resolution.y=(double) mng_get_long(&p[4]);
4932             if ((int) p[8] == PNG_RESOLUTION_METER)
4933               {
4934                 image->units=PixelsPerCentimeterResolution;
4935                 image->resolution.x=image->resolution.x/100.0f;
4936                 image->resolution.y=image->resolution.y/100.0f;
4937               }
4938           }
4939
4940         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4941         continue;
4942       }
4943
4944 #if 0
4945     if (memcmp(type,mng_iCCP,4) == 0)
4946       {
4947         /* To do: */
4948         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4949
4950         continue;
4951       }
4952 #endif
4953
4954     chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4955
4956     if (memcmp(type,mng_IEND,4))
4957       continue;
4958
4959     break;
4960   }
4961
4962
4963   /* IEND found */
4964
4965   /*
4966     Finish up reading image data:
4967
4968        o read main image from color_blob.
4969
4970        o close color_blob.
4971
4972        o if (color_type has alpha)
4973             if alpha_encoding is PNG
4974                read secondary image from alpha_blob via ReadPNG
4975             if alpha_encoding is JPEG
4976                read secondary image from alpha_blob via ReadJPEG
4977
4978        o close alpha_blob.
4979
4980        o copy intensity of secondary image into
4981          alpha samples of main image.
4982
4983        o destroy the secondary image.
4984   */
4985
4986   if (color_image_info == (ImageInfo *) NULL)
4987     {
4988       assert(color_image == (Image *) NULL);
4989       assert(alpha_image == (Image *) NULL);
4990       if (color_image != (Image *) NULL)
4991         color_image=DestroyImageList(color_image);
4992       return(DestroyImageList(image));
4993     }
4994
4995   if (color_image == (Image *) NULL)
4996     {
4997       assert(alpha_image == (Image *) NULL);
4998       ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
4999     }
5000
5001   (void) SeekBlob(color_image,0,SEEK_SET);
5002
5003   if (logging != MagickFalse)
5004     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5005       "    Reading jng_image from color_blob.");
5006
5007   assert(color_image_info != (ImageInfo *) NULL);
5008   (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,
5009     "jpeg:%s",color_image->filename);
5010
5011   color_image_info->ping=MagickFalse;   /* To do: avoid this */
5012   jng_image=ReadImage(color_image_info,exception);
5013
5014   (void) RelinquishUniqueFileResource(color_image->filename);
5015   color_image=DestroyImage(color_image);
5016   color_image_info=DestroyImageInfo(color_image_info);
5017
5018   if (jng_image == (Image *) NULL)
5019   {
5020     DestroyJNG(NULL,NULL,NULL,&alpha_image,&alpha_image_info);
5021     return(DestroyImageList(image));
5022   }
5023
5024
5025   if (logging != MagickFalse)
5026     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5027       "    Copying jng_image pixels to main image.");
5028
5029   image->rows=jng_height;
5030   image->columns=jng_width;
5031
5032   status=SetImageExtent(image,image->columns,image->rows,exception);
5033   if (status == MagickFalse)
5034     {
5035       DestroyJNG(NULL,&color_image,&color_image_info,&alpha_image,
5036         &alpha_image_info);
5037       jng_image=DestroyImageList(jng_image);
5038       return(DestroyImageList(image));
5039     }
5040   if ((image->columns != jng_image->columns) ||
5041       (image->rows != jng_image->rows))
5042     {
5043       DestroyJNG(NULL,&color_image,&color_image_info,&alpha_image,
5044         &alpha_image_info);
5045       jng_image=DestroyImageList(jng_image);
5046       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5047     }
5048   for (y=0; y < (ssize_t) image->rows; y++)
5049   {
5050     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5051     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5052     if ((s == (const Quantum *)  NULL) || (q == (Quantum *) NULL))
5053       break;
5054     for (x=(ssize_t) image->columns; x != 0; x--)
5055     {
5056       SetPixelRed(image,GetPixelRed(jng_image,s),q);
5057       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
5058       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
5059       q+=GetPixelChannels(image);
5060       s+=GetPixelChannels(jng_image);
5061     }
5062
5063     if (SyncAuthenticPixels(image,exception) == MagickFalse)
5064       break;
5065   }
5066
5067   jng_image=DestroyImage(jng_image);
5068
5069   if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
5070     {
5071       if (jng_alpha_compression_method == 0)
5072         {
5073           png_byte
5074             data[5];
5075           (void) WriteBlobMSBULong(alpha_image,0x00000000L);
5076           PNGType(data,mng_IEND);
5077           LogPNGChunk(logging,mng_IEND,0L);
5078           (void) WriteBlob(alpha_image,4,data);
5079           (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
5080         }
5081
5082       (void) CloseBlob(alpha_image);
5083
5084       if (logging != MagickFalse)
5085         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5086           "    Reading alpha from alpha_blob.");
5087
5088       (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
5089         "%s",alpha_image->filename);
5090
5091       jng_image=ReadImage(alpha_image_info,exception);
5092
5093       if (jng_image != (Image *) NULL)
5094         for (y=0; y < (ssize_t) image->rows; y++)
5095         {
5096           s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5097           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5098           if ((s == (const Quantum *)  NULL) || (q == (Quantum *) NULL))
5099             break;
5100
5101           if (image->alpha_trait != UndefinedPixelTrait)
5102             for (x=(ssize_t) image->columns; x != 0; x--)
5103             {
5104               SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5105               q+=GetPixelChannels(image);
5106               s+=GetPixelChannels(jng_image);
5107             }
5108
5109           else
5110             for (x=(ssize_t) image->columns; x != 0; x--)
5111             {
5112               SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5113               if (GetPixelAlpha(image,q) != OpaqueAlpha)
5114                 image->alpha_trait=BlendPixelTrait;
5115               q+=GetPixelChannels(image);
5116               s+=GetPixelChannels(jng_image);
5117             }
5118
5119           if (SyncAuthenticPixels(image,exception) == MagickFalse)
5120             break;
5121         }
5122       (void) RelinquishUniqueFileResource(alpha_image->filename);
5123       alpha_image=DestroyImage(alpha_image);
5124       alpha_image_info=DestroyImageInfo(alpha_image_info);
5125       if (jng_image != (Image *) NULL)
5126         jng_image=DestroyImage(jng_image);
5127     }
5128
5129   /* Read the JNG image.  */
5130
5131   if (mng_info->mng_type == 0)
5132     {
5133       mng_info->mng_width=jng_width;
5134       mng_info->mng_height=jng_height;
5135     }
5136
5137   if (image->page.width == 0 && image->page.height == 0)
5138     {
5139       image->page.width=jng_width;
5140       image->page.height=jng_height;
5141     }
5142
5143   if (image->page.x == 0 && image->page.y == 0)
5144     {
5145       image->page.x=mng_info->x_off[mng_info->object_id];
5146       image->page.y=mng_info->y_off[mng_info->object_id];
5147     }
5148
5149   else
5150     {
5151       image->page.y=mng_info->y_off[mng_info->object_id];
5152     }
5153
5154   mng_info->image_found++;
5155   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
5156     2*GetBlobSize(image));
5157
5158   if (status == MagickFalse)
5159     return(DestroyImageList(image));
5160
5161   if (logging != MagickFalse)
5162     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5163       "  exit ReadOneJNGImage()");
5164
5165   return(image);
5166 }
5167
5168 /*
5169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5170 %                                                                             %
5171 %                                                                             %
5172 %                                                                             %
5173 %   R e a d J N G I m a g e                                                   %
5174 %                                                                             %
5175 %                                                                             %
5176 %                                                                             %
5177 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5178 %
5179 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
5180 %  (including the 8-byte signature)  and returns it.  It allocates the memory
5181 %  necessary for the new Image structure and returns a pointer to the new
5182 %  image.
5183 %
5184 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
5185 %
5186 %  The format of the ReadJNGImage method is:
5187 %
5188 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
5189 %         *exception)
5190 %
5191 %  A description of each parameter follows:
5192 %
5193 %    o image_info: the image info.
5194 %
5195 %    o exception: return any errors or warnings in this structure.
5196 %
5197 */
5198
5199 static Image *ReadJNGImage(const ImageInfo *image_info,
5200                 ExceptionInfo *exception)
5201 {
5202   Image
5203     *image;
5204
5205   MagickBooleanType
5206     logging,
5207     status;
5208
5209   MngInfo
5210     *mng_info;
5211
5212   char
5213     magic_number[MagickPathExtent];
5214
5215   size_t
5216     count;
5217
5218   /*
5219     Open image file.
5220   */
5221   assert(image_info != (const ImageInfo *) NULL);
5222   assert(image_info->signature == MagickCoreSignature);
5223   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5224      image_info->filename);
5225   assert(exception != (ExceptionInfo *) NULL);
5226   assert(exception->signature == MagickCoreSignature);
5227   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
5228   image=AcquireImage(image_info,exception);
5229   mng_info=(MngInfo *) NULL;
5230   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5231
5232   if (status == MagickFalse)
5233     return(DestroyImageList(image));
5234
5235   if (LocaleCompare(image_info->magick,"JNG") != 0)
5236     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5237
5238   /* Verify JNG signature.  */
5239
5240   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5241
5242   if ((count < 8) || (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0))
5243     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5244
5245   /*
5246      Verify that file size large enough to contain a JNG datastream.
5247   */
5248   if (GetBlobSize(image) < 147)
5249     ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5250
5251   /* Allocate a MngInfo structure.  */
5252
5253   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
5254
5255   if (mng_info == (MngInfo *) NULL)
5256     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5257
5258   /* Initialize members of the MngInfo structure.  */
5259
5260   (void) memset(mng_info,0,sizeof(MngInfo));
5261
5262   mng_info->image=image;
5263   image=ReadOneJNGImage(mng_info,image_info,exception);
5264   mng_info=MngInfoFreeStruct(mng_info);
5265
5266   if (image == (Image *) NULL)
5267     {
5268       if (logging != MagickFalse)
5269         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5270           "exit ReadJNGImage() with error");
5271
5272       return((Image *) NULL);
5273     }
5274   (void) CloseBlob(image);
5275
5276   if (image->columns == 0 || image->rows == 0)
5277     {
5278       if (logging != MagickFalse)
5279         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5280           "exit ReadJNGImage() with error");
5281
5282       ThrowReaderException(CorruptImageError,"CorruptImage");
5283     }
5284
5285   if (logging != MagickFalse)
5286     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5287
5288   return(image);
5289 }
5290 #endif
5291
5292 static Image *ReadOneMNGImage(MngInfo* mng_info, const ImageInfo *image_info,
5293      ExceptionInfo *exception)
5294 {
5295   char
5296     page_geometry[MagickPathExtent];
5297
5298   Image
5299     *image;
5300
5301   MagickBooleanType
5302     logging;
5303
5304   volatile int
5305     first_mng_object,
5306     object_id,
5307     term_chunk_found,
5308     skip_to_iend;
5309
5310   volatile ssize_t
5311     image_count=0;
5312
5313   MagickBooleanType
5314     status;
5315
5316   MagickOffsetType
5317     offset;
5318
5319   MngBox
5320     default_fb,
5321     fb,
5322     previous_fb;
5323
5324 #if defined(MNG_INSERT_LAYERS)
5325   PixelInfo
5326     mng_background_color;
5327 #endif
5328
5329   register unsigned char
5330     *p;
5331
5332   register ssize_t
5333     i;
5334
5335   size_t
5336     count;
5337
5338   ssize_t
5339     loop_level;
5340
5341   volatile short
5342     skipping_loop;
5343
5344 #if defined(MNG_INSERT_LAYERS)
5345   unsigned int
5346     mandatory_back=0;
5347 #endif
5348
5349   volatile unsigned int
5350 #ifdef MNG_OBJECT_BUFFERS
5351     mng_background_object=0,
5352 #endif
5353     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5354
5355   size_t
5356     default_frame_timeout,
5357     frame_timeout,
5358 #if defined(MNG_INSERT_LAYERS)
5359     image_height,
5360     image_width,
5361 #endif
5362     length;
5363
5364   /* These delays are all measured in image ticks_per_second,
5365    * not in MNG ticks_per_second
5366    */
5367   volatile size_t
5368     default_frame_delay,
5369     final_delay,
5370     final_image_delay,
5371     frame_delay,
5372 #if defined(MNG_INSERT_LAYERS)
5373     insert_layers,
5374 #endif
5375     mng_iterations=1,
5376     simplicity=0,
5377     subframe_height=0,
5378     subframe_width=0;
5379
5380   previous_fb.top=0;
5381   previous_fb.bottom=0;
5382   previous_fb.left=0;
5383   previous_fb.right=0;
5384   default_fb.top=0;
5385   default_fb.bottom=0;
5386   default_fb.left=0;
5387   default_fb.right=0;
5388
5389   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
5390     "  Enter ReadOneMNGImage()");
5391
5392   image=mng_info->image;
5393
5394   if (LocaleCompare(image_info->magick,"MNG") == 0)
5395     {
5396       char
5397         magic_number[MagickPathExtent];
5398
5399       /* Verify MNG signature.  */
5400       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5401       if ((count < 8) || (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0))
5402         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5403
5404       /* Initialize some nonzero members of the MngInfo structure.  */
5405       for (i=0; i < MNG_MAX_OBJECTS; i++)
5406       {
5407         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5408         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5409       }
5410       mng_info->exists[0]=MagickTrue;
5411     }
5412
5413   skipping_loop=(-1);
5414   first_mng_object=MagickTrue;
5415   mng_type=0;
5416 #if defined(MNG_INSERT_LAYERS)
5417   insert_layers=MagickFalse; /* should be False during convert or mogrify */
5418 #endif
5419   default_frame_delay=0;
5420   default_frame_timeout=0;
5421   frame_delay=0;
5422   final_delay=1;
5423   mng_info->ticks_per_second=1UL*image->ticks_per_second;
5424   object_id=0;
5425   skip_to_iend=MagickFalse;
5426   term_chunk_found=MagickFalse;
5427   mng_info->framing_mode=1;
5428 #if defined(MNG_INSERT_LAYERS)
5429   mandatory_back=MagickFalse;
5430 #endif
5431 #if defined(MNG_INSERT_LAYERS)
5432   mng_background_color=image->background_color;
5433 #endif
5434   default_fb=mng_info->frame;
5435   previous_fb=mng_info->frame;
5436   do
5437   {
5438     char
5439       type[MagickPathExtent];
5440
5441     if (LocaleCompare(image_info->magick,"MNG") == 0)
5442       {
5443         unsigned char
5444           *chunk;
5445
5446         /*
5447           Read a new chunk.
5448         */
5449         type[0]='\0';
5450         (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5451         length=(size_t) ReadBlobMSBLong(image);
5452         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5453
5454         if (logging != MagickFalse)
5455           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5456            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
5457            type[0],type[1],type[2],type[3],(double) length);
5458
5459         if ((length > PNG_UINT_31_MAX) || (length > GetBlobSize(image)) ||
5460             (count < 4))
5461           ThrowReaderException(CorruptImageError,"CorruptImage");
5462
5463         p=NULL;
5464         chunk=(unsigned char *) NULL;
5465
5466         if (length != 0)
5467           {
5468             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5469
5470             if (chunk == (unsigned char *) NULL)
5471               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5472
5473             for (i=0; i < (ssize_t) length; i++)
5474             {
5475               int
5476                 c;
5477
5478               c=ReadBlobByte(image);
5479               if (c == EOF)
5480                 {
5481                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5482                   ThrowReaderException(CorruptImageError,
5483                     "InsufficientImageDataInFile");
5484                 }
5485               chunk[i]=(unsigned char) c;
5486             }
5487
5488             p=chunk;
5489           }
5490
5491         (void) ReadBlobMSBLong(image);  /* read crc word */
5492
5493 #if !defined(JNG_SUPPORTED)
5494         if (memcmp(type,mng_JHDR,4) == 0)
5495           {
5496             skip_to_iend=MagickTrue;
5497
5498             if (mng_info->jhdr_warning == 0)
5499               (void) ThrowMagickException(exception,GetMagickModule(),
5500                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5501
5502             mng_info->jhdr_warning++;
5503           }
5504 #endif
5505         if (memcmp(type,mng_DHDR,4) == 0)
5506           {
5507             skip_to_iend=MagickTrue;
5508
5509             if (mng_info->dhdr_warning == 0)
5510               (void) ThrowMagickException(exception,GetMagickModule(),
5511                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5512
5513             mng_info->dhdr_warning++;
5514           }
5515         if (memcmp(type,mng_MEND,4) == 0)
5516           {
5517             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5518             break;
5519           }
5520
5521         if (skip_to_iend)
5522           {
5523             if (memcmp(type,mng_IEND,4) == 0)
5524               skip_to_iend=MagickFalse;
5525
5526             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5527
5528             if (logging != MagickFalse)
5529               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5530                 "  Skip to IEND.");
5531
5532             continue;
5533           }
5534
5535         if (memcmp(type,mng_MHDR,4) == 0)
5536           {
5537             if (length != 28)
5538               {
5539                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5540                 ThrowReaderException(CorruptImageError,"CorruptImage");
5541               }
5542
5543             mng_info->mng_width=(unsigned long)mng_get_long(p);
5544             mng_info->mng_height=(unsigned long)mng_get_long(&p[4]);
5545
5546             if (logging != MagickFalse)
5547               {
5548                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5549                   "  MNG width: %.20g",(double) mng_info->mng_width);
5550                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5551                   "  MNG height: %.20g",(double) mng_info->mng_height);
5552               }
5553
5554             p+=8;
5555             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5556
5557             if (mng_info->ticks_per_second == 0)
5558               default_frame_delay=0;
5559
5560             else
5561               default_frame_delay=1UL*image->ticks_per_second/
5562                 mng_info->ticks_per_second;
5563
5564             frame_delay=default_frame_delay;
5565             simplicity=0;
5566
5567             p+=16;
5568             simplicity=(size_t) mng_get_long(p);
5569
5570             mng_type=1;    /* Full MNG */
5571
5572             if ((simplicity != 0) && ((simplicity | 11) == 11))
5573               mng_type=2; /* LC */
5574
5575             if ((simplicity != 0) && ((simplicity | 9) == 9))
5576               mng_type=3; /* VLC */
5577
5578 #if defined(MNG_INSERT_LAYERS)
5579             if (mng_type != 3)
5580               insert_layers=MagickTrue;
5581 #endif
5582             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5583               {
5584                 /* Allocate next image structure.  */
5585                 AcquireNextImage(image_info,image,exception);
5586
5587                 if (GetNextImageInList(image) == (Image *) NULL)
5588                   return((Image *) NULL);
5589
5590                 image=SyncNextImageInList(image);
5591                 mng_info->image=image;
5592               }
5593
5594             if ((mng_info->mng_width > 65535L) ||
5595                 (mng_info->mng_height > 65535L))
5596               {
5597                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5598                 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5599               }
5600
5601             (void) FormatLocaleString(page_geometry,MagickPathExtent,
5602               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5603               mng_info->mng_height);
5604
5605             mng_info->frame.left=0;
5606             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5607             mng_info->frame.top=0;
5608             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5609             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5610
5611             for (i=0; i < MNG_MAX_OBJECTS; i++)
5612               mng_info->object_clip[i]=mng_info->frame;
5613
5614             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5615             continue;
5616           }
5617
5618         if (memcmp(type,mng_TERM,4) == 0)
5619           {
5620             int
5621               repeat=0;
5622
5623             if (length != 0)
5624               repeat=p[0];
5625
5626             if (repeat == 3 && length > 9)
5627               {
5628                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5629                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5630
5631                 if (mng_iterations == PNG_UINT_31_MAX)
5632                   mng_iterations=0;
5633
5634                 image->iterations=mng_iterations;
5635                 term_chunk_found=MagickTrue;
5636               }
5637
5638             if (logging != MagickFalse)
5639               {
5640                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5641                   "    repeat=%d,  final_delay=%.20g,  iterations=%.20g",
5642                   repeat,(double) final_delay, (double) image->iterations);
5643               }
5644
5645             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5646             continue;
5647           }
5648         if (memcmp(type,mng_DEFI,4) == 0)
5649           {
5650             if (mng_type == 3)
5651               {
5652                 (void) ThrowMagickException(exception,GetMagickModule(),
5653                   CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5654                   image->filename);
5655                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5656                 continue;
5657               }
5658
5659             if (length < 2)
5660               {
5661                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5662                 ThrowReaderException(CorruptImageError,"CorruptImage");
5663               }
5664
5665             object_id=((unsigned int) p[0] << 8) | (unsigned int) p[1];
5666
5667             if (mng_type == 2 && object_id != 0)
5668               (void) ThrowMagickException(exception,GetMagickModule(),
5669                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5670                 image->filename);
5671
5672             if (object_id >= MNG_MAX_OBJECTS)
5673               {
5674                 /*
5675                   Instead of using a warning we should allocate a larger
5676                   MngInfo structure and continue.
5677                 */
5678                 (void) ThrowMagickException(exception,GetMagickModule(),
5679                   CoderError,"object id too large","`%s'",image->filename);
5680                 object_id=MNG_MAX_OBJECTS-1;
5681               }
5682
5683             if (mng_info->exists[object_id])
5684               if (mng_info->frozen[object_id])
5685                 {
5686                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5687                   (void) ThrowMagickException(exception,
5688                     GetMagickModule(),CoderError,
5689                     "DEFI cannot redefine a frozen MNG object","`%s'",
5690                     image->filename);
5691                   continue;
5692                 }
5693
5694             mng_info->exists[object_id]=MagickTrue;
5695
5696             if (length > 2)
5697               mng_info->invisible[object_id]=p[2];
5698
5699             /*
5700               Extract object offset info.
5701             */
5702             if (length > 11)
5703               {
5704                 mng_info->x_off[object_id]=(ssize_t) mng_get_long(&p[4]);
5705                 mng_info->y_off[object_id]=(ssize_t) mng_get_long(&p[8]);
5706                 if (logging != MagickFalse)
5707                   {
5708                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5709                       "  x_off[%d]: %.20g,  y_off[%d]: %.20g",
5710                       object_id,(double) mng_info->x_off[object_id],
5711                       object_id,(double) mng_info->y_off[object_id]);
5712                   }
5713               }
5714
5715             /*
5716               Extract object clipping info.
5717             */
5718             if (length > 27)
5719               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5720                 &p[12]);
5721
5722             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5723             continue;
5724           }
5725         if (memcmp(type,mng_bKGD,4) == 0)
5726           {
5727             mng_info->have_global_bkgd=MagickFalse;
5728
5729             if (length > 5)
5730               {
5731                 mng_info->mng_global_bkgd.red=
5732                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5733
5734                 mng_info->mng_global_bkgd.green=
5735                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5736
5737                 mng_info->mng_global_bkgd.blue=
5738                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5739
5740                 mng_info->have_global_bkgd=MagickTrue;
5741               }
5742
5743             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5744             continue;
5745           }
5746         if (memcmp(type,mng_BACK,4) == 0)
5747           {
5748 #if defined(MNG_INSERT_LAYERS)
5749             if (length > 6)
5750               mandatory_back=p[6];
5751
5752             else
5753               mandatory_back=0;
5754
5755             if (mandatory_back && length > 5)
5756               {
5757                 mng_background_color.red=
5758                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5759
5760                 mng_background_color.green=
5761                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5762
5763                 mng_background_color.blue=
5764                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5765
5766                 mng_background_color.alpha=OpaqueAlpha;
5767               }
5768
5769 #ifdef MNG_OBJECT_BUFFERS
5770             if (length > 8)
5771               mng_background_object=(p[7] << 8) | p[8];
5772 #endif
5773 #endif
5774             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5775             continue;
5776           }
5777
5778         if (memcmp(type,mng_PLTE,4) == 0)
5779           {
5780             /* Read global PLTE.  */
5781
5782             if (length && (length < 769))
5783               {
5784                 /* Read global PLTE.  */
5785
5786                 if (mng_info->global_plte == (png_colorp) NULL)
5787                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5788                     sizeof(*mng_info->global_plte));
5789
5790                 if (mng_info->global_plte == (png_colorp) NULL)
5791                   {
5792                     mng_info->global_plte_length=0;
5793                     chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5794                     ThrowReaderException(ResourceLimitError,
5795                       "MemoryAllocationFailed");
5796                   }
5797
5798                 for (i=0; i < (ssize_t) (length/3); i++)
5799                 {
5800                   mng_info->global_plte[i].red=p[3*i];
5801                   mng_info->global_plte[i].green=p[3*i+1];
5802                   mng_info->global_plte[i].blue=p[3*i+2];
5803                 }
5804
5805                 mng_info->global_plte_length=(unsigned int) (length/3);
5806               }
5807 #ifdef MNG_LOOSE
5808             for ( ; i < 256; i++)
5809             {
5810               mng_info->global_plte[i].red=i;
5811               mng_info->global_plte[i].green=i;
5812               mng_info->global_plte[i].blue=i;
5813             }
5814
5815             if (length != 0)
5816               mng_info->global_plte_length=256;
5817 #endif
5818             else
5819               mng_info->global_plte_length=0;
5820
5821             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5822             continue;
5823           }
5824
5825         if (memcmp(type,mng_tRNS,4) == 0)
5826           {
5827             /* read global tRNS */
5828
5829             if (length > 0 && length < 257)
5830               for (i=0; i < (ssize_t) length; i++)
5831                 mng_info->global_trns[i]=p[i];
5832
5833 #ifdef MNG_LOOSE
5834             for ( ; i < 256; i++)
5835               mng_info->global_trns[i]=255;
5836 #endif
5837             mng_info->global_trns_length=(unsigned int) length;
5838             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5839             continue;
5840           }
5841         if (memcmp(type,mng_gAMA,4) == 0)
5842           {
5843             if (length == 4)
5844               {
5845                 ssize_t
5846                   igamma;
5847
5848                 igamma=mng_get_long(p);
5849                 mng_info->global_gamma=((float) igamma)*0.00001;
5850                 mng_info->have_global_gama=MagickTrue;
5851               }
5852
5853             else
5854               mng_info->have_global_gama=MagickFalse;
5855
5856             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5857             continue;
5858           }
5859
5860         if (memcmp(type,mng_cHRM,4) == 0)
5861           {
5862             /* Read global cHRM */
5863
5864             if (length == 32)
5865               {
5866                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5867                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5868                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5869                 mng_info->global_chrm.red_primary.y=0.00001*
5870                   mng_get_long(&p[12]);
5871                 mng_info->global_chrm.green_primary.x=0.00001*
5872                   mng_get_long(&p[16]);
5873                 mng_info->global_chrm.green_primary.y=0.00001*
5874                   mng_get_long(&p[20]);
5875                 mng_info->global_chrm.blue_primary.x=0.00001*
5876                   mng_get_long(&p[24]);
5877                 mng_info->global_chrm.blue_primary.y=0.00001*
5878                   mng_get_long(&p[28]);
5879                 mng_info->have_global_chrm=MagickTrue;
5880               }
5881             else
5882               mng_info->have_global_chrm=MagickFalse;
5883
5884             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5885             continue;
5886           }
5887
5888         if (memcmp(type,mng_sRGB,4) == 0)
5889           {
5890             /*
5891               Read global sRGB.
5892             */
5893             if (length != 0)
5894               {
5895                 mng_info->global_srgb_intent=
5896                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5897                 mng_info->have_global_srgb=MagickTrue;
5898               }
5899             else
5900               mng_info->have_global_srgb=MagickFalse;
5901
5902             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5903             continue;
5904           }
5905
5906         if (memcmp(type,mng_iCCP,4) == 0)
5907           {
5908             /* To do: */
5909
5910             /*
5911               Read global iCCP.
5912             */
5913             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5914
5915             continue;
5916           }
5917
5918         if (memcmp(type,mng_FRAM,4) == 0)
5919           {
5920             if (mng_type == 3)
5921               (void) ThrowMagickException(exception,GetMagickModule(),
5922                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5923                 image->filename);
5924
5925             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5926               image->delay=frame_delay;
5927
5928             frame_delay=default_frame_delay;
5929             frame_timeout=default_frame_timeout;
5930             fb=default_fb;
5931
5932             if (length != 0)
5933               if (p[0])
5934                 mng_info->framing_mode=p[0];
5935
5936             if (logging != MagickFalse)
5937               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5938                 "    Framing_mode=%d",mng_info->framing_mode);
5939
5940             if (length > 6)
5941               {
5942                 /* Note the delay and frame clipping boundaries.  */
5943
5944                 p++; /* framing mode */
5945
5946                 while (((p-chunk) < (long) length) && *p)
5947                   p++;  /* frame name */
5948
5949                 p++;  /* frame name terminator */
5950
5951                 if ((p-chunk) < (ssize_t) (length-4))
5952                   {
5953                     int
5954                       change_delay,
5955                       change_timeout,
5956                       change_clipping;
5957
5958                     change_delay=(*p++);
5959                     change_timeout=(*p++);
5960                     change_clipping=(*p++);
5961                     p++; /* change_sync */
5962
5963                     if (change_delay && ((p-chunk) < (ssize_t) (length-4)))
5964                       {
5965                         frame_delay=1UL*image->ticks_per_second*
5966                           mng_get_long(p);
5967
5968                         if (mng_info->ticks_per_second != 0)
5969                           frame_delay/=mng_info->ticks_per_second;
5970
5971                         else
5972                           frame_delay=PNG_UINT_31_MAX;
5973
5974                         if (change_delay == 2)
5975                           default_frame_delay=frame_delay;
5976
5977                         p+=4;
5978
5979                         if (logging != MagickFalse)
5980                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5981                             "    Framing_delay=%.20g",(double) frame_delay);
5982                       }
5983
5984                     if (change_timeout && ((p-chunk) < (ssize_t) (length-4)))
5985                       {
5986                         frame_timeout=1UL*image->ticks_per_second*
5987                           mng_get_long(p);
5988
5989                         if (mng_info->ticks_per_second != 0)
5990                           frame_timeout/=mng_info->ticks_per_second;
5991
5992                         else
5993                           frame_timeout=PNG_UINT_31_MAX;
5994
5995                         if (change_timeout == 2)
5996                           default_frame_timeout=frame_timeout;
5997
5998                         p+=4;
5999
6000                         if (logging != MagickFalse)
6001                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6002                             "    Framing_timeout=%.20g",(double) frame_timeout);
6003                       }
6004
6005                     if (change_clipping && ((p-chunk) < (ssize_t) (length-16)))
6006                       {
6007                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
6008                         p+=16;
6009                         previous_fb=fb;
6010
6011                         if (logging != MagickFalse)
6012                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6013                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
6014                             (double) fb.left,(double) fb.right,(double) fb.top,
6015                             (double) fb.bottom);
6016
6017                         if (change_clipping == 2)
6018                           default_fb=fb;
6019                       }
6020                   }
6021               }
6022             mng_info->clip=fb;
6023             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
6024
6025             subframe_width=(size_t) (mng_info->clip.right
6026                -mng_info->clip.left);
6027
6028             subframe_height=(size_t) (mng_info->clip.bottom
6029                -mng_info->clip.top);
6030             /*
6031               Insert a background layer behind the frame if framing_mode is 4.
6032             */
6033 #if defined(MNG_INSERT_LAYERS)
6034             if (logging != MagickFalse)
6035               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6036                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
6037                 subframe_width,(double) subframe_height);
6038
6039             if (insert_layers && (mng_info->framing_mode == 4) &&
6040                 (subframe_width) && (subframe_height))
6041               {
6042                 /* Allocate next image structure.  */
6043                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6044                   {
6045                     AcquireNextImage(image_info,image,exception);
6046
6047                     if (GetNextImageInList(image) == (Image *) NULL)
6048                       return(DestroyImageList(image));
6049
6050                     image=SyncNextImageInList(image);
6051                   }
6052
6053                 mng_info->image=image;
6054
6055                 if (term_chunk_found)
6056                   {
6057                     image->start_loop=MagickTrue;
6058                     image->iterations=mng_iterations;
6059                     term_chunk_found=MagickFalse;
6060                   }
6061
6062                 else
6063                     image->start_loop=MagickFalse;
6064
6065                 image->columns=subframe_width;
6066                 image->rows=subframe_height;
6067                 image->page.width=subframe_width;
6068                 image->page.height=subframe_height;
6069                 image->page.x=mng_info->clip.left;
6070                 image->page.y=mng_info->clip.top;
6071                 image->background_color=mng_background_color;
6072                 image->alpha_trait=UndefinedPixelTrait;
6073                 image->delay=0;
6074                 (void) SetImageBackgroundColor(image,exception);
6075
6076                 if (logging != MagickFalse)
6077                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6078                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6079                     (double) mng_info->clip.left,
6080                     (double) mng_info->clip.right,
6081                     (double) mng_info->clip.top,
6082                     (double) mng_info->clip.bottom);
6083               }
6084 #endif
6085             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6086             continue;
6087           }
6088
6089         if (memcmp(type,mng_CLIP,4) == 0)
6090           {
6091             unsigned int
6092               first_object,
6093               last_object;
6094
6095             /*
6096               Read CLIP.
6097             */
6098             if (length > 3)
6099               {
6100                 first_object=(p[0] << 8) | p[1];
6101                 last_object=(p[2] << 8) | p[3];
6102                 p+=4;
6103
6104                 for (i=(int) first_object; i <= (int) last_object; i++)
6105                 {
6106                   if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6107                     continue;
6108
6109                   if (mng_info->exists[i] && !mng_info->frozen[i])
6110                     {
6111                       MngBox
6112                         box;
6113
6114                       box=mng_info->object_clip[i];
6115                       if ((p-chunk) < (ssize_t) (length-17))
6116                         mng_info->object_clip[i]=
6117                            mng_read_box(box,(char) p[0],&p[1]);
6118                     }
6119                 }
6120
6121               }
6122             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6123             continue;
6124           }
6125
6126         if (memcmp(type,mng_SAVE,4) == 0)
6127           {
6128             for (i=1; i < MNG_MAX_OBJECTS; i++)
6129               if (mng_info->exists[i])
6130                 {
6131                  mng_info->frozen[i]=MagickTrue;
6132 #ifdef MNG_OBJECT_BUFFERS
6133                  if (mng_info->ob[i] != (MngBuffer *) NULL)
6134                     mng_info->ob[i]->frozen=MagickTrue;
6135 #endif
6136                 }
6137
6138             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6139
6140             continue;
6141           }
6142
6143         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
6144           {
6145             /* Read DISC or SEEK.  */
6146
6147             if ((length == 0) || (length % 2) || !memcmp(type,mng_SEEK,4))
6148               {
6149                 for (i=1; i < MNG_MAX_OBJECTS; i++)
6150                   MngInfoDiscardObject(mng_info,i);
6151               }
6152
6153             else
6154               {
6155                 register ssize_t
6156                   j;
6157
6158                 for (j=1; j < (ssize_t) length; j+=2)
6159                 {
6160                   i=p[j-1] << 8 | p[j];
6161                   MngInfoDiscardObject(mng_info,i);
6162                 }
6163               }
6164
6165             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6166
6167             continue;
6168           }
6169
6170         if (memcmp(type,mng_MOVE,4) == 0)
6171           {
6172             size_t
6173               first_object,
6174               last_object;
6175
6176             /* read MOVE */
6177
6178             if (length > 3)
6179             {
6180               first_object=(p[0] << 8) | p[1];
6181               last_object=(p[2] << 8) | p[3];
6182               p+=4;
6183
6184               for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
6185               {
6186                 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6187                   continue;
6188
6189                 if (mng_info->exists[i] && !mng_info->frozen[i] &&
6190                     (p-chunk) < (ssize_t) (length-8))
6191                   {
6192                     MngPair
6193                       new_pair;
6194
6195                     MngPair
6196                       old_pair;
6197
6198                     old_pair.a=mng_info->x_off[i];
6199                     old_pair.b=mng_info->y_off[i];
6200                     new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
6201                     mng_info->x_off[i]=new_pair.a;
6202                     mng_info->y_off[i]=new_pair.b;
6203                   }
6204               }
6205             }
6206
6207             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6208             continue;
6209           }
6210
6211         if (memcmp(type,mng_LOOP,4) == 0)
6212           {
6213             ssize_t loop_iters=1;
6214             if (length > 4)
6215               {
6216                 loop_level=chunk[0];
6217                 mng_info->loop_active[loop_level]=1;  /* mark loop active */
6218
6219                 /* Record starting point.  */
6220                 loop_iters=mng_get_long(&chunk[1]);
6221
6222                 if (logging != MagickFalse)
6223                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6224                     "  LOOP level %.20g has %.20g iterations ",
6225                     (double) loop_level, (double) loop_iters);
6226
6227                 if (loop_iters <= 0)
6228                   skipping_loop=loop_level;
6229
6230                 else
6231                   {
6232                     if ((MagickSizeType) loop_iters > GetMagickResourceLimit(ListLengthResource))
6233                       loop_iters=GetMagickResourceLimit(ListLengthResource);
6234                     if (loop_iters >= 2147483647L)
6235                       loop_iters=2147483647L;
6236                     mng_info->loop_jump[loop_level]=TellBlob(image);
6237                     mng_info->loop_count[loop_level]=loop_iters;
6238                   }
6239
6240                 mng_info->loop_iteration[loop_level]=0;
6241               }
6242             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6243             continue;
6244           }
6245
6246         if (memcmp(type,mng_ENDL,4) == 0)
6247           {
6248             if (length > 0)
6249               {
6250                 loop_level=chunk[0];
6251
6252                 if (skipping_loop > 0)
6253                   {
6254                     if (skipping_loop == loop_level)
6255                       {
6256                         /*
6257                           Found end of zero-iteration loop.
6258                         */
6259                         skipping_loop=(-1);
6260                         mng_info->loop_active[loop_level]=0;
6261                       }
6262                   }
6263
6264                 else
6265                   {
6266                     if (mng_info->loop_active[loop_level] == 1)
6267                       {
6268                         mng_info->loop_count[loop_level]--;
6269                         mng_info->loop_iteration[loop_level]++;
6270
6271                         if (logging != MagickFalse)
6272                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6273                           "  ENDL: LOOP level %.20g has %.20g remaining iters",
6274                             (double) loop_level,(double)
6275                             mng_info->loop_count[loop_level]);
6276
6277                         if (mng_info->loop_count[loop_level] != 0)
6278                           {
6279                             offset=
6280                               SeekBlob(image,mng_info->loop_jump[loop_level],
6281                               SEEK_SET);
6282
6283                             if (offset < 0)
6284                               {
6285                                 chunk=(unsigned char *) RelinquishMagickMemory(
6286                                   chunk);
6287                                 ThrowReaderException(CorruptImageError,
6288                                   "ImproperImageHeader");
6289                               }
6290                           }
6291
6292                         else
6293                           {
6294                             short
6295                               last_level;
6296
6297                             /*
6298                               Finished loop.
6299                             */
6300                             mng_info->loop_active[loop_level]=0;
6301                             last_level=(-1);
6302                             for (i=0; i < loop_level; i++)
6303                               if (mng_info->loop_active[i] == 1)
6304                                 last_level=(short) i;
6305                             loop_level=last_level;
6306                           }
6307                       }
6308                   }
6309               }
6310
6311             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6312             continue;
6313           }
6314
6315         if (memcmp(type,mng_CLON,4) == 0)
6316           {
6317             if (mng_info->clon_warning == 0)
6318               (void) ThrowMagickException(exception,GetMagickModule(),
6319                 CoderError,"CLON is not implemented yet","`%s'",
6320                 image->filename);
6321
6322             mng_info->clon_warning++;
6323           }
6324
6325         if (memcmp(type,mng_MAGN,4) == 0)
6326           {
6327             png_uint_16
6328               magn_first,
6329               magn_last,
6330               magn_mb,
6331               magn_ml,
6332               magn_mr,
6333               magn_mt,
6334               magn_mx,
6335               magn_my,
6336               magn_methx,
6337               magn_methy;
6338
6339             if (length > 1)
6340               magn_first=(p[0] << 8) | p[1];
6341
6342             else
6343               magn_first=0;
6344
6345             if (length > 3)
6346               magn_last=(p[2] << 8) | p[3];
6347
6348             else
6349               magn_last=magn_first;
6350 #ifndef MNG_OBJECT_BUFFERS
6351             if (magn_first || magn_last)
6352               if (mng_info->magn_warning == 0)
6353                 {
6354                   (void) ThrowMagickException(exception,
6355                      GetMagickModule(),CoderError,
6356                      "MAGN is not implemented yet for nonzero objects",
6357                      "`%s'",image->filename);
6358
6359                    mng_info->magn_warning++;
6360                 }
6361 #endif
6362             if (length > 4)
6363               magn_methx=p[4];
6364
6365             else
6366               magn_methx=0;
6367
6368             if (length > 6)
6369               magn_mx=(p[5] << 8) | p[6];
6370
6371             else
6372               magn_mx=1;
6373
6374             if (magn_mx == 0)
6375               magn_mx=1;
6376
6377             if (length > 8)
6378               magn_my=(p[7] << 8) | p[8];
6379
6380             else
6381               magn_my=magn_mx;
6382
6383             if (magn_my == 0)
6384               magn_my=1;
6385
6386             if (length > 10)
6387               magn_ml=(p[9] << 8) | p[10];
6388
6389             else
6390               magn_ml=magn_mx;
6391
6392             if (magn_ml == 0)
6393               magn_ml=1;
6394
6395             if (length > 12)
6396               magn_mr=(p[11] << 8) | p[12];
6397
6398             else
6399               magn_mr=magn_mx;
6400
6401             if (magn_mr == 0)
6402               magn_mr=1;
6403
6404             if (length > 14)
6405               magn_mt=(p[13] << 8) | p[14];
6406
6407             else
6408               magn_mt=magn_my;
6409
6410             if (magn_mt == 0)
6411               magn_mt=1;
6412
6413             if (length > 16)
6414               magn_mb=(p[15] << 8) | p[16];
6415
6416             else
6417               magn_mb=magn_my;
6418
6419             if (magn_mb == 0)
6420               magn_mb=1;
6421
6422             if (length > 17)
6423               magn_methy=p[17];
6424
6425             else
6426               magn_methy=magn_methx;
6427
6428
6429             if (magn_methx > 5 || magn_methy > 5)
6430               if (mng_info->magn_warning == 0)
6431                 {
6432                   (void) ThrowMagickException(exception,
6433                      GetMagickModule(),CoderError,
6434                      "Unknown MAGN method in MNG datastream","`%s'",
6435                      image->filename);
6436
6437                    mng_info->magn_warning++;
6438                 }
6439 #ifdef MNG_OBJECT_BUFFERS
6440           /* Magnify existing objects in the range magn_first to magn_last */
6441 #endif
6442             if (magn_first == 0 || magn_last == 0)
6443               {
6444                 /* Save the magnification factors for object 0 */
6445                 mng_info->magn_mb=magn_mb;
6446                 mng_info->magn_ml=magn_ml;
6447                 mng_info->magn_mr=magn_mr;
6448                 mng_info->magn_mt=magn_mt;
6449                 mng_info->magn_mx=magn_mx;
6450                 mng_info->magn_my=magn_my;
6451                 mng_info->magn_methx=magn_methx;
6452                 mng_info->magn_methy=magn_methy;
6453               }
6454           }
6455
6456         if (memcmp(type,mng_PAST,4) == 0)
6457           {
6458             if (mng_info->past_warning == 0)
6459               (void) ThrowMagickException(exception,GetMagickModule(),
6460                 CoderError,"PAST is not implemented yet","`%s'",
6461                 image->filename);
6462
6463             mng_info->past_warning++;
6464           }
6465
6466         if (memcmp(type,mng_SHOW,4) == 0)
6467           {
6468             if (mng_info->show_warning == 0)
6469               (void) ThrowMagickException(exception,GetMagickModule(),
6470                 CoderError,"SHOW is not implemented yet","`%s'",
6471                 image->filename);
6472
6473             mng_info->show_warning++;
6474           }
6475
6476         if (memcmp(type,mng_sBIT,4) == 0)
6477           {
6478             if (length < 4)
6479               mng_info->have_global_sbit=MagickFalse;
6480
6481             else
6482               {
6483                 mng_info->global_sbit.gray=p[0];
6484                 mng_info->global_sbit.red=p[0];
6485                 mng_info->global_sbit.green=p[1];
6486                 mng_info->global_sbit.blue=p[2];
6487                 mng_info->global_sbit.alpha=p[3];
6488                 mng_info->have_global_sbit=MagickTrue;
6489              }
6490           }
6491         if (memcmp(type,mng_pHYs,4) == 0)
6492           {
6493             if (length > 8)
6494               {
6495                 mng_info->global_x_pixels_per_unit=
6496                     (size_t) mng_get_long(p);
6497                 mng_info->global_y_pixels_per_unit=
6498                     (size_t) mng_get_long(&p[4]);
6499                 mng_info->global_phys_unit_type=p[8];
6500                 mng_info->have_global_phys=MagickTrue;
6501               }
6502
6503             else
6504               mng_info->have_global_phys=MagickFalse;
6505           }
6506         if (memcmp(type,mng_pHYg,4) == 0)
6507           {
6508             if (mng_info->phyg_warning == 0)
6509               (void) ThrowMagickException(exception,GetMagickModule(),
6510                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6511
6512             mng_info->phyg_warning++;
6513           }
6514         if (memcmp(type,mng_BASI,4) == 0)
6515           {
6516             skip_to_iend=MagickTrue;
6517
6518             if (mng_info->basi_warning == 0)
6519               (void) ThrowMagickException(exception,GetMagickModule(),
6520                 CoderError,"BASI is not implemented yet","`%s'",
6521                 image->filename);
6522
6523             mng_info->basi_warning++;
6524 #ifdef MNG_BASI_SUPPORTED
6525             basi_width=(unsigned long) mng_get_long(p);
6526             basi_width=(unsigned long) mng_get_long(&p[4]);
6527             basi_color_type=p[8];
6528             basi_compression_method=p[9];
6529             basi_filter_type=p[10];
6530             basi_interlace_method=p[11];
6531             if (length > 11)
6532               basi_red=((png_uint_32) p[12] << 8) & (png_uint_32) p[13];
6533
6534             else
6535               basi_red=0;
6536
6537             if (length > 13)
6538               basi_green=((png_uint_32) p[14] << 8) & (png_uint_32) p[15];
6539
6540             else
6541               basi_green=0;
6542
6543             if (length > 15)
6544               basi_blue=((png_uint_32) p[16] << 8) & (png_uint_32) p[17];
6545
6546             else
6547               basi_blue=0;
6548
6549             if (length > 17)
6550               basi_alpha=((png_uint_32) p[18] << 8) & (png_uint_32) p[19];
6551
6552             else
6553               {
6554                 if (basi_sample_depth == 16)
6555                   basi_alpha=65535L;
6556                 else
6557                   basi_alpha=255;
6558               }
6559
6560             if (length > 19)
6561               basi_viewable=p[20];
6562
6563             else
6564               basi_viewable=0;
6565
6566 #endif
6567             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6568             continue;
6569           }
6570
6571         if (memcmp(type,mng_IHDR,4)
6572 #if defined(JNG_SUPPORTED)
6573             && memcmp(type,mng_JHDR,4)
6574 #endif
6575             )
6576           {
6577             /* Not an IHDR or JHDR chunk */
6578             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6579
6580             continue;
6581           }
6582 /* Process IHDR */
6583         if (logging != MagickFalse)
6584           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6585             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6586
6587         mng_info->exists[object_id]=MagickTrue;
6588         mng_info->viewable[object_id]=MagickTrue;
6589
6590         if (mng_info->invisible[object_id])
6591           {
6592             if (logging != MagickFalse)
6593               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6594                 "  Skipping invisible object");
6595
6596             skip_to_iend=MagickTrue;
6597             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6598             continue;
6599           }
6600 #if defined(MNG_INSERT_LAYERS)
6601         if (length < 8)
6602           {
6603             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6604             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6605           }
6606
6607         image_width=(size_t) mng_get_long(p);
6608         image_height=(size_t) mng_get_long(&p[4]);
6609 #endif
6610         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6611
6612         /*
6613           Insert a transparent background layer behind the entire animation
6614           if it is not full screen.
6615         */
6616 #if defined(MNG_INSERT_LAYERS)
6617         if (insert_layers && mng_type && first_mng_object)
6618           {
6619             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6620                 (image_width < mng_info->mng_width) ||
6621                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6622                 (image_height < mng_info->mng_height) ||
6623                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6624               {
6625                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6626                   {
6627                     /*
6628                       Allocate next image structure.
6629                     */
6630                     AcquireNextImage(image_info,image,exception);
6631
6632                     if (GetNextImageInList(image) == (Image *) NULL)
6633                       return(DestroyImageList(image));
6634
6635                     image=SyncNextImageInList(image);
6636                   }
6637                 mng_info->image=image;
6638
6639                 if (term_chunk_found)
6640                   {
6641                     image->start_loop=MagickTrue;
6642                     image->iterations=mng_iterations;
6643                     term_chunk_found=MagickFalse;
6644                   }
6645
6646                 else
6647                     image->start_loop=MagickFalse;
6648
6649                 /* Make a background rectangle.  */
6650
6651                 image->delay=0;
6652                 image->columns=mng_info->mng_width;
6653                 image->rows=mng_info->mng_height;
6654                 image->page.width=mng_info->mng_width;
6655                 image->page.height=mng_info->mng_height;
6656                 image->page.x=0;
6657                 image->page.y=0;
6658                 image->background_color=mng_background_color;
6659                 (void) SetImageBackgroundColor(image,exception);
6660                 if (logging != MagickFalse)
6661                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6662                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6663                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6664               }
6665           }
6666         /*
6667           Insert a background layer behind the upcoming image if
6668           framing_mode is 3, and we haven't already inserted one.
6669         */
6670         if (insert_layers && (mng_info->framing_mode == 3) &&
6671                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6672                 (simplicity & 0x08)))
6673           {
6674             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6675             {
6676               /*
6677                 Allocate next image structure.
6678               */
6679               AcquireNextImage(image_info,image,exception);
6680
6681               if (GetNextImageInList(image) == (Image *) NULL)
6682                 return(DestroyImageList(image));
6683
6684               image=SyncNextImageInList(image);
6685             }
6686
6687             mng_info->image=image;
6688
6689             if (term_chunk_found)
6690               {
6691                 image->start_loop=MagickTrue;
6692                 image->iterations=mng_iterations;
6693                 term_chunk_found=MagickFalse;
6694               }
6695
6696             else
6697                 image->start_loop=MagickFalse;
6698
6699             image->delay=0;
6700             image->columns=subframe_width;
6701             image->rows=subframe_height;
6702             image->page.width=subframe_width;
6703             image->page.height=subframe_height;
6704             image->page.x=mng_info->clip.left;
6705             image->page.y=mng_info->clip.top;
6706             image->background_color=mng_background_color;
6707             image->alpha_trait=UndefinedPixelTrait;
6708             (void) SetImageBackgroundColor(image,exception);
6709
6710             if (logging != MagickFalse)
6711               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6712                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6713                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6714                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6715           }
6716 #endif /* MNG_INSERT_LAYERS */
6717         first_mng_object=MagickFalse;
6718
6719         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6720           {
6721             /*
6722               Allocate next image structure.
6723             */
6724             AcquireNextImage(image_info,image,exception);
6725
6726             if (GetNextImageInList(image) == (Image *) NULL)
6727               return(DestroyImageList(image));
6728
6729             image=SyncNextImageInList(image);
6730           }
6731         mng_info->image=image;
6732         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6733           GetBlobSize(image));
6734
6735         if (status == MagickFalse)
6736           break;
6737
6738         if (term_chunk_found)
6739           {
6740             image->start_loop=MagickTrue;
6741             term_chunk_found=MagickFalse;
6742           }
6743
6744         else
6745             image->start_loop=MagickFalse;
6746
6747         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6748           {
6749             image->delay=frame_delay;
6750             frame_delay=default_frame_delay;
6751           }
6752
6753         else
6754           image->delay=0;
6755
6756         image->page.width=mng_info->mng_width;
6757         image->page.height=mng_info->mng_height;
6758         image->page.x=mng_info->x_off[object_id];
6759         image->page.y=mng_info->y_off[object_id];
6760         image->iterations=mng_iterations;
6761
6762         /*
6763           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6764         */
6765
6766         if (logging != MagickFalse)
6767           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6768             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6769             type[2],type[3]);
6770
6771         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6772
6773         if (offset < 0)
6774           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6775       }
6776
6777     mng_info->image=image;
6778     mng_info->mng_type=mng_type;
6779     mng_info->object_id=object_id;
6780
6781     if (memcmp(type,mng_IHDR,4) == 0)
6782       image=ReadOnePNGImage(mng_info,image_info,exception);
6783
6784 #if defined(JNG_SUPPORTED)
6785     else
6786       image=ReadOneJNGImage(mng_info,image_info,exception);
6787 #endif
6788
6789     if (image == (Image *) NULL)
6790       {
6791         if (logging != MagickFalse)
6792           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6793             "exit ReadJNGImage() with error");
6794
6795         return((Image *) NULL);
6796       }
6797
6798     if (image->columns == 0 || image->rows == 0)
6799       {
6800         (void) CloseBlob(image);
6801         return(DestroyImageList(image));
6802       }
6803
6804     mng_info->image=image;
6805
6806     if (mng_type)
6807       {
6808         MngBox
6809           crop_box;
6810
6811         if (mng_info->magn_methx || mng_info->magn_methy)
6812           {
6813             png_uint_32
6814                magnified_height,
6815                magnified_width;
6816
6817             if (logging != MagickFalse)
6818               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6819                 "  Processing MNG MAGN chunk");
6820
6821             if (mng_info->magn_methx == 1)
6822               {
6823                 magnified_width=mng_info->magn_ml;
6824
6825                 if (image->columns > 1)
6826                    magnified_width += mng_info->magn_mr;
6827
6828                 if (image->columns > 2)
6829                    magnified_width += (png_uint_32)
6830                       ((image->columns-2)*(mng_info->magn_mx));
6831               }
6832
6833             else
6834               {
6835                 magnified_width=(png_uint_32) image->columns;
6836
6837                 if (image->columns > 1)
6838                    magnified_width += mng_info->magn_ml-1;
6839
6840                 if (image->columns > 2)
6841                    magnified_width += mng_info->magn_mr-1;
6842
6843                 if (image->columns > 3)
6844                    magnified_width += (png_uint_32)
6845                       ((image->columns-3)*(mng_info->magn_mx-1));
6846               }
6847
6848             if (mng_info->magn_methy == 1)
6849               {
6850                 magnified_height=mng_info->magn_mt;
6851
6852                 if (image->rows > 1)
6853                    magnified_height += mng_info->magn_mb;
6854
6855                 if (image->rows > 2)
6856                    magnified_height += (png_uint_32)
6857                       ((image->rows-2)*(mng_info->magn_my));
6858               }
6859
6860             else
6861               {
6862                 magnified_height=(png_uint_32) image->rows;
6863
6864                 if (image->rows > 1)
6865                    magnified_height += mng_info->magn_mt-1;
6866
6867                 if (image->rows > 2)
6868                    magnified_height += mng_info->magn_mb-1;
6869
6870                 if (image->rows > 3)
6871                    magnified_height += (png_uint_32)
6872                       ((image->rows-3)*(mng_info->magn_my-1));
6873               }
6874
6875             if (magnified_height > image->rows ||
6876                 magnified_width > image->columns)
6877               {
6878                 Image
6879                   *large_image;
6880
6881                 int
6882                   yy;
6883
6884                 Quantum
6885                   *next,
6886                   *prev;
6887
6888                 png_uint_16
6889                   magn_methx,
6890                   magn_methy;
6891
6892                 ssize_t
6893                   m,
6894                   y;
6895
6896                 register Quantum
6897                   *n,
6898                   *q;
6899
6900                 register ssize_t
6901                   x;
6902
6903                 /* Allocate next image structure.  */
6904
6905                 if (logging != MagickFalse)
6906                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6907                     "    Allocate magnified image");
6908
6909                 AcquireNextImage(image_info,image,exception);
6910
6911                 if (GetNextImageInList(image) == (Image *) NULL)
6912                   return(DestroyImageList(image));
6913
6914                 large_image=SyncNextImageInList(image);
6915
6916                 large_image->columns=magnified_width;
6917                 large_image->rows=magnified_height;
6918
6919                 magn_methx=mng_info->magn_methx;
6920                 magn_methy=mng_info->magn_methy;
6921
6922 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6923 #define QM unsigned short
6924                 if (magn_methx != 1 || magn_methy != 1)
6925                   {
6926                   /*
6927                      Scale pixels to unsigned shorts to prevent
6928                      overflow of intermediate values of interpolations
6929                   */
6930                      for (y=0; y < (ssize_t) image->rows; y++)
6931                      {
6932                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6933                           exception);
6934                        if (q == (Quantum *) NULL)
6935                          break;
6936                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6937                        {
6938                           SetPixelRed(image,ScaleQuantumToShort(
6939                             GetPixelRed(image,q)),q);
6940                           SetPixelGreen(image,ScaleQuantumToShort(
6941                             GetPixelGreen(image,q)),q);
6942                           SetPixelBlue(image,ScaleQuantumToShort(
6943                             GetPixelBlue(image,q)),q);
6944                           SetPixelAlpha(image,ScaleQuantumToShort(
6945                             GetPixelAlpha(image,q)),q);
6946                           q+=GetPixelChannels(image);
6947                        }
6948
6949                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6950                          break;
6951                      }
6952                   }
6953 #else
6954 #define QM Quantum
6955 #endif
6956
6957                 if (image->alpha_trait != UndefinedPixelTrait)
6958                    (void) SetImageBackgroundColor(large_image,exception);
6959
6960                 else
6961                   {
6962                     large_image->background_color.alpha=OpaqueAlpha;
6963                     (void) SetImageBackgroundColor(large_image,exception);
6964
6965                     if (magn_methx == 4)
6966                       magn_methx=2;
6967
6968                     if (magn_methx == 5)
6969                       magn_methx=3;
6970
6971                     if (magn_methy == 4)
6972                       magn_methy=2;
6973
6974                     if (magn_methy == 5)
6975                       magn_methy=3;
6976                   }
6977
6978                 /* magnify the rows into the right side of the large image */
6979
6980                 if (logging != MagickFalse)
6981                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6982                     "    Magnify the rows to %.20g",
6983                     (double) large_image->rows);
6984                 m=(ssize_t) mng_info->magn_mt;
6985                 yy=0;
6986                 length=(size_t) GetPixelChannels(image)*image->columns;
6987                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6988                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6989
6990                 if ((prev == (Quantum *) NULL) ||
6991                     (next == (Quantum *) NULL))
6992                   {
6993                     if (prev != (Quantum *) NULL)
6994                       prev=(Quantum *) RelinquishMagickMemory(prev);
6995                     if (next != (Quantum *) NULL)
6996                       next=(Quantum *) RelinquishMagickMemory(next);
6997                     image=DestroyImageList(image);
6998                     ThrowReaderException(ResourceLimitError,
6999                       "MemoryAllocationFailed");
7000                   }
7001
7002                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
7003                 (void) memcpy(next,n,length);
7004
7005                 for (y=0; y < (ssize_t) image->rows; y++)
7006                 {
7007                   if (y == 0)
7008                     m=(ssize_t) mng_info->magn_mt;
7009
7010                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
7011                     m=(ssize_t) mng_info->magn_mb;
7012
7013                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
7014                     m=(ssize_t) mng_info->magn_mb;
7015
7016                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
7017                     m=1;
7018
7019                   else
7020                     m=(ssize_t) mng_info->magn_my;
7021
7022                   n=prev;
7023                   prev=next;
7024                   next=n;
7025
7026                   if (y < (ssize_t) image->rows-1)
7027                     {
7028                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
7029                           exception);
7030                       (void) memcpy(next,n,length);
7031                     }
7032
7033                   for (i=0; i < m; i++, yy++)
7034                   {
7035                     register Quantum
7036                       *pixels;
7037
7038                     assert(yy < (ssize_t) large_image->rows);
7039                     pixels=prev;
7040                     n=next;
7041                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
7042                       1,exception);
7043                     if (q == (Quantum *) NULL)
7044                       break;
7045                     q+=(large_image->columns-image->columns)*
7046                       GetPixelChannels(large_image);
7047
7048                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
7049                     {
7050                       /* To do: get color as function of indexes[x] */
7051                       /*
7052                       if (image->storage_class == PseudoClass)
7053                         {
7054                         }
7055                       */
7056
7057                       if (magn_methy <= 1)
7058                         {
7059                           /* replicate previous */
7060                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
7061                           SetPixelGreen(large_image,GetPixelGreen(image,
7062                              pixels),q);
7063                           SetPixelBlue(large_image,GetPixelBlue(image,
7064                              pixels),q);
7065                           SetPixelAlpha(large_image,GetPixelAlpha(image,
7066                              pixels),q);
7067                         }
7068
7069                       else if (magn_methy == 2 || magn_methy == 4)
7070                         {
7071                           if (i == 0)
7072                             {
7073                               SetPixelRed(large_image,GetPixelRed(image,
7074                                  pixels),q);
7075                               SetPixelGreen(large_image,GetPixelGreen(image,
7076                                  pixels),q);
7077                               SetPixelBlue(large_image,GetPixelBlue(image,
7078                                  pixels),q);
7079                               SetPixelAlpha(large_image,GetPixelAlpha(image,
7080                                  pixels),q);
7081                             }
7082
7083                           else
7084                             {
7085                               /* Interpolate */
7086                               SetPixelRed(large_image,((QM) (((ssize_t)
7087                                  (2*i*(GetPixelRed(image,n)
7088                                  -GetPixelRed(image,pixels)+m))/
7089                                  ((ssize_t) (m*2))
7090                                  +GetPixelRed(image,pixels)))),q);
7091                               SetPixelGreen(large_image,((QM) (((ssize_t)
7092                                  (2*i*(GetPixelGreen(image,n)
7093                                  -GetPixelGreen(image,pixels)+m))/
7094                                  ((ssize_t) (m*2))
7095                                  +GetPixelGreen(image,pixels)))),q);
7096                               SetPixelBlue(large_image,((QM) (((ssize_t)
7097                                  (2*i*(GetPixelBlue(image,n)
7098                                  -GetPixelBlue(image,pixels)+m))/
7099                                  ((ssize_t) (m*2))
7100                                  +GetPixelBlue(image,pixels)))),q);
7101
7102                               if (image->alpha_trait != UndefinedPixelTrait)
7103                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
7104                                     (2*i*(GetPixelAlpha(image,n)
7105                                     -GetPixelAlpha(image,pixels)+m))
7106                                     /((ssize_t) (m*2))+
7107                                    GetPixelAlpha(image,pixels)))),q);
7108                             }
7109
7110                           if (magn_methy == 4)
7111                             {
7112                               /* Replicate nearest */
7113                               if (i <= ((m+1) << 1))
7114                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
7115                                     pixels),q);
7116                               else
7117                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
7118                                     n),q);
7119                             }
7120                         }
7121
7122                       else /* if (magn_methy == 3 || magn_methy == 5) */
7123                         {
7124                           /* Replicate nearest */
7125                           if (i <= ((m+1) << 1))
7126                           {
7127                              SetPixelRed(large_image,GetPixelRed(image,
7128                                     pixels),q);
7129                              SetPixelGreen(large_image,GetPixelGreen(image,
7130                                     pixels),q);
7131                              SetPixelBlue(large_image,GetPixelBlue(image,
7132                                     pixels),q);
7133                              SetPixelAlpha(large_image,GetPixelAlpha(image,
7134                                     pixels),q);
7135                           }
7136
7137                           else
7138                           {
7139                              SetPixelRed(large_image,GetPixelRed(image,n),q);
7140                              SetPixelGreen(large_image,GetPixelGreen(image,n),
7141                                     q);
7142                              SetPixelBlue(large_image,GetPixelBlue(image,n),
7143                                     q);
7144                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
7145                                     q);
7146                           }
7147
7148                           if (magn_methy == 5)
7149                             {
7150                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
7151                                  (GetPixelAlpha(image,n)
7152                                  -GetPixelAlpha(image,pixels))
7153                                  +m))/((ssize_t) (m*2))
7154                                  +GetPixelAlpha(image,pixels)),q);
7155                             }
7156                         }
7157                       n+=GetPixelChannels(image);
7158                       q+=GetPixelChannels(large_image);
7159                       pixels+=GetPixelChannels(image);
7160                     } /* x */
7161
7162                     if (SyncAuthenticPixels(large_image,exception) == 0)
7163                       break;
7164
7165                   } /* i */
7166                 } /* y */
7167
7168                 prev=(Quantum *) RelinquishMagickMemory(prev);
7169                 next=(Quantum *) RelinquishMagickMemory(next);
7170
7171                 length=image->columns;
7172
7173                 if (logging != MagickFalse)
7174                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7175                     "    Delete original image");
7176
7177                 DeleteImageFromList(&image);
7178
7179                 image=large_image;
7180
7181                 mng_info->image=image;
7182
7183                 /* magnify the columns */
7184                 if (logging != MagickFalse)
7185                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7186                     "    Magnify the columns to %.20g",
7187                     (double) image->columns);
7188
7189                 for (y=0; y < (ssize_t) image->rows; y++)
7190                 {
7191                   register Quantum
7192                     *pixels;
7193
7194                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7195                   if (q == (Quantum *) NULL)
7196                     break;
7197                   pixels=q+(image->columns-length)*GetPixelChannels(image);
7198                   n=pixels+GetPixelChannels(image);
7199
7200                   for (x=(ssize_t) (image->columns-length);
7201                     x < (ssize_t) image->columns; x++)
7202                   {
7203                     /* To do: Rewrite using Get/Set***PixelChannel() */
7204
7205                     if (x == (ssize_t) (image->columns-length))
7206                       m=(ssize_t) mng_info->magn_ml;
7207
7208                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
7209                       m=(ssize_t) mng_info->magn_mr;
7210
7211                     else if (magn_methx <= 1 &&
7212                         x == (ssize_t) image->columns-1)
7213                       m=(ssize_t) mng_info->magn_mr;
7214
7215                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
7216                       m=1;
7217
7218                     else
7219                       m=(ssize_t) mng_info->magn_mx;
7220
7221                     for (i=0; i < m; i++)
7222                     {
7223                       if (magn_methx <= 1)
7224                         {
7225                           /* replicate previous */
7226                           SetPixelRed(image,GetPixelRed(image,pixels),q);
7227                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7228                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7229                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7230                         }
7231
7232                       else if (magn_methx == 2 || magn_methx == 4)
7233                         {
7234                           if (i == 0)
7235                           {
7236                             SetPixelRed(image,GetPixelRed(image,pixels),q);
7237                             SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7238                             SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7239                             SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7240                           }
7241
7242                           /* To do: Rewrite using Get/Set***PixelChannel() */
7243                           else
7244                             {
7245                               /* Interpolate */
7246                               SetPixelRed(image,(QM) ((2*i*(
7247                                  GetPixelRed(image,n)
7248                                  -GetPixelRed(image,pixels))+m)
7249                                  /((ssize_t) (m*2))+
7250                                  GetPixelRed(image,pixels)),q);
7251
7252                               SetPixelGreen(image,(QM) ((2*i*(
7253                                  GetPixelGreen(image,n)
7254                                  -GetPixelGreen(image,pixels))+m)
7255                                  /((ssize_t) (m*2))+
7256                                  GetPixelGreen(image,pixels)),q);
7257
7258                               SetPixelBlue(image,(QM) ((2*i*(
7259                                  GetPixelBlue(image,n)
7260                                  -GetPixelBlue(image,pixels))+m)
7261                                  /((ssize_t) (m*2))+
7262                                  GetPixelBlue(image,pixels)),q);
7263                               if (image->alpha_trait != UndefinedPixelTrait)
7264                                  SetPixelAlpha(image,(QM) ((2*i*(
7265                                    GetPixelAlpha(image,n)
7266                                    -GetPixelAlpha(image,pixels))+m)
7267                                    /((ssize_t) (m*2))+
7268                                    GetPixelAlpha(image,pixels)),q);
7269                             }
7270
7271                           if (magn_methx == 4)
7272                             {
7273                               /* Replicate nearest */
7274                               if (i <= ((m+1) << 1))
7275                               {
7276                                  SetPixelAlpha(image,
7277                                    GetPixelAlpha(image,pixels)+0,q);
7278                               }
7279                               else
7280                               {
7281                                  SetPixelAlpha(image,
7282                                    GetPixelAlpha(image,n)+0,q);
7283                               }
7284                             }
7285                         }
7286
7287                       else /* if (magn_methx == 3 || magn_methx == 5) */
7288                         {
7289                           /* Replicate nearest */
7290                           if (i <= ((m+1) << 1))
7291                           {
7292                              SetPixelRed(image,GetPixelRed(image,pixels),q);
7293                              SetPixelGreen(image,GetPixelGreen(image,
7294                                  pixels),q);
7295                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7296                              SetPixelAlpha(image,GetPixelAlpha(image,
7297                                  pixels),q);
7298                           }
7299
7300                           else
7301                           {
7302                              SetPixelRed(image,GetPixelRed(image,n),q);
7303                              SetPixelGreen(image,GetPixelGreen(image,n),q);
7304                              SetPixelBlue(image,GetPixelBlue(image,n),q);
7305                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7306                           }
7307
7308                           if (magn_methx == 5)
7309                             {
7310                               /* Interpolate */
7311                               SetPixelAlpha(image,
7312                                  (QM) ((2*i*( GetPixelAlpha(image,n)
7313                                  -GetPixelAlpha(image,pixels))+m)/
7314                                  ((ssize_t) (m*2))
7315                                  +GetPixelAlpha(image,pixels)),q);
7316                             }
7317                         }
7318                       q+=GetPixelChannels(image);
7319                     }
7320                     n+=GetPixelChannels(image);
7321                   }
7322
7323                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
7324                     break;
7325                 }
7326 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7327               if (magn_methx != 1 || magn_methy != 1)
7328                 {
7329                 /*
7330                    Rescale pixels to Quantum
7331                 */
7332                    for (y=0; y < (ssize_t) image->rows; y++)
7333                    {
7334                      q=GetAuthenticPixels(image,0,y,image->columns,1,
7335                        exception);
7336                      if (q == (Quantum *) NULL)
7337                        break;
7338
7339                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
7340                      {
7341                         SetPixelRed(image,ScaleShortToQuantum(
7342                           GetPixelRed(image,q)),q);
7343                         SetPixelGreen(image,ScaleShortToQuantum(
7344                           GetPixelGreen(image,q)),q);
7345                         SetPixelBlue(image,ScaleShortToQuantum(
7346                           GetPixelBlue(image,q)),q);
7347                         SetPixelAlpha(image,ScaleShortToQuantum(
7348                           GetPixelAlpha(image,q)),q);
7349                         q+=GetPixelChannels(image);
7350                      }
7351
7352                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
7353                        break;
7354                    }
7355                 }
7356 #endif
7357                 if (logging != MagickFalse)
7358                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7359                     "  Finished MAGN processing");
7360               }
7361           }
7362
7363         /*
7364           Crop_box is with respect to the upper left corner of the MNG.
7365         */
7366         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7367         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7368         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7369         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7370         crop_box=mng_minimum_box(crop_box,mng_info->clip);
7371         crop_box=mng_minimum_box(crop_box,mng_info->frame);
7372         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7373         if ((crop_box.left != (mng_info->image_box.left
7374             +mng_info->x_off[object_id])) ||
7375             (crop_box.right != (mng_info->image_box.right
7376             +mng_info->x_off[object_id])) ||
7377             (crop_box.top != (mng_info->image_box.top
7378             +mng_info->y_off[object_id])) ||
7379             (crop_box.bottom != (mng_info->image_box.bottom
7380             +mng_info->y_off[object_id])))
7381           {
7382             if (logging != MagickFalse)
7383               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7384                 "  Crop the PNG image");
7385
7386             if ((crop_box.left < crop_box.right) &&
7387                 (crop_box.top < crop_box.bottom))
7388               {
7389                 Image
7390                   *im;
7391
7392                 RectangleInfo
7393                   crop_info;
7394
7395                 /*
7396                   Crop_info is with respect to the upper left corner of
7397                   the image.
7398                 */
7399                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7400                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7401                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7402                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7403                 image->page.width=image->columns;
7404                 image->page.height=image->rows;
7405                 image->page.x=0;
7406                 image->page.y=0;
7407                 im=CropImage(image,&crop_info,exception);
7408
7409                 if (im != (Image *) NULL)
7410                   {
7411                     image->columns=im->columns;
7412                     image->rows=im->rows;
7413                     im=DestroyImage(im);
7414                     image->page.width=image->columns;
7415                     image->page.height=image->rows;
7416                     image->page.x=crop_box.left;
7417                     image->page.y=crop_box.top;
7418                   }
7419               }
7420
7421             else
7422               {
7423                 /*
7424                   No pixels in crop area.  The MNG spec still requires
7425                   a layer, though, so make a single transparent pixel in
7426                   the top left corner.
7427                 */
7428                 image->columns=1;
7429                 image->rows=1;
7430                 image->colors=2;
7431                 (void) SetImageBackgroundColor(image,exception);
7432                 image->page.width=1;
7433                 image->page.height=1;
7434                 image->page.x=0;
7435                 image->page.y=0;
7436               }
7437           }
7438 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7439         image=mng_info->image;
7440 #endif
7441       }
7442
7443 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7444       /* PNG does not handle depths greater than 16 so reduce it even
7445        * if lossy.
7446        */
7447       if (image->depth > 16)
7448          image->depth=16;
7449 #endif
7450
7451 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7452       if (image->depth > 8)
7453         {
7454           /* To do: fill low byte properly */
7455           image->depth=16;
7456         }
7457
7458       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7459          image->depth = 8;
7460 #endif
7461
7462       if (image_info->number_scenes != 0)
7463         {
7464           if (mng_info->scenes_found >
7465              (ssize_t) (image_info->first_scene+image_info->number_scenes))
7466             break;
7467         }
7468
7469       if (logging != MagickFalse)
7470         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7471           "  Finished reading image datastream.");
7472
7473   } while (LocaleCompare(image_info->magick,"MNG") == 0);
7474
7475   (void) CloseBlob(image);
7476
7477   if (logging != MagickFalse)
7478     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7479       "  Finished reading all image datastreams.");
7480
7481 #if defined(MNG_INSERT_LAYERS)
7482   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7483        (mng_info->mng_height))
7484     {
7485       /*
7486         Insert a background layer if nothing else was found.
7487       */
7488       if (logging != MagickFalse)
7489         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7490           "  No images found.  Inserting a background layer.");
7491
7492       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7493         {
7494           /*
7495             Allocate next image structure.
7496           */
7497           AcquireNextImage(image_info,image,exception);
7498           if (GetNextImageInList(image) == (Image *) NULL)
7499             {
7500               if (logging != MagickFalse)
7501                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7502                   "  Allocation failed, returning NULL.");
7503
7504               return(DestroyImageList(image));;
7505             }
7506           image=SyncNextImageInList(image);
7507         }
7508       image->columns=mng_info->mng_width;
7509       image->rows=mng_info->mng_height;
7510       image->page.width=mng_info->mng_width;
7511       image->page.height=mng_info->mng_height;
7512       image->page.x=0;
7513       image->page.y=0;
7514       image->background_color=mng_background_color;
7515       image->alpha_trait=UndefinedPixelTrait;
7516
7517       if (image_info->ping == MagickFalse)
7518         (void) SetImageBackgroundColor(image,exception);
7519
7520       mng_info->image_found++;
7521     }
7522 #endif
7523   image->iterations=mng_iterations;
7524
7525   if (mng_iterations == 1)
7526     image->start_loop=MagickTrue;
7527
7528   while (GetPreviousImageInList(image) != (Image *) NULL)
7529   {
7530     image_count++;
7531     if (image_count > 10*mng_info->image_found)
7532       {
7533         if (logging != MagickFalse)
7534           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7535
7536         (void) ThrowMagickException(exception,GetMagickModule(),
7537           CoderError,"Linked list is corrupted, beginning of list not found",
7538           "`%s'",image_info->filename);
7539
7540         return(DestroyImageList(image));
7541       }
7542
7543     image=GetPreviousImageInList(image);
7544
7545     if (GetNextImageInList(image) == (Image *) NULL)
7546       {
7547         if (logging != MagickFalse)
7548           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7549
7550         (void) ThrowMagickException(exception,GetMagickModule(),
7551           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7552           image_info->filename);
7553       }
7554   }
7555
7556   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7557              GetNextImageInList(image) ==
7558      (Image *) NULL)
7559     {
7560       if (logging != MagickFalse)
7561         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7562             "  First image null");
7563
7564       (void) ThrowMagickException(exception,GetMagickModule(),
7565         CoderError,"image->next for first image is NULL but shouldn't be.",
7566         "`%s'",image_info->filename);
7567     }
7568
7569   if (mng_info->image_found == 0)
7570     {
7571       if (logging != MagickFalse)
7572         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7573           "  No visible images found.");
7574
7575       (void) ThrowMagickException(exception,GetMagickModule(),
7576         CoderError,"No visible images in file","`%s'",image_info->filename);
7577
7578       return(DestroyImageList(image));
7579     }
7580
7581   if (mng_info->ticks_per_second)
7582     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7583             final_delay/mng_info->ticks_per_second;
7584
7585   else
7586     image->start_loop=MagickTrue;
7587
7588   /* Find final nonzero image delay */
7589   final_image_delay=0;
7590
7591   while (GetNextImageInList(image) != (Image *) NULL)
7592     {
7593       if (image->delay)
7594         final_image_delay=image->delay;
7595
7596       image=GetNextImageInList(image);
7597     }
7598
7599   if (final_delay < final_image_delay)
7600     final_delay=final_image_delay;
7601
7602   image->delay=final_delay;
7603
7604   if (logging != MagickFalse)
7605       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7606         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7607         (double) final_delay);
7608
7609   if (logging != MagickFalse)
7610     {
7611       int
7612         scene;
7613
7614       scene=0;
7615       image=GetFirstImageInList(image);
7616
7617       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7618         "  Before coalesce:");
7619
7620       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7621         "    scene 0 delay=%.20g",(double) image->delay);
7622
7623       while (GetNextImageInList(image) != (Image *) NULL)
7624       {
7625         image=GetNextImageInList(image);
7626         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7627           "    scene %.20g delay=%.20g",(double) scene++,
7628           (double) image->delay);
7629       }
7630     }
7631
7632   image=GetFirstImageInList(image);
7633 #ifdef MNG_COALESCE_LAYERS
7634   if (insert_layers)
7635     {
7636       Image
7637         *next_image,
7638         *next;
7639
7640       size_t
7641         scene;
7642
7643       if (logging != MagickFalse)
7644         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7645           "  Coalesce Images");
7646
7647       scene=image->scene;
7648       next_image=CoalesceImages(image,exception);
7649
7650       if (next_image == (Image *) NULL)
7651         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7652
7653       image=DestroyImageList(image);
7654       image=next_image;
7655
7656       for (next=image; next != (Image *) NULL; next=next_image)
7657       {
7658          next->page.width=mng_info->mng_width;
7659          next->page.height=mng_info->mng_height;
7660          next->page.x=0;
7661          next->page.y=0;
7662          next->scene=scene++;
7663          next_image=GetNextImageInList(next);
7664
7665          if (next_image == (Image *) NULL)
7666            break;
7667
7668          if (next->delay == 0)
7669            {
7670              scene--;
7671              next_image->previous=GetPreviousImageInList(next);
7672              if (GetPreviousImageInList(next) == (Image *) NULL)
7673                image=next_image;
7674              else
7675                next->previous->next=next_image;
7676              next=DestroyImage(next);
7677            }
7678       }
7679     }
7680 #endif
7681
7682   while (GetNextImageInList(image) != (Image *) NULL)
7683       image=GetNextImageInList(image);
7684
7685   image->dispose=BackgroundDispose;
7686
7687   if (logging != MagickFalse)
7688     {
7689       int
7690         scene;
7691
7692       scene=0;
7693       image=GetFirstImageInList(image);
7694
7695       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7696         "  After coalesce:");
7697
7698       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7699         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7700         (double) image->dispose);
7701
7702       while (GetNextImageInList(image) != (Image *) NULL)
7703       {
7704         image=GetNextImageInList(image);
7705
7706         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7707           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7708           (double) image->delay,(double) image->dispose);
7709       }
7710    }
7711
7712   if (logging != MagickFalse)
7713     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7714       "  exit ReadOneMNGImage();");
7715
7716   return(image);
7717 }
7718
7719 static Image *ReadMNGImage(const ImageInfo *image_info,
7720      ExceptionInfo *exception)
7721 {
7722   Image
7723     *image;
7724
7725   MagickBooleanType
7726     logging,
7727     status;
7728
7729   MngInfo
7730     *mng_info;
7731
7732   /* Open image file.  */
7733
7734   assert(image_info != (const ImageInfo *) NULL);
7735   assert(image_info->signature == MagickCoreSignature);
7736   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
7737      image_info->filename);
7738   assert(exception != (ExceptionInfo *) NULL);
7739   assert(exception->signature == MagickCoreSignature);
7740   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
7741   image=AcquireImage(image_info,exception);
7742   mng_info=(MngInfo *) NULL;
7743   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
7744
7745   if (status == MagickFalse)
7746     return(DestroyImageList(image));
7747
7748   /* Allocate a MngInfo structure.  */
7749
7750   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7751
7752   if (mng_info == (MngInfo *) NULL)
7753     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7754
7755   /* Initialize members of the MngInfo structure.  */
7756
7757   (void) memset(mng_info,0,sizeof(MngInfo));
7758   mng_info->image=image;
7759   image=ReadOneMNGImage(mng_info,image_info,exception);
7760   mng_info=MngInfoFreeStruct(mng_info);
7761
7762   if (image == (Image *) NULL)
7763     {
7764       if (logging != MagickFalse)
7765         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7766           "exit ReadMNGImage() with error");
7767
7768       return((Image *) NULL);
7769     }
7770   (void) CloseBlob(image);
7771
7772   if (logging != MagickFalse)
7773     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7774
7775   return(GetFirstImageInList(image));
7776 }
7777 #else /* PNG_LIBPNG_VER > 10011 */
7778 static Image *ReadPNGImage(const ImageInfo *image_info,
7779    ExceptionInfo *exception)
7780 {
7781   printf("Your PNG library is too old: You have libpng-%s\n",
7782      PNG_LIBPNG_VER_STRING);
7783
7784   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7785     "PNG library is too old","`%s'",image_info->filename);
7786
7787   return(Image *) NULL;
7788 }
7789
7790 static Image *ReadMNGImage(const ImageInfo *image_info,
7791    ExceptionInfo *exception)
7792 {
7793   return(ReadPNGImage(image_info,exception));
7794 }
7795 #endif /* PNG_LIBPNG_VER > 10011 */
7796 #endif
7797 \f
7798 /*
7799 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7800 %                                                                             %
7801 %                                                                             %
7802 %                                                                             %
7803 %   R e g i s t e r P N G I m a g e                                           %
7804 %                                                                             %
7805 %                                                                             %
7806 %                                                                             %
7807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7808 %
7809 %  RegisterPNGImage() adds properties for the PNG image format to
7810 %  the list of supported formats.  The properties include the image format
7811 %  tag, a method to read and/or write the format, whether the format
7812 %  supports the saving of more than one frame to the same file or blob,
7813 %  whether the format supports native in-memory I/O, and a brief
7814 %  description of the format.
7815 %
7816 %  The format of the RegisterPNGImage method is:
7817 %
7818 %      size_t RegisterPNGImage(void)
7819 %
7820 */
7821 ModuleExport size_t RegisterPNGImage(void)
7822 {
7823   char
7824     version[MagickPathExtent];
7825
7826   MagickInfo
7827     *entry;
7828
7829   static const char
7830     *PNGNote=
7831     {
7832       "See http://www.libpng.org/ for details about the PNG format."
7833     },
7834
7835     *JNGNote=
7836     {
7837       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7838       "format."
7839     },
7840
7841     *MNGNote=
7842     {
7843       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7844       "format."
7845     };
7846
7847   *version='\0';
7848
7849 #if defined(PNG_LIBPNG_VER_STRING)
7850   (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7851   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
7852    MagickPathExtent);
7853
7854   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7855     {
7856       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7857       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7858             MagickPathExtent);
7859     }
7860 #endif
7861
7862   entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7863   entry->flags|=CoderDecoderSeekableStreamFlag;
7864
7865 #if defined(MAGICKCORE_PNG_DELEGATE)
7866   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7867   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7868 #endif
7869
7870   entry->magick=(IsImageFormatHandler *) IsMNG;
7871
7872   if (*version != '\0')
7873     entry->version=ConstantString(version);
7874
7875   entry->mime_type=ConstantString("video/x-mng");
7876   entry->note=ConstantString(MNGNote);
7877   (void) RegisterMagickInfo(entry);
7878
7879   entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7880
7881 #if defined(MAGICKCORE_PNG_DELEGATE)
7882   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7883   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7884 #endif
7885
7886   entry->magick=(IsImageFormatHandler *) IsPNG;
7887   entry->flags|=CoderDecoderSeekableStreamFlag;
7888   entry->flags^=CoderAdjoinFlag;
7889   entry->mime_type=ConstantString("image/png");
7890
7891   if (*version != '\0')
7892     entry->version=ConstantString(version);
7893
7894   entry->note=ConstantString(PNGNote);
7895   (void) RegisterMagickInfo(entry);
7896
7897   entry=AcquireMagickInfo("PNG","PNG8",
7898     "8-bit indexed with optional binary transparency");
7899
7900 #if defined(MAGICKCORE_PNG_DELEGATE)
7901   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7902   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7903 #endif
7904
7905   entry->magick=(IsImageFormatHandler *) IsPNG;
7906   entry->flags|=CoderDecoderSeekableStreamFlag;
7907   entry->flags^=CoderAdjoinFlag;
7908   entry->mime_type=ConstantString("image/png");
7909   (void) RegisterMagickInfo(entry);
7910
7911   entry=AcquireMagickInfo("PNG","PNG24",
7912     "opaque or binary transparent 24-bit RGB");
7913   *version='\0';
7914
7915 #if defined(ZLIB_VERSION)
7916   (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7917   (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7918
7919   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7920     {
7921       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7922       (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7923     }
7924 #endif
7925
7926   if (*version != '\0')
7927     entry->version=ConstantString(version);
7928
7929 #if defined(MAGICKCORE_PNG_DELEGATE)
7930   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7931   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7932 #endif
7933
7934   entry->magick=(IsImageFormatHandler *) IsPNG;
7935   entry->flags|=CoderDecoderSeekableStreamFlag;
7936   entry->flags^=CoderAdjoinFlag;
7937   entry->mime_type=ConstantString("image/png");
7938   (void) RegisterMagickInfo(entry);
7939
7940   entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7941
7942 #if defined(MAGICKCORE_PNG_DELEGATE)
7943   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7944   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7945 #endif
7946
7947   entry->magick=(IsImageFormatHandler *) IsPNG;
7948   entry->flags|=CoderDecoderSeekableStreamFlag;
7949   entry->flags^=CoderAdjoinFlag;
7950   entry->mime_type=ConstantString("image/png");
7951   (void) RegisterMagickInfo(entry);
7952
7953   entry=AcquireMagickInfo("PNG","PNG48",
7954     "opaque or binary transparent 48-bit RGB");
7955
7956 #if defined(MAGICKCORE_PNG_DELEGATE)
7957   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7958   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7959 #endif
7960
7961   entry->magick=(IsImageFormatHandler *) IsPNG;
7962   entry->flags|=CoderDecoderSeekableStreamFlag;
7963   entry->flags^=CoderAdjoinFlag;
7964   entry->mime_type=ConstantString("image/png");
7965   (void) RegisterMagickInfo(entry);
7966
7967   entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7968
7969 #if defined(MAGICKCORE_PNG_DELEGATE)
7970   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7971   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7972 #endif
7973
7974   entry->magick=(IsImageFormatHandler *) IsPNG;
7975   entry->flags|=CoderDecoderSeekableStreamFlag;
7976   entry->flags^=CoderAdjoinFlag;
7977   entry->mime_type=ConstantString("image/png");
7978   (void) RegisterMagickInfo(entry);
7979
7980   entry=AcquireMagickInfo("PNG","PNG00",
7981     "PNG inheriting bit-depth, color-type from original, if possible");
7982
7983 #if defined(MAGICKCORE_PNG_DELEGATE)
7984   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7985   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7986 #endif
7987
7988   entry->magick=(IsImageFormatHandler *) IsPNG;
7989   entry->flags|=CoderDecoderSeekableStreamFlag;
7990   entry->flags^=CoderAdjoinFlag;
7991   entry->mime_type=ConstantString("image/png");
7992   (void) RegisterMagickInfo(entry);
7993
7994   entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7995
7996 #if defined(JNG_SUPPORTED)
7997 #if defined(MAGICKCORE_PNG_DELEGATE)
7998   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7999   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
8000 #endif
8001 #endif
8002
8003   entry->magick=(IsImageFormatHandler *) IsJNG;
8004   entry->flags|=CoderDecoderSeekableStreamFlag;
8005   entry->flags^=CoderAdjoinFlag;
8006   entry->mime_type=ConstantString("image/x-jng");
8007   entry->note=ConstantString(JNGNote);
8008   (void) RegisterMagickInfo(entry);
8009
8010 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
8011   ping_semaphore=AcquireSemaphoreInfo();
8012 #endif
8013
8014   return(MagickImageCoderSignature);
8015 }
8016 \f
8017 /*
8018 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8019 %                                                                             %
8020 %                                                                             %
8021 %                                                                             %
8022 %   U n r e g i s t e r P N G I m a g e                                       %
8023 %                                                                             %
8024 %                                                                             %
8025 %                                                                             %
8026 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8027 %
8028 %  UnregisterPNGImage() removes format registrations made by the
8029 %  PNG module from the list of supported formats.
8030 %
8031 %  The format of the UnregisterPNGImage method is:
8032 %
8033 %      UnregisterPNGImage(void)
8034 %
8035 */
8036 ModuleExport void UnregisterPNGImage(void)
8037 {
8038   (void) UnregisterMagickInfo("MNG");
8039   (void) UnregisterMagickInfo("PNG");
8040   (void) UnregisterMagickInfo("PNG8");
8041   (void) UnregisterMagickInfo("PNG24");
8042   (void) UnregisterMagickInfo("PNG32");
8043   (void) UnregisterMagickInfo("PNG48");
8044   (void) UnregisterMagickInfo("PNG64");
8045   (void) UnregisterMagickInfo("PNG00");
8046   (void) UnregisterMagickInfo("JNG");
8047
8048 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
8049   if (ping_semaphore != (SemaphoreInfo *) NULL)
8050     RelinquishSemaphoreInfo(&ping_semaphore);
8051 #endif
8052 }
8053 \f
8054 #if defined(MAGICKCORE_PNG_DELEGATE)
8055 #if PNG_LIBPNG_VER > 10011
8056 /*
8057 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8058 %                                                                             %
8059 %                                                                             %
8060 %                                                                             %
8061 %   W r i t e M N G I m a g e                                                 %
8062 %                                                                             %
8063 %                                                                             %
8064 %                                                                             %
8065 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8066 %
8067 %  WriteMNGImage() writes an image in the Portable Network Graphics
8068 %  Group's "Multiple-image Network Graphics" encoded image format.
8069 %
8070 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
8071 %
8072 %  The format of the WriteMNGImage method is:
8073 %
8074 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
8075 %        Image *image,ExceptionInfo *exception)
8076 %
8077 %  A description of each parameter follows.
8078 %
8079 %    o image_info: the image info.
8080 %
8081 %    o image:  The image.
8082 %
8083 %    o exception: return any errors or warnings in this structure.
8084 %
8085 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
8086 %    "To do" under ReadPNGImage):
8087 %
8088 %    Preserve all unknown and not-yet-handled known chunks found in input
8089 %    PNG file and copy them  into output PNG files according to the PNG
8090 %    copying rules.
8091 %
8092 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
8093 %
8094 %    Improve selection of color type (use indexed-colour or indexed-colour
8095 %    with tRNS when 256 or fewer unique RGBA values are present).
8096 %
8097 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
8098 %    This will be complicated if we limit ourselves to generating MNG-LC
8099 %    files.  For now we ignore disposal method 3 and simply overlay the next
8100 %    image on it.
8101 %
8102 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
8103 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
8104 %    [mostly done 15 June 1999 but still need to take care of tRNS]
8105 %
8106 %    Check for identical sRGB and replace with a global sRGB (and remove
8107 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
8108 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
8109 %    local gAMA/cHRM with local sRGB if appropriate).
8110 %
8111 %    Check for identical sBIT chunks and write global ones.
8112 %
8113 %    Provide option to skip writing the signature tEXt chunks.
8114 %
8115 %    Use signatures to detect identical objects and reuse the first
8116 %    instance of such objects instead of writing duplicate objects.
8117 %
8118 %    Use a smaller-than-32k value of compression window size when
8119 %    appropriate.
8120 %
8121 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
8122 %    ancillary text chunks and save profiles.
8123 %
8124 %    Provide an option to force LC files (to ensure exact framing rate)
8125 %    instead of VLC.
8126 %
8127 %    Provide an option to force VLC files instead of LC, even when offsets
8128 %    are present.  This will involve expanding the embedded images with a
8129 %    transparent region at the top and/or left.
8130 */
8131
8132 static void
8133 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
8134    png_info *ping_info, unsigned char *profile_type, unsigned char
8135    *profile_description, unsigned char *profile_data, png_uint_32 length)
8136 {
8137    png_textp
8138      text;
8139
8140    register ssize_t
8141      i;
8142
8143    unsigned char
8144      *sp;
8145
8146    png_charp
8147      dp;
8148
8149    png_uint_32
8150      allocated_length,
8151      description_length;
8152
8153    unsigned char
8154      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
8155
8156    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
8157       return;
8158
8159    if (image_info->verbose)
8160      {
8161        (void) printf("writing raw profile: type=%s, length=%.20g\n",
8162          (char *) profile_type, (double) length);
8163      }
8164
8165 #if PNG_LIBPNG_VER >= 10400
8166    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
8167 #else
8168    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
8169 #endif
8170    description_length=(png_uint_32) strlen((const char *) profile_description);
8171    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
8172       + description_length);
8173 #if PNG_LIBPNG_VER >= 10400
8174    text[0].text=(png_charp) png_malloc(ping,
8175       (png_alloc_size_t) allocated_length);
8176    text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
8177 #else
8178    text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
8179    text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
8180 #endif
8181    text[0].key[0]='\0';
8182    (void) ConcatenateMagickString(text[0].key,
8183       "Raw profile type ",MagickPathExtent);
8184    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
8185    sp=profile_data;
8186    dp=text[0].text;
8187    *dp++='\n';
8188    (void) CopyMagickString(dp,(const char *) profile_description,
8189      allocated_length);
8190    dp+=description_length;
8191    *dp++='\n';
8192    (void) FormatLocaleString(dp,allocated_length-
8193      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
8194    dp+=8;
8195
8196    for (i=0; i < (ssize_t) length; i++)
8197    {
8198      if (i%36 == 0)
8199        *dp++='\n';
8200      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
8201      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
8202    }
8203
8204    *dp++='\n';
8205    *dp='\0';
8206    text[0].text_length=(png_size_t) (dp-text[0].text);
8207    text[0].compression=image_info->compression == NoCompression ||
8208      (image_info->compression == UndefinedCompression &&
8209      text[0].text_length < 128) ? -1 : 0;
8210
8211    if (text[0].text_length <= allocated_length)
8212      png_set_text(ping,ping_info,text,1);
8213
8214    png_free(ping,text[0].text);
8215    png_free(ping,text[0].key);
8216    png_free(ping,text);
8217 }
8218
8219 static inline MagickBooleanType IsColorEqual(const Image *image,
8220   const Quantum *p, const PixelInfo *q)
8221 {
8222   MagickRealType
8223     blue,
8224     green,
8225     red;
8226
8227   red=(MagickRealType) GetPixelRed(image,p);
8228   green=(MagickRealType) GetPixelGreen(image,p);
8229   blue=(MagickRealType) GetPixelBlue(image,p);
8230   if ((AbsolutePixelValue(red-q->red) < MagickEpsilon) &&
8231       (AbsolutePixelValue(green-q->green) < MagickEpsilon) &&
8232       (AbsolutePixelValue(blue-q->blue) < MagickEpsilon))
8233     return(MagickTrue);
8234   return(MagickFalse);
8235 }
8236
8237 #if defined(PNG_tIME_SUPPORTED)
8238 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
8239   const char *timestamp,ExceptionInfo *exception)
8240 {
8241   int
8242     ret;
8243
8244   int
8245     day,
8246     hour,
8247     minute,
8248     month,
8249     second,
8250     year;
8251
8252   int
8253     addhours=0,
8254     addminutes=0;
8255
8256   png_time
8257     ptime;
8258
8259   assert(timestamp != (const char *) NULL);
8260   LogMagickEvent(CoderEvent,GetMagickModule(),
8261       "  Writing tIME chunk: timestamp property is %30s\n",timestamp);
8262   ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d",&year,&month,&day,&hour,
8263       &minute, &second);
8264   addhours=0;
8265   addminutes=0;
8266   ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d%d:%d",&year,&month,&day,&hour,
8267       &minute, &second, &addhours, &addminutes);
8268     LogMagickEvent(CoderEvent,GetMagickModule(),
8269       "   Date format specified for png:tIME=%s" ,timestamp);
8270     LogMagickEvent(CoderEvent,GetMagickModule(),
8271       "      ret=%d,y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, as=%d",
8272       ret,year,month,day,hour,minute,second,addhours,addminutes);
8273   if (ret < 6)
8274   {
8275     LogMagickEvent(CoderEvent,GetMagickModule(),
8276       "      Invalid date, ret=%d",ret);
8277     (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8278       "Invalid date format specified for png:tIME","`%s' (ret=%d)",
8279       image->filename,ret);
8280     return;
8281   }
8282   if (addhours < 0)
8283   {
8284     addhours+=24;
8285     addminutes=-addminutes;
8286     day--;
8287   }
8288   hour+=addhours;
8289   minute+=addminutes;
8290   if (day == 0)
8291   {
8292     month--;
8293     day=31;
8294     if(month == 2)
8295       day=28;
8296     else
8297     {
8298       if(month == 4 || month == 6 || month == 9 || month == 11)
8299         day=30;
8300       else
8301         day=31;
8302     }
8303   }
8304   if (month == 0)
8305   {
8306     month++;
8307     year--;
8308   }
8309   if (minute > 59)
8310   {
8311      hour++;
8312      minute-=60;
8313   }
8314   if (hour > 23)
8315   {
8316      day ++;
8317      hour -=24;
8318   }
8319   if (hour < 0)
8320   {
8321      day --;
8322      hour +=24;
8323   }
8324   /* To do: fix this for leap years */
8325   if (day > 31 || (month == 2 && day > 28) || ((month == 4 || month == 6 ||
8326       month == 9 || month == 11) && day > 30))
8327   {
8328      month++;
8329      day = 1;
8330   }
8331   if (month > 12)
8332   {
8333      year++;
8334      month=1;
8335   }
8336
8337   ptime.year = year;
8338   ptime.month = month;
8339   ptime.day = day;
8340   ptime.hour = hour;
8341   ptime.minute = minute;
8342   ptime.second = second;
8343
8344   LogMagickEvent(CoderEvent,GetMagickModule(),
8345       "      png_set_tIME: y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, am=%d",
8346       ptime.year, ptime.month, ptime.day, ptime.hour, ptime.minute,
8347       ptime.second, addhours, addminutes);
8348   png_set_tIME(ping,info,&ptime);
8349 }
8350 #endif
8351
8352 /* Write one PNG image */
8353 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
8354   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
8355 {
8356   char
8357     im_vers[32],
8358     libpng_runv[32],
8359     libpng_vers[32],
8360     zlib_runv[32],
8361     zlib_vers[32];
8362
8363   Image
8364     *image;
8365
8366   ImageInfo
8367     *image_info;
8368
8369   char
8370     *name,
8371     s[2];
8372
8373   const char
8374     *property,
8375     *value;
8376
8377   const StringInfo
8378     *profile;
8379
8380   int
8381     num_passes,
8382     pass,
8383     ping_wrote_caNv;
8384
8385   png_byte
8386      ping_trans_alpha[256];
8387
8388   png_color
8389      palette[257];
8390
8391   png_color_16
8392     ping_background,
8393     ping_trans_color;
8394
8395   png_info
8396     *ping_info;
8397
8398   png_struct
8399     *ping;
8400
8401   png_uint_32
8402     ping_height,
8403     ping_width;
8404
8405   ssize_t
8406     y;
8407
8408   MagickBooleanType
8409     image_matte,
8410     logging,
8411     matte,
8412
8413     ping_have_blob,
8414     ping_have_cheap_transparency,
8415     ping_have_color,
8416     ping_have_non_bw,
8417     ping_have_PLTE,
8418     ping_have_bKGD,
8419     ping_have_eXIf,
8420     ping_have_iCCP,
8421     ping_have_pHYs,
8422     ping_have_sRGB,
8423     ping_have_tRNS,
8424
8425     ping_exclude_bKGD,
8426     ping_exclude_cHRM,
8427     ping_exclude_date,
8428     /* ping_exclude_EXIF, */
8429     ping_exclude_eXIf,
8430     ping_exclude_gAMA,
8431     ping_exclude_iCCP,
8432     /* ping_exclude_iTXt, */
8433     ping_exclude_oFFs,
8434     ping_exclude_pHYs,
8435     ping_exclude_sRGB,
8436     ping_exclude_tEXt,
8437     ping_exclude_tIME,
8438     /* ping_exclude_tRNS, */
8439     ping_exclude_caNv,
8440     ping_exclude_zCCP, /* hex-encoded iCCP */
8441     ping_exclude_zTXt,
8442
8443     ping_preserve_colormap,
8444     ping_preserve_iCCP,
8445     ping_need_colortype_warning,
8446
8447     status,
8448     tried_332,
8449     tried_333,
8450     tried_444;
8451
8452   MemoryInfo
8453     *volatile pixel_info;
8454
8455   QuantumInfo
8456     *quantum_info;
8457
8458   PNGErrorInfo
8459     error_info;
8460
8461   register ssize_t
8462     i,
8463     x;
8464
8465   unsigned char
8466     *ping_pixels;
8467
8468   volatile int
8469     image_colors,
8470     ping_bit_depth,
8471     ping_color_type,
8472     ping_interlace_method,
8473     ping_compression_method,
8474     ping_filter_method,
8475     ping_num_trans;
8476
8477   volatile size_t
8478     image_depth,
8479     old_bit_depth;
8480
8481   size_t
8482     quality,
8483     rowbytes,
8484     save_image_depth;
8485
8486   int
8487     j,
8488     number_colors,
8489     number_opaque,
8490     number_semitransparent,
8491     number_transparent,
8492     ping_pHYs_unit_type;
8493
8494   png_uint_32
8495     ping_pHYs_x_resolution,
8496     ping_pHYs_y_resolution;
8497
8498   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8499     "  Enter WriteOnePNGImage()");
8500
8501   image = CloneImage(IMimage,0,0,MagickFalse,exception);
8502   if (image == (Image *) NULL)
8503     return(MagickFalse);
8504   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8505
8506   /* Define these outside of the following "if logging()" block so they will
8507    * show in debuggers.
8508    */
8509   *im_vers='\0';
8510   (void) ConcatenateMagickString(im_vers,
8511          MagickLibVersionText,MagickPathExtent);
8512   (void) ConcatenateMagickString(im_vers,
8513          MagickLibAddendum,MagickPathExtent);
8514
8515   *libpng_vers='\0';
8516   (void) ConcatenateMagickString(libpng_vers,
8517          PNG_LIBPNG_VER_STRING,32);
8518   *libpng_runv='\0';
8519   (void) ConcatenateMagickString(libpng_runv,
8520          png_get_libpng_ver(NULL),32);
8521
8522   *zlib_vers='\0';
8523   (void) ConcatenateMagickString(zlib_vers,
8524          ZLIB_VERSION,32);
8525   *zlib_runv='\0';
8526   (void) ConcatenateMagickString(zlib_runv,
8527          zlib_version,32);
8528
8529   if (logging != MagickFalse)
8530     {
8531        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8532           "    IM version     = %s", im_vers);
8533        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8534           "    Libpng version = %s", libpng_vers);
8535        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8536        {
8537        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8538           "      running with   %s", libpng_runv);
8539        }
8540        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8541           "    Zlib version   = %s", zlib_vers);
8542        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8543        {
8544        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8545           "      running with   %s", zlib_runv);
8546        }
8547     }
8548
8549   /* Initialize some stuff */
8550   ping_bit_depth=0,
8551   ping_color_type=0,
8552   ping_interlace_method=0,
8553   ping_compression_method=0,
8554   ping_filter_method=0,
8555   ping_num_trans = 0;
8556
8557   ping_background.red = 0;
8558   ping_background.green = 0;
8559   ping_background.blue = 0;
8560   ping_background.gray = 0;
8561   ping_background.index = 0;
8562
8563   ping_trans_color.red=0;
8564   ping_trans_color.green=0;
8565   ping_trans_color.blue=0;
8566   ping_trans_color.gray=0;
8567
8568   ping_pHYs_unit_type = 0;
8569   ping_pHYs_x_resolution = 0;
8570   ping_pHYs_y_resolution = 0;
8571
8572   ping_have_blob=MagickFalse;
8573   ping_have_cheap_transparency=MagickFalse;
8574   ping_have_color=MagickTrue;
8575   ping_have_non_bw=MagickTrue;
8576   ping_have_PLTE=MagickFalse;
8577   ping_have_bKGD=MagickFalse;
8578   ping_have_eXIf=MagickTrue;
8579   ping_have_iCCP=MagickFalse;
8580   ping_have_pHYs=MagickFalse;
8581   ping_have_sRGB=MagickFalse;
8582   ping_have_tRNS=MagickFalse;
8583
8584   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8585   ping_exclude_caNv=mng_info->ping_exclude_caNv;
8586   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8587   ping_exclude_date=mng_info->ping_exclude_date;
8588   ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
8589   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8590   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8591   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8592   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8593   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8594   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8595   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8596   ping_exclude_tIME=mng_info->ping_exclude_tIME;
8597   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8598   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8599   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8600
8601   ping_preserve_colormap = mng_info->ping_preserve_colormap;
8602   ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8603   ping_need_colortype_warning = MagickFalse;
8604
8605   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8606    * i.e., eliminate the ICC profile and set image->rendering_intent.
8607    * Note that this will not involve any changes to the actual pixels
8608    * but merely passes information to applications that read the resulting
8609    * PNG image.
8610    *
8611    * To do: recognize other variants of the sRGB profile, using the CRC to
8612    * verify all recognized variants including the 7 already known.
8613    *
8614    * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8615    *
8616    * Use something other than image->rendering_intent to record the fact
8617    * that the sRGB profile was found.
8618    *
8619    * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8620    * profile.  Record the Blackpoint Compensation, if any.
8621    */
8622    if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8623    {
8624       ResetImageProfileIterator(image);
8625       for (name=GetNextImageProfile(image); name != (char *) NULL; )
8626       {
8627         profile=GetImageProfile(image,name);
8628
8629         if (profile != (StringInfo *) NULL)
8630           {
8631             if ((LocaleCompare(name,"ICC") == 0) ||
8632                 (LocaleCompare(name,"ICM") == 0))
8633
8634              {
8635                  int
8636                    icheck,
8637                    got_crc=0;
8638
8639
8640                  png_uint_32
8641                    length,
8642                    profile_crc=0;
8643
8644                  unsigned char
8645                    *data;
8646
8647                  length=(png_uint_32) GetStringInfoLength(profile);
8648
8649                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8650                  {
8651                    if (length == sRGB_info[icheck].len)
8652                    {
8653                      if (got_crc == 0)
8654                      {
8655                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8656                          "    Got a %lu-byte ICC profile (potentially sRGB)",
8657                          (unsigned long) length);
8658
8659                        data=GetStringInfoDatum(profile);
8660                        profile_crc=crc32(0,data,length);
8661
8662                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8663                            "      with crc=%8x",(unsigned int) profile_crc);
8664                        got_crc++;
8665                      }
8666
8667                      if (profile_crc == sRGB_info[icheck].crc)
8668                      {
8669                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8670                             "      It is sRGB with rendering intent = %s",
8671                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
8672                              sRGB_info[icheck].intent));
8673                         if (image->rendering_intent==UndefinedIntent)
8674                         {
8675                           image->rendering_intent=
8676                           Magick_RenderingIntent_from_PNG_RenderingIntent(
8677                              sRGB_info[icheck].intent);
8678                         }
8679                         ping_exclude_iCCP = MagickTrue;
8680                         ping_exclude_zCCP = MagickTrue;
8681                         ping_have_sRGB = MagickTrue;
8682                         break;
8683                      }
8684                    }
8685                  }
8686                  if (sRGB_info[icheck].len == 0)
8687                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8688                         "    Got %lu-byte ICC profile not recognized as sRGB",
8689                         (unsigned long) length);
8690               }
8691           }
8692         name=GetNextImageProfile(image);
8693       }
8694   }
8695
8696   number_opaque = 0;
8697   number_semitransparent = 0;
8698   number_transparent = 0;
8699
8700   if (logging != MagickFalse)
8701     {
8702       if (image->storage_class == UndefinedClass)
8703           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8704           "    image->storage_class=UndefinedClass");
8705       if (image->storage_class == DirectClass)
8706           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8707           "    image->storage_class=DirectClass");
8708       if (image->storage_class == PseudoClass)
8709           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8710           "    image->storage_class=PseudoClass");
8711       (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8712           "    image->taint=MagickTrue":
8713           "    image->taint=MagickFalse");
8714       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8715           "    image->gamma=%g", image->gamma);
8716     }
8717
8718   if (image->storage_class == PseudoClass &&
8719      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8720      mng_info->write_png48 || mng_info->write_png64 ||
8721      (mng_info->write_png_colortype != 1 &&
8722      mng_info->write_png_colortype != 5)))
8723     {
8724       (void) SyncImage(image,exception);
8725       image->storage_class = DirectClass;
8726     }
8727
8728   if (ping_preserve_colormap == MagickFalse)
8729     {
8730       if (image->storage_class != PseudoClass && image->colormap != NULL)
8731         {
8732           /* Free the bogus colormap; it can cause trouble later */
8733            if (logging != MagickFalse)
8734               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8735               "    Freeing bogus colormap");
8736            (void) RelinquishMagickMemory(image->colormap);
8737            image->colormap=NULL;
8738         }
8739     }
8740
8741   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8742     (void) TransformImageColorspace(image,sRGBColorspace,exception);
8743
8744   /*
8745     Sometimes we get PseudoClass images whose RGB values don't match
8746     the colors in the colormap.  This code syncs the RGB values.
8747   */
8748   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8749      (void) SyncImage(image,exception);
8750
8751 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8752   if (image->depth > 8)
8753     {
8754       if (logging != MagickFalse)
8755         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8756           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8757
8758       image->depth=8;
8759     }
8760 #endif
8761
8762   /* Respect the -depth option */
8763   if (image->depth < 4)
8764     {
8765        register Quantum
8766          *r;
8767
8768        if (image->depth > 2)
8769          {
8770            /* Scale to 4-bit */
8771            LBR04PacketRGBA(image->background_color);
8772
8773            for (y=0; y < (ssize_t) image->rows; y++)
8774            {
8775              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8776
8777              if (r == (Quantum *) NULL)
8778                break;
8779
8780              for (x=0; x < (ssize_t) image->columns; x++)
8781              {
8782                 LBR04PixelRGBA(r);
8783                 r+=GetPixelChannels(image);
8784              }
8785
8786              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8787                 break;
8788            }
8789
8790            if (image->storage_class == PseudoClass && image->colormap != NULL)
8791            {
8792              for (i=0; i < (ssize_t) image->colors; i++)
8793              {
8794                LBR04PacketRGBA(image->colormap[i]);
8795              }
8796            }
8797          }
8798        else if (image->depth > 1)
8799          {
8800            /* Scale to 2-bit */
8801            LBR02PacketRGBA(image->background_color);
8802
8803            for (y=0; y < (ssize_t) image->rows; y++)
8804            {
8805              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8806
8807              if (r == (Quantum *) NULL)
8808                break;
8809
8810              for (x=0; x < (ssize_t) image->columns; x++)
8811              {
8812                 LBR02PixelRGBA(r);
8813                 r+=GetPixelChannels(image);
8814              }
8815
8816              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8817                 break;
8818            }
8819
8820            if (image->storage_class == PseudoClass && image->colormap != NULL)
8821            {
8822              for (i=0; i < (ssize_t) image->colors; i++)
8823              {
8824                LBR02PacketRGBA(image->colormap[i]);
8825              }
8826            }
8827          }
8828        else
8829          {
8830            /* Scale to 1-bit */
8831            LBR01PacketRGBA(image->background_color);
8832
8833            for (y=0; y < (ssize_t) image->rows; y++)
8834            {
8835              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8836
8837              if (r == (Quantum *) NULL)
8838                break;
8839
8840              for (x=0; x < (ssize_t) image->columns; x++)
8841              {
8842                 LBR01PixelRGBA(r);
8843                 r+=GetPixelChannels(image);
8844              }
8845
8846              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8847                 break;
8848            }
8849
8850            if (image->storage_class == PseudoClass && image->colormap != NULL)
8851            {
8852              for (i=0; i < (ssize_t) image->colors; i++)
8853              {
8854                LBR01PacketRGBA(image->colormap[i]);
8855              }
8856            }
8857          }
8858     }
8859
8860   /* To do: set to next higher multiple of 8 */
8861   if (image->depth < 8)
8862      image->depth=8;
8863
8864 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8865   /* PNG does not handle depths greater than 16 so reduce it even
8866    * if lossy
8867    */
8868   if (image->depth > 8)
8869       image->depth=16;
8870 #endif
8871
8872 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8873   if (image->depth > 8)
8874     {
8875       /* To do: fill low byte properly */
8876       image->depth=16;
8877     }
8878
8879   if (image->depth == 16 && mng_info->write_png_depth != 16)
8880     if (mng_info->write_png8 ||
8881         LosslessReduceDepthOK(image,exception) != MagickFalse)
8882       image->depth = 8;
8883 #endif
8884
8885   image_colors = (int) image->colors;
8886   number_opaque = (int) image->colors;
8887   number_transparent = 0;
8888   number_semitransparent = 0;
8889
8890   if (mng_info->write_png_colortype &&
8891      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8892      mng_info->write_png_colortype < 4 &&
8893      image->alpha_trait == UndefinedPixelTrait)))
8894   {
8895      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8896       * are not going to need the result.
8897       */
8898      if (mng_info->write_png_colortype == 1 ||
8899         mng_info->write_png_colortype == 5)
8900        ping_have_color=MagickFalse;
8901
8902      if (image->alpha_trait != UndefinedPixelTrait)
8903        {
8904          number_transparent = 2;
8905          number_semitransparent = 1;
8906        }
8907   }
8908
8909   if (mng_info->write_png_colortype < 7)
8910   {
8911   /* BUILD_PALETTE
8912    *
8913    * Normally we run this just once, but in the case of writing PNG8
8914    * we reduce the transparency to binary and run again, then if there
8915    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8916    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8917    * palette.  Then (To do) we take care of a final reduction that is only
8918    * needed if there are still 256 colors present and one of them has both
8919    * transparent and opaque instances.
8920    */
8921
8922   tried_332 = MagickFalse;
8923   tried_333 = MagickFalse;
8924   tried_444 = MagickFalse;
8925
8926   image->depth=GetImageDepth(image,exception);
8927   for (j=0; j<6; j++)
8928   {
8929     /*
8930      * Sometimes we get DirectClass images that have 256 colors or fewer.
8931      * This code will build a colormap.
8932      *
8933      * Also, sometimes we get PseudoClass images with an out-of-date
8934      * colormap.  This code will replace the colormap with a new one.
8935      * Sometimes we get PseudoClass images that have more than 256 colors.
8936      * This code will delete the colormap and change the image to
8937      * DirectClass.
8938      *
8939      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8940      * even though it sometimes contains left-over non-opaque values.
8941      *
8942      * Also we gather some information (number of opaque, transparent,
8943      * and semitransparent pixels, and whether the image has any non-gray
8944      * pixels or only black-and-white pixels) that we might need later.
8945      *
8946      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8947      * we need to check for bogus non-opaque values, at least.
8948      */
8949
8950    int
8951      n;
8952
8953    PixelInfo
8954      opaque[260],
8955      semitransparent[260],
8956      transparent[260];
8957
8958    register const Quantum
8959      *r;
8960
8961    register Quantum
8962      *q;
8963
8964    if (logging != MagickFalse)
8965      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8966          "    Enter BUILD_PALETTE:");
8967
8968    if (logging != MagickFalse)
8969      {
8970        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8971              "      image->columns=%.20g",(double) image->columns);
8972        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8973              "      image->rows=%.20g",(double) image->rows);
8974        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8975              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
8976        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8977              "      image->depth=%.20g",(double) image->depth);
8978
8979        if (image->storage_class == PseudoClass && image->colormap != NULL)
8980        {
8981          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8982              "      Original colormap:");
8983          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8984              "        i    (red,green,blue,alpha)");
8985
8986          for (i=0; i < 256; i++)
8987          {
8988                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8989                    "        %d    (%d,%d,%d,%d)",
8990                     (int) i,
8991                     (int) image->colormap[i].red,
8992                     (int) image->colormap[i].green,
8993                     (int) image->colormap[i].blue,
8994                     (int) image->colormap[i].alpha);
8995          }
8996
8997          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8998          {
8999            if (i > 255)
9000              {
9001                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9002                    "        %d    (%d,%d,%d,%d)",
9003                     (int) i,
9004                     (int) image->colormap[i].red,
9005                     (int) image->colormap[i].green,
9006                     (int) image->colormap[i].blue,
9007                     (int) image->colormap[i].alpha);
9008              }
9009          }
9010        }
9011
9012        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9013            "      image->colors=%d",(int) image->colors);
9014
9015        if (image->colors == 0)
9016          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9017              "        (zero means unknown)");
9018
9019        if (ping_preserve_colormap == MagickFalse)
9020          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9021               "      Regenerate the colormap");
9022      }
9023
9024      image_colors=0;
9025      number_opaque = 0;
9026      number_semitransparent = 0;
9027      number_transparent = 0;
9028
9029      for (y=0; y < (ssize_t) image->rows; y++)
9030      {
9031        r=GetVirtualPixels(image,0,y,image->columns,1,exception);
9032
9033        if (r == (const Quantum *) NULL)
9034          break;
9035
9036        for (x=0; x < (ssize_t) image->columns; x++)
9037        {
9038            if (image->alpha_trait == UndefinedPixelTrait ||
9039               GetPixelAlpha(image,r) == OpaqueAlpha)
9040              {
9041                if (number_opaque < 259)
9042                  {
9043                    if (number_opaque == 0)
9044                      {
9045                        GetPixelInfoPixel(image,r,opaque);
9046                        opaque[0].alpha=OpaqueAlpha;
9047                        number_opaque=1;
9048                      }
9049
9050                    for (i=0; i< (ssize_t) number_opaque; i++)
9051                      {
9052                        if (IsColorEqual(image,r,opaque+i))
9053                          break;
9054                      }
9055
9056                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
9057                      {
9058                        number_opaque++;
9059                        GetPixelInfoPixel(image,r,opaque+i);
9060                        opaque[i].alpha=OpaqueAlpha;
9061                      }
9062                  }
9063              }
9064            else if (GetPixelAlpha(image,r) == TransparentAlpha)
9065              {
9066                if (number_transparent < 259)
9067                  {
9068                    if (number_transparent == 0)
9069                      {
9070                        GetPixelInfoPixel(image,r,transparent);
9071                        ping_trans_color.red=(unsigned short)
9072                          GetPixelRed(image,r);
9073                        ping_trans_color.green=(unsigned short)
9074                          GetPixelGreen(image,r);
9075                        ping_trans_color.blue=(unsigned short)
9076                          GetPixelBlue(image,r);
9077                        ping_trans_color.gray=(unsigned short)
9078                          GetPixelGray(image,r);
9079                        number_transparent = 1;
9080                      }
9081
9082                    for (i=0; i< (ssize_t) number_transparent; i++)
9083                      {
9084                        if (IsColorEqual(image,r,transparent+i))
9085                          break;
9086                      }
9087
9088                    if (i ==  (ssize_t) number_transparent &&
9089                        number_transparent < 259)
9090                      {
9091                        number_transparent++;
9092                        GetPixelInfoPixel(image,r,transparent+i);
9093                      }
9094                  }
9095              }
9096            else
9097              {
9098                if (number_semitransparent < 259)
9099                  {
9100                    if (number_semitransparent == 0)
9101                      {
9102                        GetPixelInfoPixel(image,r,semitransparent);
9103                        number_semitransparent = 1;
9104                      }
9105
9106                    for (i=0; i< (ssize_t) number_semitransparent; i++)
9107                      {
9108                        if (IsColorEqual(image,r,semitransparent+i)
9109                            && GetPixelAlpha(image,r) ==
9110                            semitransparent[i].alpha)
9111                          break;
9112                      }
9113
9114                    if (i ==  (ssize_t) number_semitransparent &&
9115                        number_semitransparent < 259)
9116                      {
9117                        number_semitransparent++;
9118                        GetPixelInfoPixel(image,r,semitransparent+i);
9119                      }
9120                  }
9121              }
9122            r+=GetPixelChannels(image);
9123         }
9124      }
9125
9126      if (mng_info->write_png8 == MagickFalse &&
9127          ping_exclude_bKGD == MagickFalse)
9128        {
9129          /* Add the background color to the palette, if it
9130           * isn't already there.
9131           */
9132           if (logging != MagickFalse)
9133             {
9134               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9135                   "      Check colormap for background (%d,%d,%d)",
9136                   (int) image->background_color.red,
9137                   (int) image->background_color.green,
9138                   (int) image->background_color.blue);
9139             }
9140           for (i=0; i<number_opaque; i++)
9141           {
9142              if (opaque[i].red == image->background_color.red &&
9143                  opaque[i].green == image->background_color.green &&
9144                  opaque[i].blue == image->background_color.blue)
9145                break;
9146           }
9147           if (number_opaque < 259 && i == number_opaque)
9148             {
9149                opaque[i] = image->background_color;
9150                ping_background.index = i;
9151                number_opaque++;
9152                if (logging != MagickFalse)
9153                  {
9154                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9155                        "      background_color index is %d",(int) i);
9156                  }
9157
9158             }
9159           else if (logging != MagickFalse)
9160               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9161                   "      No room in the colormap to add background color");
9162        }
9163
9164      image_colors=number_opaque+number_transparent+number_semitransparent;
9165
9166      if (logging != MagickFalse)
9167        {
9168          if (image_colors > 256)
9169             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9170                   "      image has more than 256 colors");
9171
9172          else
9173             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9174                   "      image has %d colors",image_colors);
9175        }
9176
9177      if (ping_preserve_colormap != MagickFalse)
9178        break;
9179
9180      if (mng_info->write_png_colortype != 7) /* We won't need this info */
9181        {
9182          ping_have_color=MagickFalse;
9183          ping_have_non_bw=MagickFalse;
9184
9185          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
9186          {
9187            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9188               "incompatible colorspace");
9189            ping_have_color=MagickTrue;
9190            ping_have_non_bw=MagickTrue;
9191          }
9192
9193          if(image_colors > 256)
9194            {
9195              for (y=0; y < (ssize_t) image->rows; y++)
9196              {
9197                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9198
9199                if (q == (Quantum *) NULL)
9200                  break;
9201
9202                r=q;
9203                for (x=0; x < (ssize_t) image->columns; x++)
9204                {
9205                  if (GetPixelRed(image,r) != GetPixelGreen(image,r) ||
9206                      GetPixelRed(image,r) != GetPixelBlue(image,r))
9207                    {
9208                       ping_have_color=MagickTrue;
9209                       ping_have_non_bw=MagickTrue;
9210                       break;
9211                    }
9212                  r+=GetPixelChannels(image);
9213                }
9214
9215                if (ping_have_color != MagickFalse)
9216                  break;
9217
9218                /* Worst case is black-and-white; we are looking at every
9219                 * pixel twice.
9220                 */
9221
9222                if (ping_have_non_bw == MagickFalse)
9223                  {
9224                    r=q;
9225                    for (x=0; x < (ssize_t) image->columns; x++)
9226                    {
9227                      if (GetPixelRed(image,r) != 0 &&
9228                          GetPixelRed(image,r) != QuantumRange)
9229                        {
9230                          ping_have_non_bw=MagickTrue;
9231                          break;
9232                        }
9233                      r+=GetPixelChannels(image);
9234                    }
9235                }
9236              }
9237            }
9238        }
9239
9240      if (image_colors < 257)
9241        {
9242          PixelInfo
9243            colormap[260];
9244
9245          /*
9246           * Initialize image colormap.
9247           */
9248
9249          if (logging != MagickFalse)
9250             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9251                   "      Sort the new colormap");
9252
9253         /* Sort palette, transparent first */;
9254
9255          n = 0;
9256
9257          for (i=0; i<number_transparent; i++)
9258             colormap[n++] = transparent[i];
9259
9260          for (i=0; i<number_semitransparent; i++)
9261             colormap[n++] = semitransparent[i];
9262
9263          for (i=0; i<number_opaque; i++)
9264             colormap[n++] = opaque[i];
9265
9266          ping_background.index +=
9267            (number_transparent + number_semitransparent);
9268
9269          /* image_colors < 257; search the colormap instead of the pixels
9270           * to get ping_have_color and ping_have_non_bw
9271           */
9272          for (i=0; i<n; i++)
9273          {
9274            if (ping_have_color == MagickFalse)
9275              {
9276                 if (colormap[i].red != colormap[i].green ||
9277                     colormap[i].red != colormap[i].blue)
9278                   {
9279                      ping_have_color=MagickTrue;
9280                      ping_have_non_bw=MagickTrue;
9281                      break;
9282                   }
9283               }
9284
9285            if (ping_have_non_bw == MagickFalse)
9286              {
9287                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
9288                    ping_have_non_bw=MagickTrue;
9289              }
9290           }
9291
9292         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
9293             (number_transparent == 0 && number_semitransparent == 0)) &&
9294             (((mng_info->write_png_colortype-1) ==
9295             PNG_COLOR_TYPE_PALETTE) ||
9296             (mng_info->write_png_colortype == 0)))
9297           {
9298             if (logging != MagickFalse)
9299               {
9300                 if (n !=  (ssize_t) image_colors)
9301                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9302                    "   image_colors (%d) and n (%d)  don't match",
9303                    image_colors, n);
9304
9305                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9306                    "      AcquireImageColormap");
9307               }
9308
9309             image->colors = image_colors;
9310
9311             if (AcquireImageColormap(image,image_colors,exception) == MagickFalse)
9312               {
9313                 (void) ThrowMagickException(exception,GetMagickModule(),
9314                   ResourceLimitError,"MemoryAllocationFailed","`%s'",
9315                   image->filename);
9316                 break;
9317               }
9318
9319             for (i=0; i< (ssize_t) image_colors; i++)
9320                image->colormap[i] = colormap[i];
9321
9322             if (logging != MagickFalse)
9323               {
9324                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9325                       "      image->colors=%d (%d)",
9326                       (int) image->colors, image_colors);
9327
9328                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9329                       "      Update the pixel indexes");
9330               }
9331
9332             /* Sync the pixel indices with the new colormap */
9333
9334             for (y=0; y < (ssize_t) image->rows; y++)
9335             {
9336               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9337
9338               if (q == (Quantum *) NULL)
9339                 break;
9340
9341               for (x=0; x < (ssize_t) image->columns; x++)
9342               {
9343                 for (i=0; i< (ssize_t) image_colors; i++)
9344                 {
9345                   if ((image->alpha_trait == UndefinedPixelTrait ||
9346                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
9347                       image->colormap[i].red == GetPixelRed(image,q) &&
9348                       image->colormap[i].green == GetPixelGreen(image,q) &&
9349                       image->colormap[i].blue == GetPixelBlue(image,q))
9350                   {
9351                     SetPixelIndex(image,i,q);
9352                     break;
9353                   }
9354                 }
9355                 q+=GetPixelChannels(image);
9356               }
9357
9358               if (SyncAuthenticPixels(image,exception) == MagickFalse)
9359                  break;
9360             }
9361           }
9362        }
9363
9364      if (logging != MagickFalse)
9365        {
9366          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9367             "      image->colors=%d", (int) image->colors);
9368
9369          if (image->colormap != NULL)
9370            {
9371              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9372                  "       i     (red,green,blue,alpha)");
9373
9374              for (i=0; i < (ssize_t) image->colors; i++)
9375              {
9376                if (i < 300 || i >= (ssize_t) image->colors - 10)
9377                  {
9378                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9379                        "       %d     (%d,%d,%d,%d)",
9380                         (int) i,
9381                         (int) image->colormap[i].red,
9382                         (int) image->colormap[i].green,
9383                         (int) image->colormap[i].blue,
9384                         (int) image->colormap[i].alpha);
9385                  }
9386              }
9387            }
9388
9389            if (number_transparent < 257)
9390              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9391                    "      number_transparent     = %d",
9392                    number_transparent);
9393            else
9394
9395              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9396                    "      number_transparent     > 256");
9397
9398            if (number_opaque < 257)
9399              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9400                    "      number_opaque          = %d",
9401                    number_opaque);
9402
9403            else
9404              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9405                    "      number_opaque          > 256");
9406
9407            if (number_semitransparent < 257)
9408              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9409                    "      number_semitransparent = %d",
9410                    number_semitransparent);
9411
9412            else
9413              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9414                    "      number_semitransparent > 256");
9415
9416            if (ping_have_non_bw == MagickFalse)
9417               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9418                     "      All pixels and the background are black or white");
9419
9420            else if (ping_have_color == MagickFalse)
9421               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9422                     "      All pixels and the background are gray");
9423
9424            else
9425               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9426                     "      At least one pixel or the background is non-gray");
9427
9428            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9429                "    Exit BUILD_PALETTE:");
9430        }
9431
9432    if (mng_info->write_png8 == MagickFalse)
9433       break;
9434
9435    /* Make any reductions necessary for the PNG8 format */
9436     if (image_colors <= 256 &&
9437         image_colors != 0 && image->colormap != NULL &&
9438         number_semitransparent == 0 &&
9439         number_transparent <= 1)
9440       break;
9441
9442     /* PNG8 can't have semitransparent colors so we threshold the
9443      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9444      * transparent color so if more than one is transparent we merge
9445      * them into image->background_color.
9446      */
9447     if (number_semitransparent != 0 || number_transparent > 1)
9448       {
9449         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9450             "    Thresholding the alpha channel to binary");
9451
9452         for (y=0; y < (ssize_t) image->rows; y++)
9453         {
9454           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9455
9456           if (q == (Quantum *) NULL)
9457             break;
9458
9459           for (x=0; x < (ssize_t) image->columns; x++)
9460           {
9461               if (GetPixelAlpha(image,q) < OpaqueAlpha/2)
9462                 {
9463                   SetPixelViaPixelInfo(image,&image->background_color,q);
9464                   SetPixelAlpha(image,TransparentAlpha,q);
9465                 }
9466               else
9467                   SetPixelAlpha(image,OpaqueAlpha,q);
9468               q+=GetPixelChannels(image);
9469           }
9470
9471           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9472              break;
9473
9474           if (image_colors != 0 && image_colors <= 256 &&
9475              image->colormap != NULL)
9476             for (i=0; i<image_colors; i++)
9477                 image->colormap[i].alpha =
9478                     (image->colormap[i].alpha > TransparentAlpha/2 ?
9479                     TransparentAlpha : OpaqueAlpha);
9480         }
9481       continue;
9482     }
9483
9484     /* PNG8 can't have more than 256 colors so we quantize the pixels and
9485      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
9486      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9487      * colors or less.
9488      */
9489     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9490       {
9491         if (logging != MagickFalse)
9492            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9493                "    Quantizing the background color to 4-4-4");
9494
9495         tried_444 = MagickTrue;
9496
9497         LBR04PacketRGB(image->background_color);
9498
9499         if (logging != MagickFalse)
9500           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9501               "    Quantizing the pixel colors to 4-4-4");
9502
9503         if (image->colormap == NULL)
9504         {
9505           for (y=0; y < (ssize_t) image->rows; y++)
9506           {
9507             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9508
9509             if (q == (Quantum *) NULL)
9510               break;
9511
9512             for (x=0; x < (ssize_t) image->columns; x++)
9513             {
9514               if (GetPixelAlpha(image,q) == OpaqueAlpha)
9515                   LBR04PixelRGB(q);
9516               q+=GetPixelChannels(image);
9517             }
9518
9519             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9520                break;
9521           }
9522         }
9523
9524         else /* Should not reach this; colormap already exists and
9525                 must be <= 256 */
9526         {
9527           if (logging != MagickFalse)
9528               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9529               "    Quantizing the colormap to 4-4-4");
9530
9531           for (i=0; i<image_colors; i++)
9532           {
9533             LBR04PacketRGB(image->colormap[i]);
9534           }
9535         }
9536         continue;
9537       }
9538
9539     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9540       {
9541         if (logging != MagickFalse)
9542            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9543                "    Quantizing the background color to 3-3-3");
9544
9545         tried_333 = MagickTrue;
9546
9547         LBR03PacketRGB(image->background_color);
9548
9549         if (logging != MagickFalse)
9550           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9551               "    Quantizing the pixel colors to 3-3-3-1");
9552
9553         if (image->colormap == NULL)
9554         {
9555           for (y=0; y < (ssize_t) image->rows; y++)
9556           {
9557             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9558
9559             if (q == (Quantum *) NULL)
9560               break;
9561
9562             for (x=0; x < (ssize_t) image->columns; x++)
9563             {
9564               if (GetPixelAlpha(image,q) == OpaqueAlpha)
9565                   LBR03RGB(q);
9566               q+=GetPixelChannels(image);
9567             }
9568
9569             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9570                break;
9571           }
9572         }
9573
9574         else /* Should not reach this; colormap already exists and
9575                 must be <= 256 */
9576         {
9577           if (logging != MagickFalse)
9578               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9579               "    Quantizing the colormap to 3-3-3-1");
9580           for (i=0; i<image_colors; i++)
9581           {
9582               LBR03PacketRGB(image->colormap[i]);
9583           }
9584         }
9585         continue;
9586       }
9587
9588     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9589       {
9590         if (logging != MagickFalse)
9591            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9592                "    Quantizing the background color to 3-3-2");
9593
9594         tried_332 = MagickTrue;
9595
9596         /* Red and green were already done so we only quantize the blue
9597          * channel
9598          */
9599
9600         LBR02PacketBlue(image->background_color);
9601
9602         if (logging != MagickFalse)
9603           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9604               "    Quantizing the pixel colors to 3-3-2-1");
9605
9606         if (image->colormap == NULL)
9607         {
9608           for (y=0; y < (ssize_t) image->rows; y++)
9609           {
9610             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9611
9612             if (q == (Quantum *) NULL)
9613               break;
9614
9615             for (x=0; x < (ssize_t) image->columns; x++)
9616             {
9617               if (GetPixelAlpha(image,q) == OpaqueAlpha)
9618                   LBR02PixelBlue(q);
9619               q+=GetPixelChannels(image);
9620             }
9621
9622             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9623                break;
9624           }
9625         }
9626
9627         else /* Should not reach this; colormap already exists and
9628                 must be <= 256 */
9629         {
9630           if (logging != MagickFalse)
9631               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9632               "    Quantizing the colormap to 3-3-2-1");
9633           for (i=0; i<image_colors; i++)
9634           {
9635               LBR02PacketBlue(image->colormap[i]);
9636           }
9637       }
9638       continue;
9639     }
9640
9641     if (image_colors == 0 || image_colors > 256)
9642     {
9643       /* Take care of special case with 256 opaque colors + 1 transparent
9644        * color.  We don't need to quantize to 2-3-2-1; we only need to
9645        * eliminate one color, so we'll merge the two darkest red
9646        * colors (0x49, 0, 0) -> (0x24, 0, 0).
9647        */
9648       if (logging != MagickFalse)
9649         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9650             "    Merging two dark red background colors to 3-3-2-1");
9651
9652       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9653           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9654           ScaleQuantumToChar(image->background_color.blue) == 0x00)
9655       {
9656          image->background_color.red=ScaleCharToQuantum(0x24);
9657       }
9658
9659       if (logging != MagickFalse)
9660         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9661             "    Merging two dark red pixel colors to 3-3-2-1");
9662
9663       if (image->colormap == NULL)
9664       {
9665         for (y=0; y < (ssize_t) image->rows; y++)
9666         {
9667           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9668
9669           if (q == (Quantum *) NULL)
9670             break;
9671
9672           for (x=0; x < (ssize_t) image->columns; x++)
9673           {
9674             if (ScaleQuantumToChar(GetPixelRed(image,q)) == 0x49 &&
9675                 ScaleQuantumToChar(GetPixelGreen(image,q)) == 0x00 &&
9676                 ScaleQuantumToChar(GetPixelBlue(image,q)) == 0x00 &&
9677                 GetPixelAlpha(image,q) == OpaqueAlpha)
9678               {
9679                 SetPixelRed(image,ScaleCharToQuantum(0x24),q);
9680               }
9681             q+=GetPixelChannels(image);
9682           }
9683
9684           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9685              break;
9686
9687         }
9688       }
9689
9690       else
9691       {
9692          for (i=0; i<image_colors; i++)
9693          {
9694             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9695                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9696                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9697             {
9698                image->colormap[i].red=ScaleCharToQuantum(0x24);
9699             }
9700          }
9701       }
9702     }
9703   }
9704   }
9705   /* END OF BUILD_PALETTE */
9706
9707   /* If we are excluding the tRNS chunk and there is transparency,
9708    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9709    * PNG.
9710    */
9711   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9712      (number_transparent != 0 || number_semitransparent != 0))
9713     {
9714       unsigned int colortype=mng_info->write_png_colortype;
9715
9716       if (ping_have_color == MagickFalse)
9717         mng_info->write_png_colortype = 5;
9718
9719       else
9720         mng_info->write_png_colortype = 7;
9721
9722       if (colortype != 0 &&
9723          mng_info->write_png_colortype != colortype)
9724         ping_need_colortype_warning=MagickTrue;
9725
9726     }
9727
9728   /* See if cheap transparency is possible.  It is only possible
9729    * when there is a single transparent color, no semitransparent
9730    * color, and no opaque color that has the same RGB components
9731    * as the transparent color.  We only need this information if
9732    * we are writing a PNG with colortype 0 or 2, and we have not
9733    * excluded the tRNS chunk.
9734    */
9735   if (number_transparent == 1 &&
9736       mng_info->write_png_colortype < 4)
9737     {
9738        ping_have_cheap_transparency = MagickTrue;
9739
9740        if (number_semitransparent != 0)
9741          ping_have_cheap_transparency = MagickFalse;
9742
9743        else if (image_colors == 0 || image_colors > 256 ||
9744            image->colormap == NULL)
9745          {
9746            register const Quantum
9747              *q;
9748
9749            for (y=0; y < (ssize_t) image->rows; y++)
9750            {
9751              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9752
9753              if (q == (Quantum *) NULL)
9754                break;
9755
9756              for (x=0; x < (ssize_t) image->columns; x++)
9757              {
9758                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9759                      (unsigned short) GetPixelRed(image,q) ==
9760                                      ping_trans_color.red &&
9761                      (unsigned short) GetPixelGreen(image,q) ==
9762                                      ping_trans_color.green &&
9763                      (unsigned short) GetPixelBlue(image,q) ==
9764                                      ping_trans_color.blue)
9765                    {
9766                      ping_have_cheap_transparency = MagickFalse;
9767                      break;
9768                    }
9769
9770                  q+=GetPixelChannels(image);
9771              }
9772
9773              if (ping_have_cheap_transparency == MagickFalse)
9774                 break;
9775            }
9776          }
9777        else
9778          {
9779             /* Assuming that image->colormap[0] is the one transparent color
9780              * and that all others are opaque.
9781              */
9782             if (image_colors > 1)
9783               for (i=1; i<image_colors; i++)
9784                 if (image->colormap[i].red == image->colormap[0].red &&
9785                     image->colormap[i].green == image->colormap[0].green &&
9786                     image->colormap[i].blue == image->colormap[0].blue)
9787                   {
9788                      ping_have_cheap_transparency = MagickFalse;
9789                      break;
9790                   }
9791          }
9792
9793        if (logging != MagickFalse)
9794          {
9795            if (ping_have_cheap_transparency == MagickFalse)
9796              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9797                  "   Cheap transparency is not possible.");
9798
9799            else
9800              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9801                  "   Cheap transparency is possible.");
9802          }
9803      }
9804   else
9805     ping_have_cheap_transparency = MagickFalse;
9806
9807   image_depth=image->depth;
9808
9809   quantum_info = (QuantumInfo *) NULL;
9810   number_colors=0;
9811   image_colors=(int) image->colors;
9812   image_matte=image->alpha_trait !=
9813         UndefinedPixelTrait ? MagickTrue : MagickFalse;
9814
9815   if (mng_info->write_png_colortype < 5)
9816     mng_info->IsPalette=image->storage_class == PseudoClass &&
9817       image_colors <= 256 && image->colormap != NULL;
9818   else
9819     mng_info->IsPalette = MagickFalse;
9820
9821   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9822      (image->colors == 0 || image->colormap == NULL))
9823     {
9824       image_info=DestroyImageInfo(image_info);
9825       image=DestroyImage(image);
9826       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9827           "Cannot write PNG8 or color-type 3; colormap is NULL",
9828           "`%s'",IMimage->filename);
9829       return(MagickFalse);
9830     }
9831
9832   /*
9833     Allocate the PNG structures
9834   */
9835 #ifdef PNG_USER_MEM_SUPPORTED
9836  error_info.image=image;
9837  error_info.exception=exception;
9838   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9839     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9840     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9841
9842 #else
9843   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9844     MagickPNGErrorHandler,MagickPNGWarningHandler);
9845
9846 #endif
9847   if (ping == (png_struct *) NULL)
9848     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9849
9850   ping_info=png_create_info_struct(ping);
9851
9852   if (ping_info == (png_info *) NULL)
9853     {
9854       png_destroy_write_struct(&ping,(png_info **) NULL);
9855       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9856     }
9857
9858   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9859   pixel_info=(MemoryInfo *) NULL;
9860
9861   if (setjmp(png_jmpbuf(ping)))
9862     {
9863       /*
9864         PNG write failed.
9865       */
9866 #ifdef PNG_DEBUG
9867      if (image_info->verbose)
9868         (void) printf("PNG write has failed.\n");
9869 #endif
9870       png_destroy_write_struct(&ping,&ping_info);
9871 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9872       UnlockSemaphoreInfo(ping_semaphore);
9873 #endif
9874
9875       if (pixel_info != (MemoryInfo *) NULL)
9876         pixel_info=RelinquishVirtualMemory(pixel_info);
9877
9878       if (quantum_info != (QuantumInfo *) NULL)
9879         quantum_info=DestroyQuantumInfo(quantum_info);
9880
9881       if (ping_have_blob != MagickFalse)
9882           (void) CloseBlob(image);
9883       image_info=DestroyImageInfo(image_info);
9884       image=DestroyImage(image);
9885       return(MagickFalse);
9886     }
9887
9888   /* {  For navigation to end of SETJMP-protected block.  Within this
9889    *    block, use png_error() instead of Throwing an Exception, to ensure
9890    *    that libpng is able to clean up, and that the semaphore is unlocked.
9891    */
9892
9893 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9894   LockSemaphoreInfo(ping_semaphore);
9895 #endif
9896
9897 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9898   /* Allow benign errors */
9899   png_set_benign_errors(ping, 1);
9900 #endif
9901
9902 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9903   /* Reject images with too many rows or columns */
9904   png_set_user_limits(ping,
9905     (png_uint_32) MagickMin(0x7fffffffL,
9906         GetMagickResourceLimit(WidthResource)),
9907     (png_uint_32) MagickMin(0x7fffffffL,
9908         GetMagickResourceLimit(HeightResource)));
9909 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9910
9911   /*
9912     Prepare PNG for writing.
9913   */
9914
9915 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9916   if (mng_info->write_mng)
9917   {
9918      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9919 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9920      /* Disable new libpng-1.5.10 feature when writing a MNG because
9921       * zero-length PLTE is OK
9922       */
9923      png_set_check_for_invalid_index (ping, 0);
9924 # endif
9925   }
9926
9927 #else
9928 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9929   if (mng_info->write_mng)
9930      png_permit_empty_plte(ping,MagickTrue);
9931
9932 # endif
9933 #endif
9934
9935   x=0;
9936
9937   ping_width=(png_uint_32) image->columns;
9938   ping_height=(png_uint_32) image->rows;
9939
9940   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9941      image_depth=8;
9942
9943   if (mng_info->write_png48 || mng_info->write_png64)
9944      image_depth=16;
9945
9946   if (mng_info->write_png_depth != 0)
9947      image_depth=mng_info->write_png_depth;
9948
9949   /* Adjust requested depth to next higher valid depth if necessary */
9950   if (image_depth > 8)
9951      image_depth=16;
9952
9953   if ((image_depth > 4) && (image_depth < 8))
9954      image_depth=8;
9955
9956   if (image_depth == 3)
9957      image_depth=4;
9958
9959   if (logging != MagickFalse)
9960     {
9961      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9962         "    width=%.20g",(double) ping_width);
9963      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9964         "    height=%.20g",(double) ping_height);
9965      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9966         "    image_matte=%.20g",(double) image->alpha_trait);
9967      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9968         "    image->depth=%.20g",(double) image->depth);
9969      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9970         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
9971     }
9972
9973   save_image_depth=image_depth;
9974   ping_bit_depth=(png_byte) save_image_depth;
9975
9976
9977 #if defined(PNG_pHYs_SUPPORTED)
9978   if (ping_exclude_pHYs == MagickFalse)
9979   {
9980   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9981       (!mng_info->write_mng || !mng_info->equal_physs))
9982     {
9983       if (logging != MagickFalse)
9984         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9985             "    Setting up pHYs chunk");
9986
9987       if (image->units == PixelsPerInchResolution)
9988         {
9989           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9990           ping_pHYs_x_resolution=
9991              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9992           ping_pHYs_y_resolution=
9993              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9994         }
9995
9996       else if (image->units == PixelsPerCentimeterResolution)
9997         {
9998           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9999           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
10000           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
10001         }
10002
10003       else
10004         {
10005           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
10006           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
10007           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
10008         }
10009
10010       if (logging != MagickFalse)
10011         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10012           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
10013           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
10014           (int) ping_pHYs_unit_type);
10015        ping_have_pHYs = MagickTrue;
10016     }
10017   }
10018 #endif
10019
10020   if (ping_exclude_bKGD == MagickFalse)
10021   {
10022   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
10023     {
10024        unsigned int
10025          mask;
10026
10027        mask=0xffff;
10028        if (ping_bit_depth == 8)
10029           mask=0x00ff;
10030
10031        if (ping_bit_depth == 4)
10032           mask=0x000f;
10033
10034        if (ping_bit_depth == 2)
10035           mask=0x0003;
10036
10037        if (ping_bit_depth == 1)
10038           mask=0x0001;
10039
10040        ping_background.red=(png_uint_16)
10041          (ScaleQuantumToShort(image->background_color.red) & mask);
10042
10043        ping_background.green=(png_uint_16)
10044          (ScaleQuantumToShort(image->background_color.green) & mask);
10045
10046        ping_background.blue=(png_uint_16)
10047          (ScaleQuantumToShort(image->background_color.blue) & mask);
10048
10049        ping_background.gray=(png_uint_16) ping_background.green;
10050     }
10051
10052   if (logging != MagickFalse)
10053     {
10054       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10055           "    Setting up bKGD chunk (1)");
10056       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10057           "      background_color index is %d",
10058           (int) ping_background.index);
10059
10060       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10061           "    ping_bit_depth=%d",ping_bit_depth);
10062     }
10063
10064   ping_have_bKGD = MagickTrue;
10065   }
10066
10067   /*
10068     Select the color type.
10069   */
10070   matte=image_matte;
10071   old_bit_depth=0;
10072
10073   if (mng_info->IsPalette && mng_info->write_png8)
10074     {
10075       /* To do: make this a function cause it's used twice, except
10076          for reducing the sample depth from 8. */
10077
10078       number_colors=image_colors;
10079
10080       ping_have_tRNS=MagickFalse;
10081
10082       /*
10083         Set image palette.
10084       */
10085       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10086
10087       if (logging != MagickFalse)
10088         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10089             "  Setting up PLTE chunk with %d colors (%d)",
10090             number_colors, image_colors);
10091
10092       for (i=0; i < (ssize_t) number_colors; i++)
10093       {
10094         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10095         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10096         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10097         if (logging != MagickFalse)
10098           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10099 #if MAGICKCORE_QUANTUM_DEPTH == 8
10100             "    %3ld (%3d,%3d,%3d)",
10101 #else
10102             "    %5ld (%5d,%5d,%5d)",
10103 #endif
10104             (long) i,palette[i].red,palette[i].green,palette[i].blue);
10105
10106       }
10107
10108       ping_have_PLTE=MagickTrue;
10109       image_depth=ping_bit_depth;
10110       ping_num_trans=0;
10111
10112       if (matte != MagickFalse)
10113       {
10114           /*
10115             Identify which colormap entry is transparent.
10116           */
10117           assert(number_colors <= 256);
10118           assert(image->colormap != NULL);
10119
10120           for (i=0; i < (ssize_t) number_transparent; i++)
10121              ping_trans_alpha[i]=0;
10122
10123
10124           ping_num_trans=(unsigned short) (number_transparent +
10125              number_semitransparent);
10126
10127           if (ping_num_trans == 0)
10128              ping_have_tRNS=MagickFalse;
10129
10130           else
10131              ping_have_tRNS=MagickTrue;
10132       }
10133
10134       if (ping_exclude_bKGD == MagickFalse)
10135       {
10136        /*
10137         * Identify which colormap entry is the background color.
10138         */
10139
10140         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
10141           if (IsPNGColorEqual(ping_background,image->colormap[i]))
10142             break;
10143
10144         ping_background.index=(png_byte) i;
10145
10146         if (logging != MagickFalse)
10147           {
10148             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10149                  "      background_color index is %d",
10150                  (int) ping_background.index);
10151           }
10152       }
10153     } /* end of write_png8 */
10154
10155   else if (mng_info->write_png_colortype == 1)
10156     {
10157       image_matte=MagickFalse;
10158       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10159     }
10160
10161   else if (mng_info->write_png24 || mng_info->write_png48 ||
10162       mng_info->write_png_colortype == 3)
10163     {
10164       image_matte=MagickFalse;
10165       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10166     }
10167
10168   else if (mng_info->write_png32 || mng_info->write_png64 ||
10169       mng_info->write_png_colortype == 7)
10170     {
10171       image_matte=MagickTrue;
10172       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10173     }
10174
10175   else /* mng_info->write_pngNN not specified */
10176     {
10177       image_depth=ping_bit_depth;
10178
10179       if (mng_info->write_png_colortype != 0)
10180         {
10181           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
10182
10183           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10184               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10185             image_matte=MagickTrue;
10186
10187           else
10188             image_matte=MagickFalse;
10189
10190           if (logging != MagickFalse)
10191              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10192              "   PNG colortype %d was specified:",(int) ping_color_type);
10193         }
10194
10195       else /* write_png_colortype not specified */
10196         {
10197           if (logging != MagickFalse)
10198              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10199              "  Selecting PNG colortype:");
10200
10201           if (image_info->type == TrueColorType)
10202             {
10203               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10204               image_matte=MagickFalse;
10205             }
10206           else if (image_info->type == TrueColorAlphaType)
10207             {
10208               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10209               image_matte=MagickTrue;
10210             }
10211           else if (image_info->type == PaletteType ||
10212                    image_info->type == PaletteAlphaType)
10213             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10214           else
10215             {
10216               if (ping_have_color == MagickFalse)
10217                 {
10218                   if (image_matte == MagickFalse)
10219                     {
10220                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10221                       image_matte=MagickFalse;
10222                     }
10223
10224                   else
10225                     {
10226                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
10227                       image_matte=MagickTrue;
10228                     }
10229                 }
10230               else
10231                 {
10232                   if (image_matte == MagickFalse)
10233                     {
10234                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10235                       image_matte=MagickFalse;
10236                     }
10237
10238                   else
10239                     {
10240                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
10241                       image_matte=MagickTrue;
10242                     }
10243                  }
10244             }
10245
10246         }
10247
10248       if (logging != MagickFalse)
10249          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10250          "    Selected PNG colortype=%d",ping_color_type);
10251
10252       if (ping_bit_depth < 8)
10253         {
10254           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10255               ping_color_type == PNG_COLOR_TYPE_RGB ||
10256               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10257             ping_bit_depth=8;
10258         }
10259
10260       old_bit_depth=ping_bit_depth;
10261
10262       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10263         {
10264           if (image->alpha_trait == UndefinedPixelTrait &&
10265                ping_have_non_bw == MagickFalse)
10266              ping_bit_depth=1;
10267         }
10268
10269       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
10270         {
10271            size_t one = 1;
10272            ping_bit_depth=1;
10273
10274            if (image->colors == 0)
10275            {
10276               /* DO SOMETHING */
10277                 png_error(ping,"image has 0 colors");
10278            }
10279
10280            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
10281              ping_bit_depth <<= 1;
10282         }
10283
10284       if (logging != MagickFalse)
10285          {
10286            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10287             "    Number of colors: %.20g",(double) image_colors);
10288
10289            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10290             "    Tentative PNG bit depth: %d",ping_bit_depth);
10291          }
10292
10293       if (ping_bit_depth < (int) mng_info->write_png_depth)
10294          ping_bit_depth = mng_info->write_png_depth;
10295     }
10296
10297   image_depth=ping_bit_depth;
10298
10299   if (logging != MagickFalse)
10300     {
10301       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10302         "    Tentative PNG color type: %s (%.20g)",
10303         PngColorTypeToString(ping_color_type),
10304         (double) ping_color_type);
10305
10306       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10307         "    image_info->type: %.20g",(double) image_info->type);
10308
10309       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10310         "    image_depth: %.20g",(double) image_depth);
10311
10312       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10313
10314         "    image->depth: %.20g",(double) image->depth);
10315
10316       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10317         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
10318     }
10319
10320   if (matte != MagickFalse)
10321     {
10322       if (mng_info->IsPalette)
10323         {
10324           if (mng_info->write_png_colortype == 0)
10325             {
10326               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10327
10328               if (ping_have_color != MagickFalse)
10329                  ping_color_type=PNG_COLOR_TYPE_RGBA;
10330             }
10331
10332           /*
10333            * Determine if there is any transparent color.
10334           */
10335           if (number_transparent + number_semitransparent == 0)
10336             {
10337               /*
10338                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
10339               */
10340
10341               image_matte=MagickFalse;
10342
10343               if (mng_info->write_png_colortype == 0)
10344                 ping_color_type&=0x03;
10345             }
10346
10347           else
10348             {
10349               unsigned int
10350                 mask;
10351
10352               mask=0xffff;
10353
10354               if (ping_bit_depth == 8)
10355                  mask=0x00ff;
10356
10357               if (ping_bit_depth == 4)
10358                  mask=0x000f;
10359
10360               if (ping_bit_depth == 2)
10361                  mask=0x0003;
10362
10363               if (ping_bit_depth == 1)
10364                  mask=0x0001;
10365
10366               ping_trans_color.red=(png_uint_16)
10367                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10368
10369               ping_trans_color.green=(png_uint_16)
10370                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10371
10372               ping_trans_color.blue=(png_uint_16)
10373                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10374
10375               ping_trans_color.gray=(png_uint_16)
10376                 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
10377                    image->colormap)) & mask);
10378
10379               ping_trans_color.index=(png_byte) 0;
10380
10381               ping_have_tRNS=MagickTrue;
10382             }
10383
10384           if (ping_have_tRNS != MagickFalse)
10385             {
10386               /*
10387                * Determine if there is one and only one transparent color
10388                * and if so if it is fully transparent.
10389                */
10390               if (ping_have_cheap_transparency == MagickFalse)
10391                 ping_have_tRNS=MagickFalse;
10392             }
10393
10394           if (ping_have_tRNS != MagickFalse)
10395             {
10396               if (mng_info->write_png_colortype == 0)
10397                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
10398
10399               if (image_depth == 8)
10400                 {
10401                   ping_trans_color.red&=0xff;
10402                   ping_trans_color.green&=0xff;
10403                   ping_trans_color.blue&=0xff;
10404                   ping_trans_color.gray&=0xff;
10405                 }
10406             }
10407         }
10408       else
10409         {
10410           if (image_depth == 8)
10411             {
10412               ping_trans_color.red&=0xff;
10413               ping_trans_color.green&=0xff;
10414               ping_trans_color.blue&=0xff;
10415               ping_trans_color.gray&=0xff;
10416             }
10417         }
10418     }
10419
10420     matte=image_matte;
10421
10422     if (ping_have_tRNS != MagickFalse)
10423       image_matte=MagickFalse;
10424
10425     if ((mng_info->IsPalette) &&
10426         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10427         ping_have_color == MagickFalse &&
10428         (image_matte == MagickFalse || image_depth >= 8))
10429       {
10430         size_t one=1;
10431
10432         if (image_matte != MagickFalse)
10433           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10434
10435         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10436           {
10437             ping_color_type=PNG_COLOR_TYPE_GRAY;
10438
10439             if (save_image_depth == 16 && image_depth == 8)
10440               {
10441                 if (logging != MagickFalse)
10442                   {
10443                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10444                         "  Scaling ping_trans_color (0)");
10445                   }
10446                     ping_trans_color.gray*=0x0101;
10447               }
10448           }
10449
10450         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10451           image_depth=MAGICKCORE_QUANTUM_DEPTH;
10452
10453         if ((image_colors == 0) ||
10454              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10455           image_colors=(int) (one << image_depth);
10456
10457         if (image_depth > 8)
10458           ping_bit_depth=16;
10459
10460         else
10461           {
10462             ping_bit_depth=8;
10463             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10464               {
10465                 if(!mng_info->write_png_depth)
10466                   {
10467                     ping_bit_depth=1;
10468
10469                     while ((int) (one << ping_bit_depth)
10470                         < (ssize_t) image_colors)
10471                       ping_bit_depth <<= 1;
10472                   }
10473               }
10474
10475             else if (ping_color_type ==
10476                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10477                 mng_info->IsPalette)
10478               {
10479               /* Check if grayscale is reducible */
10480
10481                 int
10482                   depth_4_ok=MagickTrue,
10483                   depth_2_ok=MagickTrue,
10484                   depth_1_ok=MagickTrue;
10485
10486                 for (i=0; i < (ssize_t) image_colors; i++)
10487                 {
10488                    unsigned char
10489                      intensity;
10490
10491                    intensity=ScaleQuantumToChar(image->colormap[i].red);
10492
10493                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10494                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10495                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10496                      depth_2_ok=depth_1_ok=MagickFalse;
10497                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10498                      depth_1_ok=MagickFalse;
10499                 }
10500
10501                 if (depth_1_ok && mng_info->write_png_depth <= 1)
10502                   ping_bit_depth=1;
10503
10504                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10505                   ping_bit_depth=2;
10506
10507                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10508                   ping_bit_depth=4;
10509               }
10510           }
10511
10512           image_depth=ping_bit_depth;
10513       }
10514
10515     else
10516
10517       if (mng_info->IsPalette)
10518       {
10519         number_colors=image_colors;
10520
10521         if (image_depth <= 8)
10522           {
10523             /*
10524               Set image palette.
10525             */
10526             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10527
10528             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10529               {
10530                 for (i=0; i < (ssize_t) number_colors; i++)
10531                 {
10532                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10533                   palette[i].green=
10534                     ScaleQuantumToChar(image->colormap[i].green);
10535                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10536                 }
10537
10538                 if (logging != MagickFalse)
10539                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10540                     "  Setting up PLTE chunk with %d colors",
10541                     number_colors);
10542
10543                 ping_have_PLTE=MagickTrue;
10544               }
10545
10546             /* color_type is PNG_COLOR_TYPE_PALETTE */
10547             if (mng_info->write_png_depth == 0)
10548               {
10549                 size_t
10550                   one;
10551
10552                 ping_bit_depth=1;
10553                 one=1;
10554
10555                 while ((one << ping_bit_depth) < (size_t) number_colors)
10556                   ping_bit_depth <<= 1;
10557               }
10558
10559             ping_num_trans=0;
10560
10561             if (matte != MagickFalse)
10562               {
10563                 /*
10564                  * Set up trans_colors array.
10565                  */
10566                 assert(number_colors <= 256);
10567
10568                 ping_num_trans=(unsigned short) (number_transparent +
10569                   number_semitransparent);
10570
10571                 if (ping_num_trans == 0)
10572                   ping_have_tRNS=MagickFalse;
10573
10574                 else
10575                   {
10576                     if (logging != MagickFalse)
10577                       {
10578                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10579                           "  Scaling ping_trans_color (1)");
10580                       }
10581                     ping_have_tRNS=MagickTrue;
10582
10583                     for (i=0; i < ping_num_trans; i++)
10584                     {
10585                        ping_trans_alpha[i]= (png_byte)
10586                          ScaleQuantumToChar(image->colormap[i].alpha);
10587                     }
10588                   }
10589               }
10590           }
10591       }
10592
10593     else
10594       {
10595
10596         if (image_depth < 8)
10597           image_depth=8;
10598
10599         if ((save_image_depth == 16) && (image_depth == 8))
10600           {
10601             if (logging != MagickFalse)
10602               {
10603                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10604                   "    Scaling ping_trans_color from (%d,%d,%d)",
10605                   (int) ping_trans_color.red,
10606                   (int) ping_trans_color.green,
10607                   (int) ping_trans_color.blue);
10608               }
10609
10610             ping_trans_color.red*=0x0101;
10611             ping_trans_color.green*=0x0101;
10612             ping_trans_color.blue*=0x0101;
10613             ping_trans_color.gray*=0x0101;
10614
10615             if (logging != MagickFalse)
10616               {
10617                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10618                   "    to (%d,%d,%d)",
10619                   (int) ping_trans_color.red,
10620                   (int) ping_trans_color.green,
10621                   (int) ping_trans_color.blue);
10622               }
10623           }
10624       }
10625
10626     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
10627          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
10628
10629     /*
10630       Adjust background and transparency samples in sub-8-bit grayscale files.
10631     */
10632     if (ping_bit_depth < 8 && ping_color_type ==
10633         PNG_COLOR_TYPE_GRAY)
10634       {
10635          png_uint_16
10636            maxval;
10637
10638          size_t
10639            one=1;
10640
10641          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10642
10643          if (ping_exclude_bKGD == MagickFalse)
10644          {
10645
10646          ping_background.gray=(png_uint_16) ((maxval/65535.)*
10647            (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10648            &image->background_color))) +.5)));
10649
10650          if (logging != MagickFalse)
10651            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10652              "  Setting up bKGD chunk (2)");
10653          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10654              "      background_color index is %d",
10655              (int) ping_background.index);
10656
10657          ping_have_bKGD = MagickTrue;
10658          }
10659
10660          if (logging != MagickFalse)
10661            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10662              "  Scaling ping_trans_color.gray from %d",
10663              (int)ping_trans_color.gray);
10664
10665          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10666            ping_trans_color.gray)+.5);
10667
10668          if (logging != MagickFalse)
10669            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10670              "      to %d", (int)ping_trans_color.gray);
10671       }
10672
10673   if (ping_exclude_bKGD == MagickFalse)
10674   {
10675     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10676       {
10677         /*
10678            Identify which colormap entry is the background color.
10679         */
10680
10681         number_colors=image_colors;
10682
10683         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10684           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10685             break;
10686
10687         ping_background.index=(png_byte) i;
10688
10689         if (logging != MagickFalse)
10690           {
10691             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10692               "  Setting up bKGD chunk with index=%d",(int) i);
10693           }
10694
10695         if (i < (ssize_t) number_colors)
10696           {
10697             ping_have_bKGD = MagickTrue;
10698
10699             if (logging != MagickFalse)
10700               {
10701                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10702                   "     background   =(%d,%d,%d)",
10703                         (int) ping_background.red,
10704                         (int) ping_background.green,
10705                         (int) ping_background.blue);
10706               }
10707           }
10708
10709         else  /* Can't happen */
10710           {
10711             if (logging != MagickFalse)
10712               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10713                   "      No room in PLTE to add bKGD color");
10714             ping_have_bKGD = MagickFalse;
10715           }
10716       }
10717   }
10718
10719   if (logging != MagickFalse)
10720     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10721       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10722       ping_color_type);
10723   /*
10724     Initialize compression level and filtering.
10725   */
10726   if (logging != MagickFalse)
10727     {
10728       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10729         "  Setting up deflate compression");
10730
10731       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10732         "    Compression buffer size: 32768");
10733     }
10734
10735   png_set_compression_buffer_size(ping,32768L);
10736
10737   if (logging != MagickFalse)
10738     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10739       "    Compression mem level: 9");
10740
10741   png_set_compression_mem_level(ping, 9);
10742
10743   /* Untangle the "-quality" setting:
10744
10745      Undefined is 0; the default is used.
10746      Default is 75
10747
10748      10's digit:
10749
10750         0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10751            zlib default compression level
10752
10753         1-9: the zlib compression level
10754
10755      1's digit:
10756
10757         0-4: the PNG filter method
10758
10759         5:   libpng adaptive filtering if compression level > 5
10760              libpng filter type "none" if compression level <= 5
10761                 or if image is grayscale or palette
10762
10763         6:   libpng adaptive filtering
10764
10765         7:   "LOCO" filtering (intrapixel differing) if writing
10766              a MNG, otherwise "none".  Did not work in IM-6.7.0-9
10767              and earlier because of a missing "else".
10768
10769         8:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10770              filtering. Unused prior to IM-6.7.0-10, was same as 6
10771
10772         9:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10773              Unused prior to IM-6.7.0-10, was same as 6
10774
10775     Note that using the -quality option, not all combinations of
10776     PNG filter type, zlib compression level, and zlib compression
10777     strategy are possible.  This will be addressed soon in a
10778     release that accomodates "-define png:compression-strategy", etc.
10779
10780    */
10781
10782   quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10783      image_info->quality;
10784
10785   if (quality <= 9)
10786     {
10787       if (mng_info->write_png_compression_strategy == 0)
10788         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10789     }
10790
10791   else if (mng_info->write_png_compression_level == 0)
10792     {
10793       int
10794         level;
10795
10796       level=(int) MagickMin((ssize_t) quality/10,9);
10797
10798       mng_info->write_png_compression_level = level+1;
10799     }
10800
10801   if (mng_info->write_png_compression_strategy == 0)
10802     {
10803         if ((quality %10) == 8 || (quality %10) == 9)
10804 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10805           mng_info->write_png_compression_strategy=Z_RLE+1;
10806 #else
10807           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10808 #endif
10809     }
10810
10811   if (mng_info->write_png_compression_filter == 0)
10812         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10813
10814   if (logging != MagickFalse)
10815     {
10816         if (mng_info->write_png_compression_level)
10817           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10818             "    Compression level:    %d",
10819             (int) mng_info->write_png_compression_level-1);
10820
10821         if (mng_info->write_png_compression_strategy)
10822           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10823             "    Compression strategy: %d",
10824             (int) mng_info->write_png_compression_strategy-1);
10825
10826         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10827           "  Setting up filtering");
10828
10829         if (mng_info->write_png_compression_filter == 6)
10830           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10831             "    Base filter method: ADAPTIVE");
10832         else if (mng_info->write_png_compression_filter == 0 ||
10833                  mng_info->write_png_compression_filter == 1)
10834           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10835             "    Base filter method: NONE");
10836         else
10837           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10838             "    Base filter method: %d",
10839             (int) mng_info->write_png_compression_filter-1);
10840     }
10841
10842   if (mng_info->write_png_compression_level != 0)
10843     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10844
10845   if (mng_info->write_png_compression_filter == 6)
10846     {
10847       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10848          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10849          (quality < 50))
10850         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10851       else
10852         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10853      }
10854   else if (mng_info->write_png_compression_filter == 7 ||
10855       mng_info->write_png_compression_filter == 10)
10856     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10857
10858   else if (mng_info->write_png_compression_filter == 8)
10859     {
10860 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10861       if (mng_info->write_mng)
10862       {
10863          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10864              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10865         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10866       }
10867 #endif
10868       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10869     }
10870
10871   else if (mng_info->write_png_compression_filter == 9)
10872     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10873
10874   else if (mng_info->write_png_compression_filter != 0)
10875     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10876        mng_info->write_png_compression_filter-1);
10877
10878   if (mng_info->write_png_compression_strategy != 0)
10879     png_set_compression_strategy(ping,
10880        mng_info->write_png_compression_strategy-1);
10881
10882   ping_interlace_method=image_info->interlace != NoInterlace;
10883
10884   if (mng_info->write_mng)
10885     png_set_sig_bytes(ping,8);
10886
10887   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10888
10889   if (mng_info->write_png_colortype != 0)
10890     {
10891      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10892        if (ping_have_color != MagickFalse)
10893          {
10894            ping_color_type = PNG_COLOR_TYPE_RGB;
10895
10896            if (ping_bit_depth < 8)
10897              ping_bit_depth=8;
10898          }
10899
10900      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10901        if (ping_have_color != MagickFalse)
10902          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10903     }
10904
10905   if (ping_need_colortype_warning != MagickFalse ||
10906      ((mng_info->write_png_depth &&
10907      (int) mng_info->write_png_depth != ping_bit_depth) ||
10908      (mng_info->write_png_colortype &&
10909      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10910       mng_info->write_png_colortype != 7 &&
10911       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10912     {
10913       if (logging != MagickFalse)
10914         {
10915           if (ping_need_colortype_warning != MagickFalse)
10916             {
10917               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10918                  "  Image has transparency but tRNS chunk was excluded");
10919             }
10920
10921           if (mng_info->write_png_depth)
10922             {
10923               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10924                   "  Defined png:bit-depth=%u, Computed depth=%u",
10925                   mng_info->write_png_depth,
10926                   ping_bit_depth);
10927             }
10928
10929           if (mng_info->write_png_colortype)
10930             {
10931               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10932                   "  Defined png:color-type=%u, Computed color type=%u",
10933                   mng_info->write_png_colortype-1,
10934                   ping_color_type);
10935             }
10936         }
10937
10938       png_warning(ping,
10939         "Cannot write image with defined png:bit-depth or png:color-type.");
10940     }
10941
10942   if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10943     {
10944       /* Add an opaque matte channel */
10945       image->alpha_trait = BlendPixelTrait;
10946       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10947
10948       if (logging != MagickFalse)
10949         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10950           "  Added an opaque matte channel");
10951     }
10952
10953   if (number_transparent != 0 || number_semitransparent != 0)
10954     {
10955       if (ping_color_type < 4)
10956         {
10957            ping_have_tRNS=MagickTrue;
10958            if (logging != MagickFalse)
10959              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10960                "  Setting ping_have_tRNS=MagickTrue.");
10961         }
10962     }
10963
10964   if (logging != MagickFalse)
10965     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10966       "  Writing PNG header chunks");
10967
10968   png_set_IHDR(ping,ping_info,ping_width,ping_height,
10969                ping_bit_depth,ping_color_type,
10970                ping_interlace_method,ping_compression_method,
10971                ping_filter_method);
10972
10973   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10974     {
10975       png_set_PLTE(ping,ping_info,palette,number_colors);
10976
10977       if (logging != MagickFalse)
10978         {
10979           for (i=0; i< (ssize_t) number_colors; i++)
10980           {
10981             if (i < ping_num_trans)
10982               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10983                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10984                       (int) i,
10985                       (int) palette[i].red,
10986                       (int) palette[i].green,
10987                       (int) palette[i].blue,
10988                       (int) i,
10989                       (int) ping_trans_alpha[i]);
10990              else
10991               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10992                 "     PLTE[%d] = (%d,%d,%d)",
10993                       (int) i,
10994                       (int) palette[i].red,
10995                       (int) palette[i].green,
10996                       (int) palette[i].blue);
10997            }
10998          }
10999     }
11000
11001   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
11002   if (ping_exclude_sRGB != MagickFalse ||
11003      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11004   {
11005     if ((ping_exclude_tEXt == MagickFalse ||
11006        ping_exclude_zTXt == MagickFalse) &&
11007        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
11008     {
11009       ResetImageProfileIterator(image);
11010       for (name=GetNextImageProfile(image); name != (char *) NULL; )
11011       {
11012         profile=GetImageProfile(image,name);
11013
11014         if (profile != (StringInfo *) NULL)
11015           {
11016 #ifdef PNG_WRITE_iCCP_SUPPORTED
11017             if ((LocaleCompare(name,"ICC") == 0) ||
11018                 (LocaleCompare(name,"ICM") == 0))
11019               {
11020                 ping_have_iCCP = MagickTrue;
11021                 if (ping_exclude_iCCP == MagickFalse)
11022                   {
11023                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11024                         "  Setting up iCCP chunk");
11025
11026                     png_set_iCCP(ping,ping_info,(png_charp) name,0,
11027 #if (PNG_LIBPNG_VER < 10500)
11028                     (png_charp) GetStringInfoDatum(profile),
11029 #else
11030                     (const png_byte *) GetStringInfoDatum(profile),
11031 #endif
11032                     (png_uint_32) GetStringInfoLength(profile));
11033                   }
11034                 else
11035                   {
11036                     /* Do not write hex-encoded ICC chunk */
11037                        name=GetNextImageProfile(image);
11038                        continue;
11039                   }
11040               }
11041 #endif /* WRITE_iCCP */
11042
11043             if (LocaleCompare(name,"exif") == 0)
11044               {
11045                    /* Do not write hex-encoded ICC chunk; we will
11046                       write it later as an eXIf chunk */
11047                    name=GetNextImageProfile(image);
11048                    continue;
11049               }
11050
11051               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11052                  "  Setting up zTXt chunk with uuencoded %s profile",
11053                  name);
11054               Magick_png_write_raw_profile(image_info,ping,ping_info,
11055                 (unsigned char *) name,(unsigned char *) name,
11056                 GetStringInfoDatum(profile),
11057                 (png_uint_32) GetStringInfoLength(profile));
11058           }
11059         name=GetNextImageProfile(image);
11060       }
11061     }
11062   }
11063
11064 #if defined(PNG_WRITE_sRGB_SUPPORTED)
11065   if ((mng_info->have_write_global_srgb == 0) &&
11066       ping_have_iCCP != MagickTrue &&
11067       (ping_have_sRGB != MagickFalse ||
11068       png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11069     {
11070       if (ping_exclude_sRGB == MagickFalse)
11071         {
11072           /*
11073             Note image rendering intent.
11074           */
11075           if (logging != MagickFalse)
11076             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11077                 "  Setting up sRGB chunk");
11078
11079           (void) png_set_sRGB(ping,ping_info,(
11080             Magick_RenderingIntent_to_PNG_RenderingIntent(
11081               image->rendering_intent)));
11082
11083           ping_have_sRGB = MagickTrue;
11084         }
11085     }
11086
11087   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11088 #endif
11089     {
11090       if (ping_exclude_gAMA == MagickFalse &&
11091           ping_have_iCCP == MagickFalse &&
11092           ping_have_sRGB == MagickFalse &&
11093           (ping_exclude_sRGB == MagickFalse ||
11094           (image->gamma < .45 || image->gamma > .46)))
11095       {
11096       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
11097         {
11098           /*
11099             Note image gamma.
11100             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11101           */
11102           if (logging != MagickFalse)
11103             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11104               "  Setting up gAMA chunk");
11105
11106           png_set_gAMA(ping,ping_info,image->gamma);
11107         }
11108       }
11109
11110       if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
11111         {
11112           if ((mng_info->have_write_global_chrm == 0) &&
11113               (image->chromaticity.red_primary.x != 0.0))
11114             {
11115               /*
11116                 Note image chromaticity.
11117                 Note: if cHRM+gAMA == sRGB write sRGB instead.
11118               */
11119                PrimaryInfo
11120                  bp,
11121                  gp,
11122                  rp,
11123                  wp;
11124
11125                wp=image->chromaticity.white_point;
11126                rp=image->chromaticity.red_primary;
11127                gp=image->chromaticity.green_primary;
11128                bp=image->chromaticity.blue_primary;
11129
11130                if (logging != MagickFalse)
11131                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11132                    "  Setting up cHRM chunk");
11133
11134                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
11135                    bp.x,bp.y);
11136            }
11137         }
11138     }
11139
11140   if (ping_exclude_bKGD == MagickFalse)
11141     {
11142       if (ping_have_bKGD != MagickFalse)
11143         {
11144           png_set_bKGD(ping,ping_info,&ping_background);
11145           if (logging != MagickFalse)
11146             {
11147               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11148                    "    Setting up bKGD chunk");
11149               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11150                    "      background color = (%d,%d,%d)",
11151                         (int) ping_background.red,
11152                         (int) ping_background.green,
11153                         (int) ping_background.blue);
11154               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11155                    "      index = %d, gray=%d",
11156                         (int) ping_background.index,
11157                         (int) ping_background.gray);
11158             }
11159          }
11160     }
11161
11162   if (ping_exclude_pHYs == MagickFalse)
11163     {
11164       if (ping_have_pHYs != MagickFalse)
11165         {
11166           png_set_pHYs(ping,ping_info,
11167              ping_pHYs_x_resolution,
11168              ping_pHYs_y_resolution,
11169              ping_pHYs_unit_type);
11170
11171           if (logging != MagickFalse)
11172             {
11173               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11174                    "    Setting up pHYs chunk");
11175               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11176                    "      x_resolution=%lu",
11177                    (unsigned long) ping_pHYs_x_resolution);
11178               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11179                    "      y_resolution=%lu",
11180                    (unsigned long) ping_pHYs_y_resolution);
11181               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11182                    "      unit_type=%lu",
11183                    (unsigned long) ping_pHYs_unit_type);
11184             }
11185         }
11186     }
11187
11188 #if defined(PNG_tIME_SUPPORTED)
11189   if (ping_exclude_tIME == MagickFalse)
11190     {
11191       const char
11192         *timestamp;
11193
11194       if (image->taint == MagickFalse)
11195         {
11196           timestamp=GetImageOption(image_info,"png:tIME");
11197
11198           if (timestamp == (const char *) NULL)
11199             timestamp=GetImageProperty(image,"png:tIME",exception);
11200         }
11201
11202       else
11203         {
11204           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11205              "  Reset tIME in tainted image");
11206
11207           timestamp=GetImageProperty(image,"date:modify",exception);
11208         }
11209
11210       if (timestamp != (const char *) NULL)
11211           write_tIME_chunk(image,ping,ping_info,timestamp,exception);
11212     }
11213 #endif
11214
11215   if (mng_info->need_blob != MagickFalse)
11216   {
11217     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
11218        MagickFalse)
11219        png_error(ping,"WriteBlob Failed");
11220
11221      ping_have_blob=MagickTrue;
11222   }
11223
11224   png_write_info_before_PLTE(ping, ping_info);
11225
11226   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
11227     {
11228       if (logging != MagickFalse)
11229         {
11230           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11231               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
11232         }
11233
11234       if (ping_color_type == 3)
11235          (void) png_set_tRNS(ping, ping_info,
11236                 ping_trans_alpha,
11237                 ping_num_trans,
11238                 NULL);
11239
11240       else
11241         {
11242            (void) png_set_tRNS(ping, ping_info,
11243                   NULL,
11244                   0,
11245                   &ping_trans_color);
11246
11247            if (logging != MagickFalse)
11248              {
11249                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11250                  "     tRNS color   =(%d,%d,%d)",
11251                        (int) ping_trans_color.red,
11252                        (int) ping_trans_color.green,
11253                        (int) ping_trans_color.blue);
11254              }
11255          }
11256     }
11257
11258   png_write_info(ping,ping_info);
11259
11260   /* write orNT if image->orientation is defined */
11261   if (image->orientation != UndefinedOrientation)
11262     {
11263       unsigned char
11264         chunk[6];
11265       (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
11266       PNGType(chunk,mng_orNT);
11267       LogPNGChunk(logging,mng_orNT,1L);
11268       /* PNG uses Exif orientation values */
11269       chunk[4]=Magick_Orientation_to_Exif_Orientation(image->orientation);
11270       (void) WriteBlob(image,5,chunk);
11271       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11272     }
11273
11274   ping_wrote_caNv = MagickFalse;
11275
11276   /* write caNv chunk */
11277   if (ping_exclude_caNv == MagickFalse)
11278     {
11279       if ((image->page.width != 0 && image->page.width != image->columns) ||
11280           (image->page.height != 0 && image->page.height != image->rows) ||
11281           image->page.x != 0 || image->page.y != 0)
11282         {
11283           unsigned char
11284             chunk[20];
11285
11286           (void) WriteBlobMSBULong(image,16L);  /* data length=8 */
11287           PNGType(chunk,mng_caNv);
11288           LogPNGChunk(logging,mng_caNv,16L);
11289           PNGLong(chunk+4,(png_uint_32) image->page.width);
11290           PNGLong(chunk+8,(png_uint_32) image->page.height);
11291           PNGsLong(chunk+12,(png_int_32) image->page.x);
11292           PNGsLong(chunk+16,(png_int_32) image->page.y);
11293           (void) WriteBlob(image,20,chunk);
11294           (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11295           ping_wrote_caNv = MagickTrue;
11296         }
11297     }
11298
11299 #if defined(PNG_oFFs_SUPPORTED)
11300   if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
11301     {
11302       if (image->page.x || image->page.y)
11303         {
11304            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
11305               (png_int_32) image->page.y, 0);
11306
11307            if (logging != MagickFalse)
11308              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11309                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
11310                  (int) image->page.x, (int) image->page.y);
11311         }
11312     }
11313 #endif
11314
11315 #if (PNG_LIBPNG_VER == 10206)
11316     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
11317 #define PNG_HAVE_IDAT               0x04
11318     ping->mode |= PNG_HAVE_IDAT;
11319 #undef PNG_HAVE_IDAT
11320 #endif
11321
11322   png_set_packing(ping);
11323   /*
11324     Allocate memory.
11325   */
11326   rowbytes=image->columns;
11327   if (image_depth > 8)
11328     rowbytes*=2;
11329   switch (ping_color_type)
11330     {
11331       case PNG_COLOR_TYPE_RGB:
11332         rowbytes*=3;
11333         break;
11334
11335       case PNG_COLOR_TYPE_GRAY_ALPHA:
11336         rowbytes*=2;
11337         break;
11338
11339       case PNG_COLOR_TYPE_RGBA:
11340         rowbytes*=4;
11341         break;
11342
11343       default:
11344         break;
11345     }
11346
11347   if (logging != MagickFalse)
11348     {
11349       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11350         "  Writing PNG image data");
11351
11352       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11353         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
11354     }
11355   pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
11356   if (pixel_info == (MemoryInfo *) NULL)
11357     png_error(ping,"Allocation of memory for pixels failed");
11358   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
11359   /*
11360     Initialize image scanlines.
11361   */
11362   quantum_info=AcquireQuantumInfo(image_info,image);
11363   if (quantum_info == (QuantumInfo *) NULL)
11364     png_error(ping,"Memory allocation for quantum_info failed");
11365   quantum_info->format=UndefinedQuantumFormat;
11366   SetQuantumDepth(image,quantum_info,image_depth);
11367   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
11368   num_passes=png_set_interlace_handling(ping);
11369
11370   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11371        !mng_info->write_png48 && !mng_info->write_png64 &&
11372        !mng_info->write_png32) &&
11373        (mng_info->IsPalette ||
11374        (image_info->type == BilevelType)) &&
11375        image_matte == MagickFalse &&
11376        ping_have_non_bw == MagickFalse)
11377     {
11378       /* Palette, Bilevel, or Opaque Monochrome */
11379       register const Quantum
11380         *p;
11381
11382       SetQuantumDepth(image,quantum_info,8);
11383       for (pass=0; pass < num_passes; pass++)
11384       {
11385         /*
11386           Convert PseudoClass image to a PNG monochrome image.
11387         */
11388         for (y=0; y < (ssize_t) image->rows; y++)
11389         {
11390           if (logging != MagickFalse && y == 0)
11391              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11392                  "    Writing row of pixels (0)");
11393
11394           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11395
11396           if (p == (const Quantum *) NULL)
11397             break;
11398
11399           if (mng_info->IsPalette)
11400             {
11401               (void) ExportQuantumPixels(image,(CacheView *) NULL,
11402                 quantum_info,GrayQuantum,ping_pixels,exception);
11403               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
11404                   mng_info->write_png_depth &&
11405                   mng_info->write_png_depth != old_bit_depth)
11406                 {
11407                   /* Undo pixel scaling */
11408                   for (i=0; i < (ssize_t) image->columns; i++)
11409                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
11410                      >> (8-old_bit_depth));
11411                 }
11412             }
11413
11414           else
11415             {
11416               (void) ExportQuantumPixels(image,(CacheView *) NULL,
11417                 quantum_info,RedQuantum,ping_pixels,exception);
11418             }
11419
11420           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11421             for (i=0; i < (ssize_t) image->columns; i++)
11422                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11423                       255 : 0);
11424
11425           if (logging != MagickFalse && y == 0)
11426             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11427                 "    Writing row of pixels (1)");
11428
11429           png_write_row(ping,ping_pixels);
11430
11431           status=SetImageProgress(image,SaveImageTag,
11432               (MagickOffsetType) (pass * image->rows + y),
11433               num_passes * image->rows);
11434
11435           if (status == MagickFalse)
11436             break;
11437         }
11438       }
11439     }
11440
11441   else   /* Not Palette, Bilevel, or Opaque Monochrome */
11442     {
11443       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11444           !mng_info->write_png48 && !mng_info->write_png64 &&
11445           !mng_info->write_png32) && (image_matte != MagickFalse ||
11446           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11447           (mng_info->IsPalette) && ping_have_color == MagickFalse)
11448         {
11449           register const Quantum
11450             *p;
11451
11452           for (pass=0; pass < num_passes; pass++)
11453           {
11454
11455           for (y=0; y < (ssize_t) image->rows; y++)
11456           {
11457             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11458
11459             if (p == (const Quantum *) NULL)
11460               break;
11461
11462             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11463               {
11464                 if (mng_info->IsPalette)
11465                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11466                     quantum_info,GrayQuantum,ping_pixels,exception);
11467
11468                 else
11469                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11470                     quantum_info,RedQuantum,ping_pixels,exception);
11471
11472                 if (logging != MagickFalse && y == 0)
11473                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11474                        "    Writing GRAY PNG pixels (2)");
11475               }
11476
11477             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11478               {
11479                 if (logging != MagickFalse && y == 0)
11480                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11481                          "    Writing GRAY_ALPHA PNG pixels (2)");
11482
11483                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11484                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11485               }
11486
11487             if (logging != MagickFalse && y == 0)
11488               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11489                   "    Writing row of pixels (2)");
11490
11491             png_write_row(ping,ping_pixels);
11492
11493             status=SetImageProgress(image,SaveImageTag,
11494               (MagickOffsetType) (pass * image->rows + y),
11495               num_passes * image->rows);
11496
11497             if (status == MagickFalse)
11498               break;
11499             }
11500           }
11501         }
11502
11503       else
11504         {
11505           register const Quantum
11506             *p;
11507
11508           for (pass=0; pass < num_passes; pass++)
11509           {
11510             if ((image_depth > 8) ||
11511                 mng_info->write_png24 ||
11512                 mng_info->write_png32 ||
11513                 mng_info->write_png48 ||
11514                 mng_info->write_png64 ||
11515                 (!mng_info->write_png8 && !mng_info->IsPalette))
11516             {
11517               for (y=0; y < (ssize_t) image->rows; y++)
11518               {
11519                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11520
11521                 if (p == (const Quantum *) NULL)
11522                   break;
11523
11524                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11525                   {
11526                     if (image->storage_class == DirectClass)
11527                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11528                         quantum_info,RedQuantum,ping_pixels,exception);
11529
11530                     else
11531                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11532                         quantum_info,GrayQuantum,ping_pixels,exception);
11533                   }
11534
11535                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11536                   {
11537                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11538                       quantum_info,GrayAlphaQuantum,ping_pixels,
11539                       exception);
11540
11541                     if (logging != MagickFalse && y == 0)
11542                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11543                            "    Writing GRAY_ALPHA PNG pixels (3)");
11544                   }
11545
11546                 else if (image_matte != MagickFalse)
11547                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11548                     quantum_info,RGBAQuantum,ping_pixels,exception);
11549
11550                 else
11551                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11552                     quantum_info,RGBQuantum,ping_pixels,exception);
11553
11554                 if (logging != MagickFalse && y == 0)
11555                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11556                       "    Writing row of pixels (3)");
11557
11558                 png_write_row(ping,ping_pixels);
11559
11560                 status=SetImageProgress(image,SaveImageTag,
11561                   (MagickOffsetType) (pass * image->rows + y),
11562                   num_passes * image->rows);
11563
11564                 if (status == MagickFalse)
11565                   break;
11566               }
11567             }
11568
11569           else
11570             /* not ((image_depth > 8) ||
11571                 mng_info->write_png24 || mng_info->write_png32 ||
11572                 mng_info->write_png48 || mng_info->write_png64 ||
11573                 (!mng_info->write_png8 && !mng_info->IsPalette))
11574              */
11575             {
11576               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11577                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11578                 {
11579                   if (logging != MagickFalse)
11580                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11581                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11582
11583                   SetQuantumDepth(image,quantum_info,8);
11584                   image_depth=8;
11585                 }
11586
11587               for (y=0; y < (ssize_t) image->rows; y++)
11588               {
11589                 if (logging != MagickFalse && y == 0)
11590                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11591                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
11592                     pass);
11593
11594                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11595
11596                 if (p == (const Quantum *) NULL)
11597                   break;
11598
11599                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11600                   {
11601                     SetQuantumDepth(image,quantum_info,image->depth);
11602
11603                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11604                        quantum_info,GrayQuantum,ping_pixels,exception);
11605                   }
11606
11607                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11608                   {
11609                     if (logging != MagickFalse && y == 0)
11610                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11611                            "  Writing GRAY_ALPHA PNG pixels (4)");
11612
11613                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11614                          quantum_info,GrayAlphaQuantum,ping_pixels,
11615                          exception);
11616                   }
11617
11618                 else
11619                   {
11620                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11621                       quantum_info,IndexQuantum,ping_pixels,exception);
11622
11623                     if (logging != MagickFalse && y <= 2)
11624                     {
11625                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11626                           "  Writing row of non-gray pixels (4)");
11627
11628                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11629                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
11630                           (int)ping_pixels[0],(int)ping_pixels[1]);
11631                     }
11632                   }
11633                 png_write_row(ping,ping_pixels);
11634
11635                 status=SetImageProgress(image,SaveImageTag,
11636                   (MagickOffsetType) (pass * image->rows + y),
11637                   num_passes * image->rows);
11638
11639                 if (status == MagickFalse)
11640                   break;
11641               }
11642             }
11643           }
11644         }
11645     }
11646
11647   if (quantum_info != (QuantumInfo *) NULL)
11648     quantum_info=DestroyQuantumInfo(quantum_info);
11649
11650   if (logging != MagickFalse)
11651     {
11652       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11653         "  Wrote PNG image data");
11654
11655       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11656         "    Width: %.20g",(double) ping_width);
11657
11658       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11659         "    Height: %.20g",(double) ping_height);
11660
11661       if (mng_info->write_png_depth)
11662         {
11663           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11664             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
11665         }
11666
11667       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11668         "    PNG bit-depth written: %d",ping_bit_depth);
11669
11670       if (mng_info->write_png_colortype)
11671         {
11672           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11673             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
11674         }
11675
11676       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11677         "    PNG color-type written: %d",ping_color_type);
11678
11679       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11680         "    PNG Interlace method: %d",ping_interlace_method);
11681     }
11682   /*
11683     Generate text chunks after IDAT.
11684   */
11685   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11686   {
11687     ResetImagePropertyIterator(image);
11688     property=GetNextImageProperty(image);
11689     while (property != (const char *) NULL)
11690     {
11691       png_textp
11692         text;
11693
11694       value=GetImageProperty(image,property,exception);
11695
11696       /* Don't write any "png:" or "jpeg:" properties; those are just for
11697        * "identify" or for passing through to another JPEG
11698        */
11699       if ((LocaleNCompare(property,"png:",4) != 0 &&
11700            LocaleNCompare(property,"jpeg:",5) != 0) &&
11701
11702
11703           /* Suppress density and units if we wrote a pHYs chunk */
11704           (ping_exclude_pHYs != MagickFalse      ||
11705           LocaleCompare(property,"density") != 0 ||
11706           LocaleCompare(property,"units") != 0) &&
11707
11708           /* Suppress the IM-generated Date:create and Date:modify */
11709           (ping_exclude_date == MagickFalse      ||
11710           LocaleNCompare(property, "Date:",5) != 0))
11711         {
11712         if (value != (const char *) NULL)
11713           {
11714
11715 #if PNG_LIBPNG_VER >= 10400
11716             text=(png_textp) png_malloc(ping,
11717                  (png_alloc_size_t) sizeof(png_text));
11718 #else
11719             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11720 #endif
11721             text[0].key=(char *) property;
11722             text[0].text=(char *) value;
11723             text[0].text_length=strlen(value);
11724
11725             if (ping_exclude_tEXt != MagickFalse)
11726                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11727
11728             else if (ping_exclude_zTXt != MagickFalse)
11729                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11730
11731             else
11732             {
11733                text[0].compression=image_info->compression == NoCompression ||
11734                  (image_info->compression == UndefinedCompression &&
11735                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11736                  PNG_TEXT_COMPRESSION_zTXt ;
11737             }
11738
11739             if (logging != MagickFalse)
11740               {
11741                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11742                   "  Setting up text chunk");
11743
11744                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11745                   "    keyword: '%s'",text[0].key);
11746               }
11747
11748             png_set_text(ping,ping_info,text,1);
11749             png_free(ping,text);
11750           }
11751         }
11752       property=GetNextImageProperty(image);
11753     }
11754   }
11755
11756   /* write eXIf profile */
11757   if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
11758     {
11759       ResetImageProfileIterator(image);
11760
11761       for (name=GetNextImageProfile(image); name != (char *) NULL; )
11762       {
11763         if (LocaleCompare(name,"exif") == 0)
11764           {
11765             profile=GetImageProfile(image,name);
11766
11767             if (profile != (StringInfo *) NULL)
11768               {
11769                 png_uint_32
11770                   length;
11771
11772                 unsigned char
11773                   chunk[4],
11774                   *data;
11775
11776                 StringInfo
11777                   *ping_profile;
11778
11779                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11780                     "  Have eXIf profile");
11781
11782                 ping_profile=CloneStringInfo(profile);
11783                 data=GetStringInfoDatum(ping_profile),
11784                 length=(png_uint_32) GetStringInfoLength(ping_profile);
11785
11786                 PNGType(chunk,mng_eXIf);
11787                 if (length < 7)
11788                   {
11789                     ping_profile=DestroyStringInfo(ping_profile);
11790                     break;  /* otherwise crashes */
11791                   }
11792
11793                 if (*data == 'E' && *(data+1) == 'x' && *(data+2) == 'i' &&
11794                     *(data+3) == 'f' && *(data+4) == '\0' && *(data+5) == '\0')
11795                   {
11796                     /* skip the "Exif\0\0" JFIF Exif Header ID */
11797                     length -= 6;
11798                     data += 6;
11799                   }
11800
11801                 LogPNGChunk(logging,chunk,length);
11802                 (void) WriteBlobMSBULong(image,length);
11803                 (void) WriteBlob(image,4,chunk);
11804                 (void) WriteBlob(image,length,data);
11805                 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4), data,
11806                   (uInt) length));
11807                 ping_profile=DestroyStringInfo(ping_profile);
11808                 break;
11809              }
11810          }
11811        name=GetNextImageProfile(image);
11812      }
11813   }
11814
11815   if (logging != MagickFalse)
11816     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11817       "  Writing PNG end info");
11818
11819   png_write_end(ping,ping_info);
11820
11821   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11822     {
11823       if (mng_info->page.x || mng_info->page.y ||
11824           (ping_width != mng_info->page.width) ||
11825           (ping_height != mng_info->page.height))
11826         {
11827           unsigned char
11828             chunk[32];
11829
11830           /*
11831             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11832           */
11833           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11834           PNGType(chunk,mng_FRAM);
11835           LogPNGChunk(logging,mng_FRAM,27L);
11836           chunk[4]=4;
11837           chunk[5]=0;  /* frame name separator (no name) */
11838           chunk[6]=1;  /* flag for changing delay, for next frame only */
11839           chunk[7]=0;  /* flag for changing frame timeout */
11840           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11841           chunk[9]=0;  /* flag for changing frame sync_id */
11842           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11843           chunk[14]=0; /* clipping boundaries delta type */
11844           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11845           PNGLong(chunk+19,
11846              (png_uint_32) (mng_info->page.x + ping_width));
11847           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11848           PNGLong(chunk+27,
11849              (png_uint_32) (mng_info->page.y + ping_height));
11850           (void) WriteBlob(image,31,chunk);
11851           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11852           mng_info->old_framing_mode=4;
11853           mng_info->framing_mode=1;
11854         }
11855
11856       else
11857         mng_info->framing_mode=3;
11858     }
11859   if (mng_info->write_mng && !mng_info->need_fram &&
11860       ((int) image->dispose == 3))
11861      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11862
11863   /*
11864     Free PNG resources.
11865   */
11866
11867   png_destroy_write_struct(&ping,&ping_info);
11868
11869   pixel_info=RelinquishVirtualMemory(pixel_info);
11870
11871   if (ping_have_blob != MagickFalse)
11872      (void) CloseBlob(image);
11873
11874   image_info=DestroyImageInfo(image_info);
11875   image=DestroyImage(image);
11876
11877   /* Store bit depth actually written */
11878   s[0]=(char) ping_bit_depth;
11879   s[1]='\0';
11880
11881   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11882
11883   if (logging != MagickFalse)
11884     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11885       "  exit WriteOnePNGImage()");
11886
11887 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11888   UnlockSemaphoreInfo(ping_semaphore);
11889 #endif
11890
11891    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11892     *    Throwing an Exception when an error occurs.
11893     */
11894
11895   return(MagickTrue);
11896 /*  End write one PNG image */
11897
11898 }
11899
11900 /*
11901 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11902 %                                                                             %
11903 %                                                                             %
11904 %                                                                             %
11905 %   W r i t e P N G I m a g e                                                 %
11906 %                                                                             %
11907 %                                                                             %
11908 %                                                                             %
11909 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11910 %
11911 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11912 %  Multiple-image Network Graphics (MNG) image file.
11913 %
11914 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11915 %
11916 %  The format of the WritePNGImage method is:
11917 %
11918 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11919 %        Image *image,ExceptionInfo *exception)
11920 %
11921 %  A description of each parameter follows:
11922 %
11923 %    o image_info: the image info.
11924 %
11925 %    o image:  The image.
11926 %
11927 %    o exception: return any errors or warnings in this structure.
11928 %
11929 %  Returns MagickTrue on success, MagickFalse on failure.
11930 %
11931 %  Communicating with the PNG encoder:
11932 %
11933 %  While the datastream written is always in PNG format and normally would
11934 %  be given the "png" file extension, this method also writes the following
11935 %  pseudo-formats which are subsets of png:
11936 %
11937 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11938 %               a depth greater than 8, the depth is reduced. If transparency
11939 %               is present, the tRNS chunk must only have values 0 and 255
11940 %               (i.e., transparency is binary: fully opaque or fully
11941 %               transparent).  If other values are present they will be
11942 %               50%-thresholded to binary transparency.  If more than 256
11943 %               colors are present, they will be quantized to the 4-4-4-1,
11944 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11945 %               of any resulting fully-transparent pixels is changed to
11946 %               the image's background color.
11947 %
11948 %               If you want better quantization or dithering of the colors
11949 %               or alpha than that, you need to do it before calling the
11950 %               PNG encoder. The pixels contain 8-bit indices even if
11951 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11952 %               images will be written as indexed PNG files even though the
11953 %               PNG grayscale type might be slightly more efficient.  Please
11954 %               note that writing to the PNG8 format may result in loss
11955 %               of color and alpha data.
11956 %
11957 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11958 %               chunk can be present to convey binary transparency by naming
11959 %               one of the colors as transparent.  The only loss incurred
11960 %               is reduction of sample depth to 8.  If the image has more
11961 %               than one transparent color, has semitransparent pixels, or
11962 %               has an opaque pixel with the same RGB components as the
11963 %               transparent color, an image is not written.
11964 %
11965 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
11966 %               transparency is permitted, i.e., the alpha sample for
11967 %               each pixel can have any value from 0 to 255. The alpha
11968 %               channel is present even if the image is fully opaque.
11969 %               The only loss in data is the reduction of the sample depth
11970 %               to 8.
11971 %
11972 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
11973 %               chunk can be present to convey binary transparency by naming
11974 %               one of the colors as transparent.  If the image has more
11975 %               than one transparent color, has semitransparent pixels, or
11976 %               has an opaque pixel with the same RGB components as the
11977 %               transparent color, an image is not written.
11978 %
11979 %    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
11980 %               transparency is permitted, i.e., the alpha sample for
11981 %               each pixel can have any value from 0 to 65535. The alpha
11982 %               channel is present even if the image is fully opaque.
11983 %
11984 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
11985 %               image, if the input was a PNG, is written.  If these values
11986 %               cannot be found, or if the pixels have been changed in a way
11987 %               that makes this impossible, then "PNG00" falls back to the
11988 %               regular "PNG" format.
11989 %
11990 %    o -define: For more precise control of the PNG output, you can use the
11991 %               Image options "png:bit-depth" and "png:color-type".  These
11992 %               can be set from the commandline with "-define" and also
11993 %               from the application programming interfaces.  The options
11994 %               are case-independent and are converted to lowercase before
11995 %               being passed to this encoder.
11996 %
11997 %               png:color-type can be 0, 2, 3, 4, or 6.
11998 %
11999 %               When png:color-type is 0 (Grayscale), png:bit-depth can
12000 %               be 1, 2, 4, 8, or 16.
12001 %
12002 %               When png:color-type is 2 (RGB), png:bit-depth can
12003 %               be 8 or 16.
12004 %
12005 %               When png:color-type is 3 (Indexed), png:bit-depth can
12006 %               be 1, 2, 4, or 8.  This refers to the number of bits
12007 %               used to store the index.  The color samples always have
12008 %               bit-depth 8 in indexed PNG files.
12009 %
12010 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
12011 %               png:bit-depth can be 8 or 16.
12012 %
12013 %               If the image cannot be written without loss with the
12014 %               requested bit-depth and color-type, a PNG file will not
12015 %               be written, a warning will be issued, and the encoder will
12016 %               return MagickFalse.
12017 %
12018 %  Since image encoders should not be responsible for the "heavy lifting",
12019 %  the user should make sure that ImageMagick has already reduced the
12020 %  image depth and number of colors and limit transparency to binary
12021 %  transparency prior to attempting to write the image with depth, color,
12022 %  or transparency limitations.
12023 %
12024 %  Note that another definition, "png:bit-depth-written" exists, but it
12025 %  is not intended for external use.  It is only used internally by the
12026 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
12027 %
12028 %  As of version 6.6.6 the following optimizations are always done:
12029 %
12030 %   o  32-bit depth is reduced to 16.
12031 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
12032 %      high byte and low byte are identical.
12033 %   o  Palette is sorted to remove unused entries and to put a
12034 %      transparent color first, if BUILD_PNG_PALETTE is defined.
12035 %   o  Opaque matte channel is removed when writing an indexed PNG.
12036 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
12037 %      this can be done without loss and a larger bit depth N was not
12038 %      requested via the "-define png:bit-depth=N" option.
12039 %   o  If matte channel is present but only one transparent color is
12040 %      present, RGB+tRNS is written instead of RGBA
12041 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
12042 %      was requested when converting an opaque image).
12043 %
12044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12045 */
12046 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
12047   Image *image,ExceptionInfo *exception)
12048 {
12049   MagickBooleanType
12050     excluding,
12051     logging,
12052     status;
12053
12054   MngInfo
12055     *mng_info;
12056
12057   const char
12058     *value;
12059
12060   int
12061     source;
12062
12063   /*
12064     Open image file.
12065   */
12066   assert(image_info != (const ImageInfo *) NULL);
12067   assert(image_info->signature == MagickCoreSignature);
12068   assert(image != (Image *) NULL);
12069   assert(image->signature == MagickCoreSignature);
12070   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12071   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
12072   /*
12073     Allocate a MngInfo structure.
12074   */
12075   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12076
12077   if (mng_info == (MngInfo *) NULL)
12078     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12079
12080   /*
12081     Initialize members of the MngInfo structure.
12082   */
12083   (void) memset(mng_info,0,sizeof(MngInfo));
12084   mng_info->image=image;
12085   mng_info->equal_backgrounds=MagickTrue;
12086
12087   /* See if user has requested a specific PNG subformat */
12088
12089   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12090   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12091   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12092   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
12093   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
12094
12095   value=GetImageOption(image_info,"png:format");
12096
12097   if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
12098     {
12099       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12100          "  Format=%s",value);
12101
12102       mng_info->write_png8 = MagickFalse;
12103       mng_info->write_png24 = MagickFalse;
12104       mng_info->write_png32 = MagickFalse;
12105       mng_info->write_png48 = MagickFalse;
12106       mng_info->write_png64 = MagickFalse;
12107
12108       if (LocaleCompare(value,"png8") == 0)
12109         mng_info->write_png8 = MagickTrue;
12110
12111       else if (LocaleCompare(value,"png24") == 0)
12112         mng_info->write_png24 = MagickTrue;
12113
12114       else if (LocaleCompare(value,"png32") == 0)
12115         mng_info->write_png32 = MagickTrue;
12116
12117       else if (LocaleCompare(value,"png48") == 0)
12118         mng_info->write_png48 = MagickTrue;
12119
12120       else if (LocaleCompare(value,"png64") == 0)
12121         mng_info->write_png64 = MagickTrue;
12122
12123       else if ((LocaleCompare(value,"png00") == 0) ||
12124          LocaleCompare(image_info->magick,"PNG00") == 0)
12125         {
12126           /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
12127           value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
12128
12129           if (value != (char *) NULL)
12130             {
12131               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12132                  "  png00 inherited bit depth=%s",value);
12133
12134               if (LocaleCompare(value,"1") == 0)
12135                 mng_info->write_png_depth = 1;
12136
12137               else if (LocaleCompare(value,"2") == 0)
12138                 mng_info->write_png_depth = 2;
12139
12140               else if (LocaleCompare(value,"4") == 0)
12141                 mng_info->write_png_depth = 4;
12142
12143               else if (LocaleCompare(value,"8") == 0)
12144                 mng_info->write_png_depth = 8;
12145
12146               else if (LocaleCompare(value,"16") == 0)
12147                 mng_info->write_png_depth = 16;
12148             }
12149
12150           value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
12151
12152           if (value != (char *) NULL)
12153             {
12154               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12155                  "  png00 inherited color type=%s",value);
12156
12157               if (LocaleCompare(value,"0") == 0)
12158                 mng_info->write_png_colortype = 1;
12159
12160               else if (LocaleCompare(value,"2") == 0)
12161                 mng_info->write_png_colortype = 3;
12162
12163               else if (LocaleCompare(value,"3") == 0)
12164                 mng_info->write_png_colortype = 4;
12165
12166               else if (LocaleCompare(value,"4") == 0)
12167                 mng_info->write_png_colortype = 5;
12168
12169               else if (LocaleCompare(value,"6") == 0)
12170                 mng_info->write_png_colortype = 7;
12171             }
12172         }
12173     }
12174
12175   if (mng_info->write_png8)
12176     {
12177       mng_info->write_png_colortype = /* 3 */ 4;
12178       mng_info->write_png_depth = 8;
12179       image->depth = 8;
12180     }
12181
12182   if (mng_info->write_png24)
12183     {
12184       mng_info->write_png_colortype = /* 2 */ 3;
12185       mng_info->write_png_depth = 8;
12186       image->depth = 8;
12187
12188       if (image->alpha_trait != UndefinedPixelTrait)
12189         (void) SetImageType(image,TrueColorAlphaType,exception);
12190
12191       else
12192         (void) SetImageType(image,TrueColorType,exception);
12193
12194       (void) SyncImage(image,exception);
12195     }
12196
12197   if (mng_info->write_png32)
12198     {
12199       mng_info->write_png_colortype = /* 6 */  7;
12200       mng_info->write_png_depth = 8;
12201       image->depth = 8;
12202       image->alpha_trait = BlendPixelTrait;
12203
12204       (void) SetImageType(image,TrueColorAlphaType,exception);
12205       (void) SyncImage(image,exception);
12206     }
12207
12208   if (mng_info->write_png48)
12209     {
12210       mng_info->write_png_colortype = /* 2 */ 3;
12211       mng_info->write_png_depth = 16;
12212       image->depth = 16;
12213
12214       if (image->alpha_trait != UndefinedPixelTrait)
12215         (void) SetImageType(image,TrueColorAlphaType,exception);
12216
12217       else
12218         (void) SetImageType(image,TrueColorType,exception);
12219
12220       (void) SyncImage(image,exception);
12221     }
12222
12223   if (mng_info->write_png64)
12224     {
12225       mng_info->write_png_colortype = /* 6 */  7;
12226       mng_info->write_png_depth = 16;
12227       image->depth = 16;
12228       image->alpha_trait = BlendPixelTrait;
12229
12230       (void) SetImageType(image,TrueColorAlphaType,exception);
12231       (void) SyncImage(image,exception);
12232     }
12233
12234   value=GetImageOption(image_info,"png:bit-depth");
12235
12236   if (value != (char *) NULL)
12237     {
12238       if (LocaleCompare(value,"1") == 0)
12239         mng_info->write_png_depth = 1;
12240
12241       else if (LocaleCompare(value,"2") == 0)
12242         mng_info->write_png_depth = 2;
12243
12244       else if (LocaleCompare(value,"4") == 0)
12245         mng_info->write_png_depth = 4;
12246
12247       else if (LocaleCompare(value,"8") == 0)
12248         mng_info->write_png_depth = 8;
12249
12250       else if (LocaleCompare(value,"16") == 0)
12251         mng_info->write_png_depth = 16;
12252
12253       else
12254         (void) ThrowMagickException(exception,
12255              GetMagickModule(),CoderWarning,
12256              "ignoring invalid defined png:bit-depth",
12257              "=%s",value);
12258
12259       if (logging != MagickFalse)
12260         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12261           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
12262     }
12263
12264   value=GetImageOption(image_info,"png:color-type");
12265
12266   if (value != (char *) NULL)
12267     {
12268       /* We must store colortype+1 because 0 is a valid colortype */
12269       if (LocaleCompare(value,"0") == 0)
12270         mng_info->write_png_colortype = 1;
12271
12272       else if (LocaleCompare(value,"1") == 0)
12273         mng_info->write_png_colortype = 2;
12274
12275       else if (LocaleCompare(value,"2") == 0)
12276         mng_info->write_png_colortype = 3;
12277
12278       else if (LocaleCompare(value,"3") == 0)
12279         mng_info->write_png_colortype = 4;
12280
12281       else if (LocaleCompare(value,"4") == 0)
12282         mng_info->write_png_colortype = 5;
12283
12284       else if (LocaleCompare(value,"6") == 0)
12285         mng_info->write_png_colortype = 7;
12286
12287       else
12288         (void) ThrowMagickException(exception,
12289              GetMagickModule(),CoderWarning,
12290              "ignoring invalid defined png:color-type",
12291              "=%s",value);
12292
12293       if (logging != MagickFalse)
12294         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12295           "  png:color-type=%d was defined.\n",
12296           mng_info->write_png_colortype-1);
12297     }
12298
12299   /* Check for chunks to be excluded:
12300    *
12301    * The default is to not exclude any known chunks except for any
12302    * listed in the "unused_chunks" array, above.
12303    *
12304    * Chunks can be listed for exclusion via a "png:exclude-chunk"
12305    * define (in the image properties or in the image artifacts)
12306    * or via a mng_info member.  For convenience, in addition
12307    * to or instead of a comma-separated list of chunks, the
12308    * "exclude-chunk" string can be simply "all" or "none".
12309    *
12310    * Note that the "-strip" option provides a convenient way of
12311    * doing the equivalent of
12312    *
12313    *    -define png:exclude-chunk="bKGD,caNv,cHRM,eXIf,gAMA,iCCP,
12314    *            iTXt,pHYs,sRGB,tEXt,zCCP,zTXt,date"
12315    *
12316    * The exclude-chunk define takes priority over the mng_info.
12317    *
12318    * A "png:include-chunk" define takes  priority over both the
12319    * mng_info and the "png:exclude-chunk" define.  Like the
12320    * "exclude-chunk" string, it can define "all" or "none" as
12321    * well as a comma-separated list.  Chunks that are unknown to
12322    * ImageMagick are always excluded, regardless of their "copy-safe"
12323    * status according to the PNG specification, and even if they
12324    * appear in the "include-chunk" list. Such defines appearing among
12325    * the image options take priority over those found among the image
12326    * artifacts.
12327    *
12328    * Finally, all chunks listed in the "unused_chunks" array are
12329    * automatically excluded, regardless of the other instructions
12330    * or lack thereof.
12331    *
12332    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
12333    * will not be written and the gAMA chunk will only be written if it
12334    * is not between .45 and .46, or approximately (1.0/2.2).
12335    *
12336    * If you exclude tRNS and the image has transparency, the colortype
12337    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
12338    *
12339    * The -strip option causes StripImage() to set the png:include-chunk
12340    * artifact to "none,trns,gama".
12341    */
12342
12343   mng_info->ping_exclude_bKGD=MagickFalse;
12344   mng_info->ping_exclude_caNv=MagickFalse;
12345   mng_info->ping_exclude_cHRM=MagickFalse;
12346   mng_info->ping_exclude_date=MagickFalse;
12347   mng_info->ping_exclude_eXIf=MagickFalse;
12348   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
12349   mng_info->ping_exclude_gAMA=MagickFalse;
12350   mng_info->ping_exclude_iCCP=MagickFalse;
12351   /* mng_info->ping_exclude_iTXt=MagickFalse; */
12352   mng_info->ping_exclude_oFFs=MagickFalse;
12353   mng_info->ping_exclude_pHYs=MagickFalse;
12354   mng_info->ping_exclude_sRGB=MagickFalse;
12355   mng_info->ping_exclude_tEXt=MagickFalse;
12356   mng_info->ping_exclude_tIME=MagickFalse;
12357   mng_info->ping_exclude_tRNS=MagickFalse;
12358   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
12359   mng_info->ping_exclude_zTXt=MagickFalse;
12360
12361   mng_info->ping_preserve_colormap=MagickFalse;
12362
12363   value=GetImageOption(image_info,"png:preserve-colormap");
12364   if (value == NULL)
12365      value=GetImageArtifact(image,"png:preserve-colormap");
12366   if (value != NULL)
12367      mng_info->ping_preserve_colormap=MagickTrue;
12368
12369   mng_info->ping_preserve_iCCP=MagickFalse;
12370
12371   value=GetImageOption(image_info,"png:preserve-iCCP");
12372   if (value == NULL)
12373      value=GetImageArtifact(image,"png:preserve-iCCP");
12374   if (value != NULL)
12375      mng_info->ping_preserve_iCCP=MagickTrue;
12376
12377   /* These compression-level, compression-strategy, and compression-filter
12378    * defines take precedence over values from the -quality option.
12379    */
12380   value=GetImageOption(image_info,"png:compression-level");
12381   if (value == NULL)
12382      value=GetImageArtifact(image,"png:compression-level");
12383   if (value != NULL)
12384   {
12385       /* We have to add 1 to everything because 0 is a valid input,
12386        * and we want to use 0 (the default) to mean undefined.
12387        */
12388       if (LocaleCompare(value,"0") == 0)
12389         mng_info->write_png_compression_level = 1;
12390
12391       else if (LocaleCompare(value,"1") == 0)
12392         mng_info->write_png_compression_level = 2;
12393
12394       else if (LocaleCompare(value,"2") == 0)
12395         mng_info->write_png_compression_level = 3;
12396
12397       else if (LocaleCompare(value,"3") == 0)
12398         mng_info->write_png_compression_level = 4;
12399
12400       else if (LocaleCompare(value,"4") == 0)
12401         mng_info->write_png_compression_level = 5;
12402
12403       else if (LocaleCompare(value,"5") == 0)
12404         mng_info->write_png_compression_level = 6;
12405
12406       else if (LocaleCompare(value,"6") == 0)
12407         mng_info->write_png_compression_level = 7;
12408
12409       else if (LocaleCompare(value,"7") == 0)
12410         mng_info->write_png_compression_level = 8;
12411
12412       else if (LocaleCompare(value,"8") == 0)
12413         mng_info->write_png_compression_level = 9;
12414
12415       else if (LocaleCompare(value,"9") == 0)
12416         mng_info->write_png_compression_level = 10;
12417
12418       else
12419         (void) ThrowMagickException(exception,
12420              GetMagickModule(),CoderWarning,
12421              "ignoring invalid defined png:compression-level",
12422              "=%s",value);
12423     }
12424
12425   value=GetImageOption(image_info,"png:compression-strategy");
12426   if (value == NULL)
12427      value=GetImageArtifact(image,"png:compression-strategy");
12428   if (value != NULL)
12429   {
12430       if (LocaleCompare(value,"0") == 0)
12431         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12432
12433       else if (LocaleCompare(value,"1") == 0)
12434         mng_info->write_png_compression_strategy = Z_FILTERED+1;
12435
12436       else if (LocaleCompare(value,"2") == 0)
12437         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
12438
12439       else if (LocaleCompare(value,"3") == 0)
12440 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
12441         mng_info->write_png_compression_strategy = Z_RLE+1;
12442 #else
12443         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12444 #endif
12445
12446       else if (LocaleCompare(value,"4") == 0)
12447 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
12448         mng_info->write_png_compression_strategy = Z_FIXED+1;
12449 #else
12450         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12451 #endif
12452
12453       else
12454         (void) ThrowMagickException(exception,
12455              GetMagickModule(),CoderWarning,
12456              "ignoring invalid defined png:compression-strategy",
12457              "=%s",value);
12458     }
12459
12460   value=GetImageOption(image_info,"png:compression-filter");
12461   if (value == NULL)
12462      value=GetImageArtifact(image,"png:compression-filter");
12463   if (value != NULL)
12464   {
12465       /* To do: combinations of filters allowed by libpng
12466        * masks 0x08 through 0xf8
12467        *
12468        * Implement this as a comma-separated list of 0,1,2,3,4,5
12469        * where 5 is a special case meaning PNG_ALL_FILTERS.
12470        */
12471
12472       if (LocaleCompare(value,"0") == 0)
12473         mng_info->write_png_compression_filter = 1;
12474
12475       else if (LocaleCompare(value,"1") == 0)
12476         mng_info->write_png_compression_filter = 2;
12477
12478       else if (LocaleCompare(value,"2") == 0)
12479         mng_info->write_png_compression_filter = 3;
12480
12481       else if (LocaleCompare(value,"3") == 0)
12482         mng_info->write_png_compression_filter = 4;
12483
12484       else if (LocaleCompare(value,"4") == 0)
12485         mng_info->write_png_compression_filter = 5;
12486
12487       else if (LocaleCompare(value,"5") == 0)
12488         mng_info->write_png_compression_filter = 6;
12489
12490       else
12491         (void) ThrowMagickException(exception,
12492              GetMagickModule(),CoderWarning,
12493              "ignoring invalid defined png:compression-filter",
12494              "=%s",value);
12495   }
12496
12497   for (source=0; source<8; source++)
12498   {
12499     value = NULL;
12500
12501     if (source == 0)
12502       value=GetImageOption(image_info,"png:exclude-chunks");
12503
12504     if (source == 1)
12505       value=GetImageArtifact(image,"png:exclude-chunks");
12506
12507     if (source == 2)
12508       value=GetImageOption(image_info,"png:exclude-chunk");
12509
12510     if (source == 3)
12511       value=GetImageArtifact(image,"png:exclude-chunk");
12512
12513     if (source == 4)
12514       value=GetImageOption(image_info,"png:include-chunks");
12515
12516     if (source == 5)
12517       value=GetImageArtifact(image,"png:include-chunks");
12518
12519     if (source == 6)
12520       value=GetImageOption(image_info,"png:include-chunk");
12521
12522     if (source == 7)
12523       value=GetImageArtifact(image,"png:include-chunk");
12524
12525     if (value == NULL)
12526        continue;
12527
12528     if (source < 4)
12529       excluding = MagickTrue;
12530     else
12531       excluding = MagickFalse;
12532
12533     if (logging != MagickFalse)
12534       {
12535         if (source == 0 || source == 2)
12536            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12537               "  png:exclude-chunk=%s found in image options.\n", value);
12538         else if (source == 1 || source == 3)
12539            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12540               "  png:exclude-chunk=%s found in image artifacts.\n", value);
12541         else if (source == 4 || source == 6)
12542            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12543               "  png:include-chunk=%s found in image options.\n", value);
12544         else /* if (source == 5 || source == 7) */
12545            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12546               "  png:include-chunk=%s found in image artifacts.\n", value);
12547       }
12548
12549     if (IsOptionMember("all",value) != MagickFalse)
12550       {
12551         mng_info->ping_exclude_bKGD=excluding;
12552         mng_info->ping_exclude_caNv=excluding;
12553         mng_info->ping_exclude_cHRM=excluding;
12554         mng_info->ping_exclude_date=excluding;
12555         mng_info->ping_exclude_EXIF=excluding;
12556         mng_info->ping_exclude_eXIf=excluding;
12557         mng_info->ping_exclude_gAMA=excluding;
12558         mng_info->ping_exclude_iCCP=excluding;
12559         /* mng_info->ping_exclude_iTXt=excluding; */
12560         mng_info->ping_exclude_oFFs=excluding;
12561         mng_info->ping_exclude_pHYs=excluding;
12562         mng_info->ping_exclude_sRGB=excluding;
12563         mng_info->ping_exclude_tEXt=excluding;
12564         mng_info->ping_exclude_tIME=excluding;
12565         mng_info->ping_exclude_tRNS=excluding;
12566         mng_info->ping_exclude_zCCP=excluding;
12567         mng_info->ping_exclude_zTXt=excluding;
12568       }
12569
12570     if (IsOptionMember("none",value) != MagickFalse)
12571       {
12572         mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12573           MagickTrue;
12574         mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
12575           MagickTrue;
12576         mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12577           MagickTrue;
12578         mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12579           MagickTrue;
12580         mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
12581           MagickTrue;
12582         mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12583           MagickTrue;
12584         mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12585           MagickTrue;
12586         mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12587           MagickTrue;
12588         /* mng_info->ping_exclude_iTXt=!excluding; */
12589         mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12590           MagickTrue;
12591         mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12592           MagickTrue;
12593         mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12594           MagickTrue;
12595         mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12596           MagickTrue;
12597         mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12598           MagickTrue;
12599         mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12600           MagickTrue;
12601         mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12602           MagickTrue;
12603         mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12604           MagickTrue;
12605       }
12606
12607     if (IsOptionMember("bkgd",value) != MagickFalse)
12608       mng_info->ping_exclude_bKGD=excluding;
12609
12610     if (IsOptionMember("caNv",value) != MagickFalse)
12611       mng_info->ping_exclude_caNv=excluding;
12612
12613     if (IsOptionMember("chrm",value) != MagickFalse)
12614       mng_info->ping_exclude_cHRM=excluding;
12615
12616     if (IsOptionMember("date",value) != MagickFalse)
12617       mng_info->ping_exclude_date=excluding;
12618
12619     if (IsOptionMember("exif",value) != MagickFalse)
12620       {
12621         mng_info->ping_exclude_EXIF=excluding;
12622         mng_info->ping_exclude_eXIf=excluding;
12623       }
12624
12625     if (IsOptionMember("gama",value) != MagickFalse)
12626       mng_info->ping_exclude_gAMA=excluding;
12627
12628     if (IsOptionMember("iccp",value) != MagickFalse)
12629       mng_info->ping_exclude_iCCP=excluding;
12630
12631 #if 0
12632     if (IsOptionMember("itxt",value) != MagickFalse)
12633       mng_info->ping_exclude_iTXt=excluding;
12634 #endif
12635
12636     if (IsOptionMember("offs",value) != MagickFalse)
12637       mng_info->ping_exclude_oFFs=excluding;
12638
12639     if (IsOptionMember("phys",value) != MagickFalse)
12640       mng_info->ping_exclude_pHYs=excluding;
12641
12642     if (IsOptionMember("srgb",value) != MagickFalse)
12643       mng_info->ping_exclude_sRGB=excluding;
12644
12645     if (IsOptionMember("text",value) != MagickFalse)
12646       mng_info->ping_exclude_tEXt=excluding;
12647
12648     if (IsOptionMember("time",value) != MagickFalse)
12649       mng_info->ping_exclude_tIME=excluding;
12650
12651     if (IsOptionMember("trns",value) != MagickFalse)
12652       mng_info->ping_exclude_tRNS=excluding;
12653
12654     if (IsOptionMember("zccp",value) != MagickFalse)
12655       mng_info->ping_exclude_zCCP=excluding;
12656
12657     if (IsOptionMember("ztxt",value) != MagickFalse)
12658       mng_info->ping_exclude_zTXt=excluding;
12659   }
12660
12661   if (logging != MagickFalse)
12662   {
12663     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12664       "  Chunks to be excluded from the output png:");
12665     if (mng_info->ping_exclude_bKGD != MagickFalse)
12666       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12667           "    bKGD");
12668     if (mng_info->ping_exclude_caNv != MagickFalse)
12669       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12670           "    caNv");
12671     if (mng_info->ping_exclude_cHRM != MagickFalse)
12672       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12673           "    cHRM");
12674     if (mng_info->ping_exclude_date != MagickFalse)
12675       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12676           "    date");
12677     if (mng_info->ping_exclude_EXIF != MagickFalse)
12678       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12679           "    EXIF");
12680     if (mng_info->ping_exclude_eXIf != MagickFalse)
12681       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12682           "    eXIf");
12683     if (mng_info->ping_exclude_gAMA != MagickFalse)
12684       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12685           "    gAMA");
12686     if (mng_info->ping_exclude_iCCP != MagickFalse)
12687       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12688           "    iCCP");
12689 #if 0
12690     if (mng_info->ping_exclude_iTXt != MagickFalse)
12691       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12692           "    iTXt");
12693 #endif
12694
12695     if (mng_info->ping_exclude_oFFs != MagickFalse)
12696       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12697           "    oFFs");
12698     if (mng_info->ping_exclude_pHYs != MagickFalse)
12699       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12700           "    pHYs");
12701     if (mng_info->ping_exclude_sRGB != MagickFalse)
12702       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12703           "    sRGB");
12704     if (mng_info->ping_exclude_tEXt != MagickFalse)
12705       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12706           "    tEXt");
12707     if (mng_info->ping_exclude_tIME != MagickFalse)
12708       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12709           "    tIME");
12710     if (mng_info->ping_exclude_tRNS != MagickFalse)
12711       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12712           "    tRNS");
12713     if (mng_info->ping_exclude_zCCP != MagickFalse)
12714       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12715           "    zCCP");
12716     if (mng_info->ping_exclude_zTXt != MagickFalse)
12717       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12718           "    zTXt");
12719   }
12720
12721   mng_info->need_blob = MagickTrue;
12722
12723   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12724
12725   mng_info=MngInfoFreeStruct(mng_info);
12726
12727   if (logging != MagickFalse)
12728     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12729
12730   return(status);
12731 }
12732
12733 #if defined(JNG_SUPPORTED)
12734
12735 /* Write one JNG image */
12736 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12737    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12738 {
12739   Image
12740     *jpeg_image;
12741
12742   ImageInfo
12743     *jpeg_image_info;
12744
12745   MagickBooleanType
12746     logging,
12747     status;
12748
12749   size_t
12750     length;
12751
12752   unsigned char
12753     *blob,
12754     chunk[80],
12755     *p;
12756
12757   unsigned int
12758     jng_alpha_compression_method,
12759     jng_alpha_sample_depth,
12760     jng_color_type,
12761     transparent;
12762
12763   size_t
12764     jng_alpha_quality,
12765     jng_quality;
12766
12767   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12768     "  Enter WriteOneJNGImage()");
12769
12770   blob=(unsigned char *) NULL;
12771   jpeg_image=(Image *) NULL;
12772   jpeg_image_info=(ImageInfo *) NULL;
12773   length=0;
12774
12775   status=MagickTrue;
12776   transparent=image_info->type==GrayscaleAlphaType ||
12777      image_info->type==TrueColorAlphaType ||
12778      image->alpha_trait != UndefinedPixelTrait;
12779
12780   jng_alpha_sample_depth = 0;
12781
12782   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12783
12784   jng_alpha_compression_method=(image->compression==JPEGCompression ||
12785     image_info->compression==JPEGCompression) ? 8 : 0;
12786
12787   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12788       image_info->quality;
12789
12790   if (jng_alpha_quality >= 1000)
12791     jng_alpha_quality /= 1000;
12792
12793   length=0;
12794
12795   if (transparent != 0)
12796     {
12797       jng_color_type=14;
12798
12799       /* Create JPEG blob, image, and image_info */
12800       if (logging != MagickFalse)
12801         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12802           "  Creating jpeg_image_info for alpha.");
12803
12804       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12805
12806       if (jpeg_image_info == (ImageInfo *) NULL)
12807         {
12808           jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12809           ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12810         }
12811
12812       if (logging != MagickFalse)
12813         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12814           "  Creating jpeg_image.");
12815
12816       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12817       if (jpeg_image == (Image *) NULL)
12818         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12819       (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12820       jpeg_image->alpha_trait=UndefinedPixelTrait;
12821       jpeg_image->quality=jng_alpha_quality;
12822       jpeg_image_info->type=GrayscaleType;
12823       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12824       (void) AcquireUniqueFilename(jpeg_image->filename);
12825       (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12826         "%s",jpeg_image->filename);
12827     }
12828   else
12829     {
12830       jng_alpha_compression_method=0;
12831       jng_color_type=10;
12832       jng_alpha_sample_depth=0;
12833     }
12834
12835   /* To do: check bit depth of PNG alpha channel */
12836
12837   /* Check if image is grayscale. */
12838   if (image_info->type != TrueColorAlphaType && image_info->type !=
12839     TrueColorType && SetImageGray(image,exception))
12840     jng_color_type-=2;
12841
12842   if (logging != MagickFalse)
12843     {
12844         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12845           "    JNG Quality           = %d",(int) jng_quality);
12846         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12847           "    JNG Color Type        = %d",jng_color_type);
12848         if (transparent != 0)
12849           {
12850             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12851               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12852             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12853               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12854             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12855               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12856           }
12857     }
12858
12859   if (transparent != 0)
12860     {
12861       if (jng_alpha_compression_method==0)
12862         {
12863           const char
12864             *value;
12865
12866           /* Encode alpha as a grayscale PNG blob */
12867           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12868             exception);
12869           if (status == MagickFalse)
12870             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12871
12872           if (logging != MagickFalse)
12873             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12874               "  Creating PNG blob.");
12875
12876           (void) CopyMagickString(jpeg_image_info->magick,"PNG",
12877              MagickPathExtent);
12878           (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12879           jpeg_image_info->interlace=NoInterlace;
12880
12881           /* Exclude all ancillary chunks */
12882           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12883
12884           blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12885             &length,exception);
12886
12887           /* Retrieve sample depth used */
12888           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12889           if (value != (char *) NULL)
12890             jng_alpha_sample_depth= (unsigned int) value[0];
12891         }
12892       else
12893         {
12894           /* Encode alpha as a grayscale JPEG blob */
12895
12896           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12897             exception);
12898           if (status == MagickFalse)
12899             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12900
12901           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",
12902             MagickPathExtent);
12903           (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12904           jpeg_image_info->interlace=NoInterlace;
12905           if (logging != MagickFalse)
12906             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12907               "  Creating blob.");
12908           blob=(unsigned char *) ImageToBlob(jpeg_image_info,
12909              jpeg_image,&length,
12910            exception);
12911           jng_alpha_sample_depth=8;
12912
12913           if (logging != MagickFalse)
12914             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12915               "  Successfully read jpeg_image into a blob, length=%.20g.",
12916               (double) length);
12917
12918         }
12919       /* Destroy JPEG image and image_info */
12920       jpeg_image=DestroyImage(jpeg_image);
12921       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12922       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12923     }
12924
12925   /* Write JHDR chunk */
12926   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12927   PNGType(chunk,mng_JHDR);
12928   LogPNGChunk(logging,mng_JHDR,16L);
12929   PNGLong(chunk+4,(png_uint_32) image->columns);
12930   PNGLong(chunk+8,(png_uint_32) image->rows);
12931   chunk[12]=jng_color_type;
12932   chunk[13]=8;  /* sample depth */
12933   chunk[14]=8; /*jng_image_compression_method */
12934   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12935   chunk[16]=jng_alpha_sample_depth;
12936   chunk[17]=jng_alpha_compression_method;
12937   chunk[18]=0; /*jng_alpha_filter_method */
12938   chunk[19]=0; /*jng_alpha_interlace_method */
12939   (void) WriteBlob(image,20,chunk);
12940   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12941   if (logging != MagickFalse)
12942     {
12943       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12944         "    JNG width:%15lu",(unsigned long) image->columns);
12945
12946       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12947         "    JNG height:%14lu",(unsigned long) image->rows);
12948
12949       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12950         "    JNG color type:%10d",jng_color_type);
12951
12952       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12953         "    JNG sample depth:%8d",8);
12954
12955       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12956         "    JNG compression:%9d",8);
12957
12958       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12959         "    JNG interlace:%11d",0);
12960
12961       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12962         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
12963
12964       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12965         "    JNG alpha compression:%3d",jng_alpha_compression_method);
12966
12967       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12968         "    JNG alpha filter:%8d",0);
12969
12970       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12971         "    JNG alpha interlace:%5d",0);
12972     }
12973
12974   /*
12975      Write leading ancillary chunks
12976   */
12977
12978   if (transparent != 0)
12979   {
12980     /*
12981       Write JNG bKGD chunk
12982     */
12983
12984     unsigned char
12985       blue,
12986       green,
12987       red;
12988
12989     ssize_t
12990       num_bytes;
12991
12992     if (jng_color_type == 8 || jng_color_type == 12)
12993       num_bytes=6L;
12994     else
12995       num_bytes=10L;
12996     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12997     PNGType(chunk,mng_bKGD);
12998     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12999     red=ScaleQuantumToChar(image->background_color.red);
13000     green=ScaleQuantumToChar(image->background_color.green);
13001     blue=ScaleQuantumToChar(image->background_color.blue);
13002     *(chunk+4)=0;
13003     *(chunk+5)=red;
13004     *(chunk+6)=0;
13005     *(chunk+7)=green;
13006     *(chunk+8)=0;
13007     *(chunk+9)=blue;
13008     (void) WriteBlob(image,(size_t) num_bytes,chunk);
13009     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
13010   }
13011
13012   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
13013     {
13014       /*
13015         Write JNG sRGB chunk
13016       */
13017       (void) WriteBlobMSBULong(image,1L);
13018       PNGType(chunk,mng_sRGB);
13019       LogPNGChunk(logging,mng_sRGB,1L);
13020
13021       if (image->rendering_intent != UndefinedIntent)
13022         chunk[4]=(unsigned char)
13023           Magick_RenderingIntent_to_PNG_RenderingIntent(
13024           (image->rendering_intent));
13025
13026       else
13027         chunk[4]=(unsigned char)
13028           Magick_RenderingIntent_to_PNG_RenderingIntent(
13029           (PerceptualIntent));
13030
13031       (void) WriteBlob(image,5,chunk);
13032       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13033     }
13034   else
13035     {
13036       if (image->gamma != 0.0)
13037         {
13038           /*
13039              Write JNG gAMA chunk
13040           */
13041           (void) WriteBlobMSBULong(image,4L);
13042           PNGType(chunk,mng_gAMA);
13043           LogPNGChunk(logging,mng_gAMA,4L);
13044           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13045           (void) WriteBlob(image,8,chunk);
13046           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13047         }
13048
13049       if ((mng_info->equal_chrms == MagickFalse) &&
13050           (image->chromaticity.red_primary.x != 0.0))
13051         {
13052           PrimaryInfo
13053             primary;
13054
13055           /*
13056              Write JNG cHRM chunk
13057           */
13058           (void) WriteBlobMSBULong(image,32L);
13059           PNGType(chunk,mng_cHRM);
13060           LogPNGChunk(logging,mng_cHRM,32L);
13061           primary=image->chromaticity.white_point;
13062           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13063           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13064           primary=image->chromaticity.red_primary;
13065           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13066           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13067           primary=image->chromaticity.green_primary;
13068           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13069           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13070           primary=image->chromaticity.blue_primary;
13071           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13072           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13073           (void) WriteBlob(image,36,chunk);
13074           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13075         }
13076     }
13077
13078   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
13079     {
13080       /*
13081          Write JNG pHYs chunk
13082       */
13083       (void) WriteBlobMSBULong(image,9L);
13084       PNGType(chunk,mng_pHYs);
13085       LogPNGChunk(logging,mng_pHYs,9L);
13086       if (image->units == PixelsPerInchResolution)
13087         {
13088           PNGLong(chunk+4,(png_uint_32)
13089             (image->resolution.x*100.0/2.54+0.5));
13090
13091           PNGLong(chunk+8,(png_uint_32)
13092             (image->resolution.y*100.0/2.54+0.5));
13093
13094           chunk[12]=1;
13095         }
13096
13097       else
13098         {
13099           if (image->units == PixelsPerCentimeterResolution)
13100             {
13101               PNGLong(chunk+4,(png_uint_32)
13102                 (image->resolution.x*100.0+0.5));
13103
13104               PNGLong(chunk+8,(png_uint_32)
13105                 (image->resolution.y*100.0+0.5));
13106
13107               chunk[12]=1;
13108             }
13109
13110           else
13111             {
13112               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13113               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13114               chunk[12]=0;
13115             }
13116         }
13117       (void) WriteBlob(image,13,chunk);
13118       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13119     }
13120
13121   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
13122     {
13123       /*
13124          Write JNG oFFs chunk
13125       */
13126       (void) WriteBlobMSBULong(image,9L);
13127       PNGType(chunk,mng_oFFs);
13128       LogPNGChunk(logging,mng_oFFs,9L);
13129       PNGsLong(chunk+4,(ssize_t) (image->page.x));
13130       PNGsLong(chunk+8,(ssize_t) (image->page.y));
13131       chunk[12]=0;
13132       (void) WriteBlob(image,13,chunk);
13133       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13134     }
13135
13136   if (transparent != 0)
13137     {
13138       if (jng_alpha_compression_method==0)
13139         {
13140           register ssize_t
13141             i;
13142
13143           size_t
13144             len;
13145
13146           /* Write IDAT chunk header */
13147           if (logging != MagickFalse)
13148             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13149               "  Write IDAT chunks from blob, length=%.20g.",(double)
13150               length);
13151
13152           /* Copy IDAT chunks */
13153           len=0;
13154           p=blob+8;
13155           for (i=8; i<(ssize_t) length; i+=len+12)
13156           {
13157             len=(((unsigned int) *(p    ) & 0xff) << 24) +
13158                 (((unsigned int) *(p + 1) & 0xff) << 16) +
13159                 (((unsigned int) *(p + 2) & 0xff) <<  8) +
13160                 (((unsigned int) *(p + 3) & 0xff)      ) ;
13161             p+=4;
13162
13163             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
13164               {
13165                 /* Found an IDAT chunk. */
13166                 (void) WriteBlobMSBULong(image,len);
13167                 LogPNGChunk(logging,mng_IDAT,len);
13168                 (void) WriteBlob(image,len+4,p);
13169                 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
13170               }
13171
13172             else
13173               {
13174                 if (logging != MagickFalse)
13175                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13176                     "    Skipping %c%c%c%c chunk, length=%.20g.",
13177                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
13178               }
13179             p+=(8+len);
13180           }
13181         }
13182       else if (length != 0)
13183         {
13184           /* Write JDAA chunk header */
13185           if (logging != MagickFalse)
13186             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13187               "  Write JDAA chunk, length=%.20g.",(double) length);
13188           (void) WriteBlobMSBULong(image,(size_t) length);
13189           PNGType(chunk,mng_JDAA);
13190           LogPNGChunk(logging,mng_JDAA,length);
13191           /* Write JDAT chunk(s) data */
13192           (void) WriteBlob(image,4,chunk);
13193           (void) WriteBlob(image,length,blob);
13194           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
13195              (uInt) length));
13196         }
13197       blob=(unsigned char *) RelinquishMagickMemory(blob);
13198     }
13199
13200   /* Encode image as a JPEG blob */
13201   if (logging != MagickFalse)
13202     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13203       "  Creating jpeg_image_info.");
13204   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
13205   if (jpeg_image_info == (ImageInfo *) NULL)
13206     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13207
13208   if (logging != MagickFalse)
13209     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13210       "  Creating jpeg_image.");
13211
13212   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
13213   if (jpeg_image == (Image *) NULL)
13214     {
13215       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13216       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13217     }
13218   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13219
13220   (void) AcquireUniqueFilename(jpeg_image->filename);
13221   (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
13222     jpeg_image->filename);
13223
13224   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
13225     exception);
13226
13227   if (logging != MagickFalse)
13228     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13229       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
13230       (double) jpeg_image->rows);
13231
13232   if (status == MagickFalse)
13233     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13234
13235   if (jng_color_type == 8 || jng_color_type == 12)
13236     jpeg_image_info->type=GrayscaleType;
13237
13238   jpeg_image_info->quality=jng_quality;
13239   jpeg_image->quality=jng_quality;
13240   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
13241   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13242
13243   if (logging != MagickFalse)
13244     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13245       "  Creating blob.");
13246
13247   blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
13248     exception);
13249
13250   if (logging != MagickFalse)
13251     {
13252       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13253         "  Successfully read jpeg_image into a blob, length=%.20g.",
13254         (double) length);
13255
13256       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13257         "  Write JDAT chunk, length=%.20g.",(double) length);
13258     }
13259
13260   /* Write JDAT chunk(s) */
13261   (void) WriteBlobMSBULong(image,(size_t) length);
13262   PNGType(chunk,mng_JDAT);
13263   LogPNGChunk(logging,mng_JDAT,length);
13264   (void) WriteBlob(image,4,chunk);
13265   (void) WriteBlob(image,length,blob);
13266   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
13267
13268   jpeg_image=DestroyImage(jpeg_image);
13269   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
13270   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13271   blob=(unsigned char *) RelinquishMagickMemory(blob);
13272
13273   /* Write IEND chunk */
13274   (void) WriteBlobMSBULong(image,0L);
13275   PNGType(chunk,mng_IEND);
13276   LogPNGChunk(logging,mng_IEND,0);
13277   (void) WriteBlob(image,4,chunk);
13278   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13279
13280   if (logging != MagickFalse)
13281     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13282       "  exit WriteOneJNGImage()");
13283
13284   return(status);
13285 }
13286
13287 /*
13288 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13289 %                                                                             %
13290 %                                                                             %
13291 %                                                                             %
13292 %   W r i t e J N G I m a g e                                                 %
13293 %                                                                             %
13294 %                                                                             %
13295 %                                                                             %
13296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13297 %
13298 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
13299 %
13300 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
13301 %
13302 %  The format of the WriteJNGImage method is:
13303 %
13304 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13305 %        Image *image,ExceptionInfo *exception)
13306 %
13307 %  A description of each parameter follows:
13308 %
13309 %    o image_info: the image info.
13310 %
13311 %    o image:  The image.
13312 %
13313 %    o exception: return any errors or warnings in this structure.
13314 %
13315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13316 */
13317 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13318   Image *image, ExceptionInfo *exception)
13319 {
13320   MagickBooleanType
13321     logging,
13322     status;
13323
13324   MngInfo
13325     *mng_info;
13326
13327   /*
13328     Open image file.
13329   */
13330   assert(image_info != (const ImageInfo *) NULL);
13331   assert(image_info->signature == MagickCoreSignature);
13332   assert(image != (Image *) NULL);
13333   assert(image->signature == MagickCoreSignature);
13334   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13335   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
13336   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13337   if (status == MagickFalse)
13338     return(status);
13339   if ((image->columns > 65535UL) || (image->rows > 65535UL))
13340     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
13341
13342   /*
13343     Allocate a MngInfo structure.
13344   */
13345   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13346   if (mng_info == (MngInfo *) NULL)
13347     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13348   /*
13349     Initialize members of the MngInfo structure.
13350   */
13351   (void) memset(mng_info,0,sizeof(MngInfo));
13352   mng_info->image=image;
13353
13354   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
13355
13356   status=WriteOneJNGImage(mng_info,image_info,image,exception);
13357   mng_info=MngInfoFreeStruct(mng_info);
13358   (void) CloseBlob(image);
13359
13360   (void) CatchImageException(image);
13361   if (logging != MagickFalse)
13362     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13363   return(status);
13364 }
13365 #endif
13366
13367 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
13368   Image *image, ExceptionInfo *exception)
13369 {
13370   Image
13371     *next_image;
13372
13373   MagickBooleanType
13374     status;
13375
13376   volatile MagickBooleanType
13377     logging;
13378
13379   MngInfo
13380     *mng_info;
13381
13382   int
13383     image_count,
13384     need_iterations,
13385     need_matte;
13386
13387   volatile int
13388 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13389     defined(PNG_MNG_FEATURES_SUPPORTED)
13390     need_local_plte,
13391 #endif
13392     all_images_are_gray,
13393     need_defi,
13394     use_global_plte;
13395
13396   register ssize_t
13397     i;
13398
13399   unsigned char
13400     chunk[800];
13401
13402   volatile unsigned int
13403     write_jng,
13404     write_mng;
13405
13406   volatile size_t
13407     scene;
13408
13409   size_t
13410     final_delay=0,
13411     imageListLength,
13412     initial_delay;
13413
13414 #if (PNG_LIBPNG_VER < 10200)
13415     if (image_info->verbose)
13416       printf("Your PNG library (libpng-%s) is rather old.\n",
13417          PNG_LIBPNG_VER_STRING);
13418 #endif
13419
13420   /*
13421     Open image file.
13422   */
13423   assert(image_info != (const ImageInfo *) NULL);
13424   assert(image_info->signature == MagickCoreSignature);
13425   assert(image != (Image *) NULL);
13426   assert(image->signature == MagickCoreSignature);
13427   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13428   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13429   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13430   if (status == MagickFalse)
13431     return(status);
13432
13433   /*
13434     Allocate a MngInfo structure.
13435   */
13436   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13437   if (mng_info == (MngInfo *) NULL)
13438     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13439   /*
13440     Initialize members of the MngInfo structure.
13441   */
13442   (void) memset(mng_info,0,sizeof(MngInfo));
13443   mng_info->image=image;
13444   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13445
13446   /*
13447    * See if user has requested a specific PNG subformat to be used
13448    * for all of the PNGs in the MNG being written, e.g.,
13449    *
13450    *    convert *.png png8:animation.mng
13451    *
13452    * To do: check -define png:bit_depth and png:color_type as well,
13453    * or perhaps use mng:bit_depth and mng:color_type instead for
13454    * global settings.
13455    */
13456
13457   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13458   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13459   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13460
13461   write_jng=MagickFalse;
13462   if (image_info->compression == JPEGCompression)
13463     write_jng=MagickTrue;
13464
13465   mng_info->adjoin=image_info->adjoin &&
13466     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13467
13468   if (logging != MagickFalse)
13469     {
13470       /* Log some info about the input */
13471       Image
13472         *p;
13473
13474       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13475         "  Checking input image(s)\n"
13476         "    Image_info depth: %.20g,    Type: %d",
13477         (double) image_info->depth, image_info->type);
13478
13479       scene=0;
13480       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13481       {
13482
13483         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13484            "    Scene: %.20g\n,   Image depth: %.20g",
13485            (double) scene++, (double) p->depth);
13486
13487         if (p->alpha_trait != UndefinedPixelTrait)
13488           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13489             "      Matte: True");
13490
13491         else
13492           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13493             "      Matte: False");
13494
13495         if (p->storage_class == PseudoClass)
13496           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13497             "      Storage class: PseudoClass");
13498
13499         else
13500           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13501             "      Storage class: DirectClass");
13502
13503         if (p->colors)
13504           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13505             "      Number of colors: %.20g",(double) p->colors);
13506
13507         else
13508           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13509             "      Number of colors: unspecified");
13510
13511         if (mng_info->adjoin == MagickFalse)
13512           break;
13513       }
13514     }
13515
13516   use_global_plte=MagickFalse;
13517   all_images_are_gray=MagickFalse;
13518 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13519   need_local_plte=MagickTrue;
13520 #endif
13521   need_defi=MagickFalse;
13522   need_matte=MagickFalse;
13523   mng_info->framing_mode=1;
13524   mng_info->old_framing_mode=1;
13525
13526   if (write_mng)
13527       if (image_info->page != (char *) NULL)
13528         {
13529           /*
13530             Determine image bounding box.
13531           */
13532           SetGeometry(image,&mng_info->page);
13533           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13534             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13535         }
13536   if (write_mng)
13537     {
13538       unsigned int
13539         need_geom;
13540
13541       unsigned short
13542         red,
13543         green,
13544         blue;
13545
13546       const char *
13547         option;
13548
13549       mng_info->page=image->page;
13550       need_geom=MagickTrue;
13551       if (mng_info->page.width || mng_info->page.height)
13552          need_geom=MagickFalse;
13553       /*
13554         Check all the scenes.
13555       */
13556       initial_delay=image->delay;
13557       need_iterations=MagickFalse;
13558       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13559       mng_info->equal_physs=MagickTrue,
13560       mng_info->equal_gammas=MagickTrue;
13561       mng_info->equal_srgbs=MagickTrue;
13562       mng_info->equal_backgrounds=MagickTrue;
13563       image_count=0;
13564 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13565     defined(PNG_MNG_FEATURES_SUPPORTED)
13566       all_images_are_gray=MagickTrue;
13567       mng_info->equal_palettes=MagickFalse;
13568       need_local_plte=MagickFalse;
13569 #endif
13570       for (next_image=image; next_image != (Image *) NULL; )
13571       {
13572         if (need_geom)
13573           {
13574             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13575               mng_info->page.width=next_image->columns+next_image->page.x;
13576
13577             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13578               mng_info->page.height=next_image->rows+next_image->page.y;
13579           }
13580
13581         if (next_image->page.x || next_image->page.y)
13582           need_defi=MagickTrue;
13583
13584         if (next_image->alpha_trait != UndefinedPixelTrait)
13585           need_matte=MagickTrue;
13586
13587         if ((int) next_image->dispose >= BackgroundDispose)
13588           if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13589                next_image->page.x || next_image->page.y ||
13590               ((next_image->columns < mng_info->page.width) &&
13591                (next_image->rows < mng_info->page.height)))
13592             mng_info->need_fram=MagickTrue;
13593
13594         if (next_image->iterations)
13595           need_iterations=MagickTrue;
13596
13597         final_delay=next_image->delay;
13598
13599         if (final_delay != initial_delay || final_delay > 1UL*
13600            next_image->ticks_per_second)
13601           mng_info->need_fram=1;
13602
13603 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13604     defined(PNG_MNG_FEATURES_SUPPORTED)
13605         /*
13606           check for global palette possibility.
13607         */
13608         if (image->alpha_trait != UndefinedPixelTrait)
13609            need_local_plte=MagickTrue;
13610
13611         if (need_local_plte == 0)
13612           {
13613             if (SetImageGray(image,exception) == MagickFalse)
13614               all_images_are_gray=MagickFalse;
13615             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13616             if (use_global_plte == 0)
13617               use_global_plte=mng_info->equal_palettes;
13618             need_local_plte=!mng_info->equal_palettes;
13619           }
13620 #endif
13621         if (GetNextImageInList(next_image) != (Image *) NULL)
13622           {
13623             if (next_image->background_color.red !=
13624                 next_image->next->background_color.red ||
13625                 next_image->background_color.green !=
13626                 next_image->next->background_color.green ||
13627                 next_image->background_color.blue !=
13628                 next_image->next->background_color.blue)
13629               mng_info->equal_backgrounds=MagickFalse;
13630
13631             if (next_image->gamma != next_image->next->gamma)
13632               mng_info->equal_gammas=MagickFalse;
13633
13634             if (next_image->rendering_intent !=
13635                 next_image->next->rendering_intent)
13636               mng_info->equal_srgbs=MagickFalse;
13637
13638             if ((next_image->units != next_image->next->units) ||
13639                 (next_image->resolution.x != next_image->next->resolution.x) ||
13640                 (next_image->resolution.y != next_image->next->resolution.y))
13641               mng_info->equal_physs=MagickFalse;
13642
13643             if (mng_info->equal_chrms)
13644               {
13645                 if (next_image->chromaticity.red_primary.x !=
13646                     next_image->next->chromaticity.red_primary.x ||
13647                     next_image->chromaticity.red_primary.y !=
13648                     next_image->next->chromaticity.red_primary.y ||
13649                     next_image->chromaticity.green_primary.x !=
13650                     next_image->next->chromaticity.green_primary.x ||
13651                     next_image->chromaticity.green_primary.y !=
13652                     next_image->next->chromaticity.green_primary.y ||
13653                     next_image->chromaticity.blue_primary.x !=
13654                     next_image->next->chromaticity.blue_primary.x ||
13655                     next_image->chromaticity.blue_primary.y !=
13656                     next_image->next->chromaticity.blue_primary.y ||
13657                     next_image->chromaticity.white_point.x !=
13658                     next_image->next->chromaticity.white_point.x ||
13659                     next_image->chromaticity.white_point.y !=
13660                     next_image->next->chromaticity.white_point.y)
13661                   mng_info->equal_chrms=MagickFalse;
13662               }
13663           }
13664         image_count++;
13665         next_image=GetNextImageInList(next_image);
13666       }
13667       if (image_count < 2)
13668         {
13669           mng_info->equal_backgrounds=MagickFalse;
13670           mng_info->equal_chrms=MagickFalse;
13671           mng_info->equal_gammas=MagickFalse;
13672           mng_info->equal_srgbs=MagickFalse;
13673           mng_info->equal_physs=MagickFalse;
13674           use_global_plte=MagickFalse;
13675 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13676           need_local_plte=MagickTrue;
13677 #endif
13678           need_iterations=MagickFalse;
13679         }
13680
13681      if (mng_info->need_fram == MagickFalse)
13682        {
13683          /*
13684            Only certain framing rates 100/n are exactly representable without
13685            the FRAM chunk but we'll allow some slop in VLC files
13686          */
13687          if (final_delay == 0)
13688            {
13689              if (need_iterations != MagickFalse)
13690                {
13691                  /*
13692                    It's probably a GIF with loop; don't run it *too* fast.
13693                  */
13694                  if (mng_info->adjoin)
13695                    {
13696                      final_delay=10;
13697                      (void) ThrowMagickException(exception,GetMagickModule(),
13698                        CoderWarning,
13699                        "input has zero delay between all frames; assuming",
13700                        " 10 cs `%s'","");
13701                    }
13702                }
13703              else
13704                mng_info->ticks_per_second=0;
13705            }
13706          if (final_delay != 0)
13707            mng_info->ticks_per_second=(png_uint_32)
13708               (image->ticks_per_second/final_delay);
13709          if (final_delay > 50)
13710            mng_info->ticks_per_second=2;
13711
13712          if (final_delay > 75)
13713            mng_info->ticks_per_second=1;
13714
13715          if (final_delay > 125)
13716            mng_info->need_fram=MagickTrue;
13717
13718          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13719             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13720             (final_delay != 25) && (final_delay != 50) &&
13721             (final_delay != (size_t) image->ticks_per_second))
13722            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13723        }
13724
13725      if (mng_info->need_fram != MagickFalse)
13726         mng_info->ticks_per_second=image->ticks_per_second;
13727      /*
13728         If pseudocolor, we should also check to see if all the
13729         palettes are identical and write a global PLTE if they are.
13730         ../glennrp Feb 99.
13731      */
13732      /*
13733         Write the MNG version 1.0 signature and MHDR chunk.
13734      */
13735      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13736      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13737      PNGType(chunk,mng_MHDR);
13738      LogPNGChunk(logging,mng_MHDR,28L);
13739      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13740      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13741      PNGLong(chunk+12,mng_info->ticks_per_second);
13742      PNGLong(chunk+16,0L);  /* layer count=unknown */
13743      PNGLong(chunk+20,0L);  /* frame count=unknown */
13744      PNGLong(chunk+24,0L);  /* play time=unknown   */
13745      if (write_jng)
13746        {
13747          if (need_matte)
13748            {
13749              if (need_defi || mng_info->need_fram || use_global_plte)
13750                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13751
13752              else
13753                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13754            }
13755
13756          else
13757            {
13758              if (need_defi || mng_info->need_fram || use_global_plte)
13759                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13760
13761              else
13762                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13763            }
13764        }
13765
13766      else
13767        {
13768          if (need_matte)
13769            {
13770              if (need_defi || mng_info->need_fram || use_global_plte)
13771                PNGLong(chunk+28,11L);    /* simplicity=LC */
13772
13773              else
13774                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13775            }
13776
13777          else
13778            {
13779              if (need_defi || mng_info->need_fram || use_global_plte)
13780                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13781
13782              else
13783                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13784            }
13785        }
13786      (void) WriteBlob(image,32,chunk);
13787      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13788      option=GetImageOption(image_info,"mng:need-cacheoff");
13789      if (option != (const char *) NULL)
13790        {
13791          size_t
13792            length;
13793          /*
13794            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13795          */
13796          PNGType(chunk,mng_nEED);
13797          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13798          (void) WriteBlobMSBULong(image,(size_t) length);
13799          LogPNGChunk(logging,mng_nEED,(size_t) length);
13800          length+=4;
13801          (void) WriteBlob(image,length,chunk);
13802          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13803        }
13804      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13805          (GetNextImageInList(image) != (Image *) NULL) &&
13806          (image->iterations != 1))
13807        {
13808          /*
13809            Write MNG TERM chunk
13810          */
13811          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13812          PNGType(chunk,mng_TERM);
13813          LogPNGChunk(logging,mng_TERM,10L);
13814          chunk[4]=3;  /* repeat animation */
13815          chunk[5]=0;  /* show last frame when done */
13816          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13817             final_delay/MagickMax(image->ticks_per_second,1)));
13818
13819          if (image->iterations == 0)
13820            PNGLong(chunk+10,PNG_UINT_31_MAX);
13821
13822          else
13823            PNGLong(chunk+10,(png_uint_32) image->iterations);
13824
13825          if (logging != MagickFalse)
13826            {
13827              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13828                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13829               final_delay/MagickMax(image->ticks_per_second,1)));
13830
13831              if (image->iterations == 0)
13832                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13833                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13834
13835              else
13836                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13837                  "     Image iterations: %.20g",(double) image->iterations);
13838            }
13839          (void) WriteBlob(image,14,chunk);
13840          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13841        }
13842      /*
13843        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13844      */
13845      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13846           mng_info->equal_srgbs)
13847        {
13848          /*
13849            Write MNG sRGB chunk
13850          */
13851          (void) WriteBlobMSBULong(image,1L);
13852          PNGType(chunk,mng_sRGB);
13853          LogPNGChunk(logging,mng_sRGB,1L);
13854
13855          if (image->rendering_intent != UndefinedIntent)
13856            chunk[4]=(unsigned char)
13857              Magick_RenderingIntent_to_PNG_RenderingIntent(
13858              (image->rendering_intent));
13859
13860          else
13861            chunk[4]=(unsigned char)
13862              Magick_RenderingIntent_to_PNG_RenderingIntent(
13863                (PerceptualIntent));
13864
13865          (void) WriteBlob(image,5,chunk);
13866          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13867          mng_info->have_write_global_srgb=MagickTrue;
13868        }
13869
13870      else
13871        {
13872          if (image->gamma && mng_info->equal_gammas)
13873            {
13874              /*
13875                 Write MNG gAMA chunk
13876              */
13877              (void) WriteBlobMSBULong(image,4L);
13878              PNGType(chunk,mng_gAMA);
13879              LogPNGChunk(logging,mng_gAMA,4L);
13880              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13881              (void) WriteBlob(image,8,chunk);
13882              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13883              mng_info->have_write_global_gama=MagickTrue;
13884            }
13885          if (mng_info->equal_chrms)
13886            {
13887              PrimaryInfo
13888                primary;
13889
13890              /*
13891                 Write MNG cHRM chunk
13892              */
13893              (void) WriteBlobMSBULong(image,32L);
13894              PNGType(chunk,mng_cHRM);
13895              LogPNGChunk(logging,mng_cHRM,32L);
13896              primary=image->chromaticity.white_point;
13897              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13898              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13899              primary=image->chromaticity.red_primary;
13900              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13901              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13902              primary=image->chromaticity.green_primary;
13903              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13904              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13905              primary=image->chromaticity.blue_primary;
13906              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13907              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13908              (void) WriteBlob(image,36,chunk);
13909              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13910              mng_info->have_write_global_chrm=MagickTrue;
13911            }
13912        }
13913      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13914        {
13915          /*
13916             Write MNG pHYs chunk
13917          */
13918          (void) WriteBlobMSBULong(image,9L);
13919          PNGType(chunk,mng_pHYs);
13920          LogPNGChunk(logging,mng_pHYs,9L);
13921
13922          if (image->units == PixelsPerInchResolution)
13923            {
13924              PNGLong(chunk+4,(png_uint_32)
13925                (image->resolution.x*100.0/2.54+0.5));
13926
13927              PNGLong(chunk+8,(png_uint_32)
13928                (image->resolution.y*100.0/2.54+0.5));
13929
13930              chunk[12]=1;
13931            }
13932
13933          else
13934            {
13935              if (image->units == PixelsPerCentimeterResolution)
13936                {
13937                  PNGLong(chunk+4,(png_uint_32)
13938                    (image->resolution.x*100.0+0.5));
13939
13940                  PNGLong(chunk+8,(png_uint_32)
13941                    (image->resolution.y*100.0+0.5));
13942
13943                  chunk[12]=1;
13944                }
13945
13946              else
13947                {
13948                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13949                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13950                  chunk[12]=0;
13951                }
13952            }
13953          (void) WriteBlob(image,13,chunk);
13954          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13955        }
13956      /*
13957        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13958        or does not cover the entire frame.
13959      */
13960      if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13961          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13962          (image->page.width+image->page.x < mng_info->page.width))
13963          || (image->page.height && (image->page.height+image->page.y
13964          < mng_info->page.height))))
13965        {
13966          (void) WriteBlobMSBULong(image,6L);
13967          PNGType(chunk,mng_BACK);
13968          LogPNGChunk(logging,mng_BACK,6L);
13969          red=ScaleQuantumToShort(image->background_color.red);
13970          green=ScaleQuantumToShort(image->background_color.green);
13971          blue=ScaleQuantumToShort(image->background_color.blue);
13972          PNGShort(chunk+4,red);
13973          PNGShort(chunk+6,green);
13974          PNGShort(chunk+8,blue);
13975          (void) WriteBlob(image,10,chunk);
13976          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13977          if (mng_info->equal_backgrounds)
13978            {
13979              (void) WriteBlobMSBULong(image,6L);
13980              PNGType(chunk,mng_bKGD);
13981              LogPNGChunk(logging,mng_bKGD,6L);
13982              (void) WriteBlob(image,10,chunk);
13983              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13984            }
13985        }
13986
13987 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13988      if ((need_local_plte == MagickFalse) &&
13989          (image->storage_class == PseudoClass) &&
13990          (all_images_are_gray == MagickFalse))
13991        {
13992          size_t
13993            data_length;
13994
13995          /*
13996            Write MNG PLTE chunk
13997          */
13998          data_length=3*image->colors;
13999          (void) WriteBlobMSBULong(image,data_length);
14000          PNGType(chunk,mng_PLTE);
14001          LogPNGChunk(logging,mng_PLTE,data_length);
14002
14003          for (i=0; i < (ssize_t) image->colors; i++)
14004          {
14005            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
14006              image->colormap[i].red) & 0xff);
14007            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
14008              image->colormap[i].green) & 0xff);
14009            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
14010              image->colormap[i].blue) & 0xff);
14011          }
14012
14013          (void) WriteBlob(image,data_length+4,chunk);
14014          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
14015          mng_info->have_write_global_plte=MagickTrue;
14016        }
14017 #endif
14018     }
14019   scene=0;
14020   mng_info->delay=0;
14021 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14022     defined(PNG_MNG_FEATURES_SUPPORTED)
14023   mng_info->equal_palettes=MagickFalse;
14024 #endif
14025   imageListLength=GetImageListLength(image);
14026   do
14027   {
14028     if (mng_info->adjoin)
14029     {
14030 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14031     defined(PNG_MNG_FEATURES_SUPPORTED)
14032     /*
14033       If we aren't using a global palette for the entire MNG, check to
14034       see if we can use one for two or more consecutive images.
14035     */
14036     if (need_local_plte && use_global_plte && !all_images_are_gray)
14037       {
14038         if (mng_info->IsPalette)
14039           {
14040             /*
14041               When equal_palettes is true, this image has the same palette
14042               as the previous PseudoClass image
14043             */
14044             mng_info->have_write_global_plte=mng_info->equal_palettes;
14045             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
14046             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
14047               {
14048                 /*
14049                   Write MNG PLTE chunk
14050                 */
14051                 size_t
14052                   data_length;
14053
14054                 data_length=3*image->colors;
14055                 (void) WriteBlobMSBULong(image,data_length);
14056                 PNGType(chunk,mng_PLTE);
14057                 LogPNGChunk(logging,mng_PLTE,data_length);
14058
14059                 for (i=0; i < (ssize_t) image->colors; i++)
14060                 {
14061                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
14062                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
14063                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
14064                 }
14065
14066                 (void) WriteBlob(image,data_length+4,chunk);
14067                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
14068                    (uInt) (data_length+4)));
14069                 mng_info->have_write_global_plte=MagickTrue;
14070               }
14071           }
14072         else
14073           mng_info->have_write_global_plte=MagickFalse;
14074       }
14075 #endif
14076     if (need_defi)
14077       {
14078         ssize_t
14079           previous_x,
14080           previous_y;
14081
14082         if (scene != 0)
14083           {
14084             previous_x=mng_info->page.x;
14085             previous_y=mng_info->page.y;
14086           }
14087         else
14088           {
14089             previous_x=0;
14090             previous_y=0;
14091           }
14092         mng_info->page=image->page;
14093         if ((mng_info->page.x !=  previous_x) ||
14094             (mng_info->page.y != previous_y))
14095           {
14096              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
14097              PNGType(chunk,mng_DEFI);
14098              LogPNGChunk(logging,mng_DEFI,12L);
14099              chunk[4]=0; /* object 0 MSB */
14100              chunk[5]=0; /* object 0 LSB */
14101              chunk[6]=0; /* visible  */
14102              chunk[7]=0; /* abstract */
14103              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
14104              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
14105              (void) WriteBlob(image,16,chunk);
14106              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
14107           }
14108       }
14109     }
14110
14111    mng_info->write_mng=write_mng;
14112
14113    if ((int) image->dispose >= 3)
14114      mng_info->framing_mode=3;
14115
14116    if (mng_info->need_fram && mng_info->adjoin &&
14117        ((image->delay != mng_info->delay) ||
14118         (mng_info->framing_mode != mng_info->old_framing_mode)))
14119      {
14120        if (image->delay == mng_info->delay)
14121          {
14122            /*
14123              Write a MNG FRAM chunk with the new framing mode.
14124            */
14125            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
14126            PNGType(chunk,mng_FRAM);
14127            LogPNGChunk(logging,mng_FRAM,1L);
14128            chunk[4]=(unsigned char) mng_info->framing_mode;
14129            (void) WriteBlob(image,5,chunk);
14130            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
14131          }
14132        else
14133          {
14134            /*
14135              Write a MNG FRAM chunk with the delay.
14136            */
14137            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
14138            PNGType(chunk,mng_FRAM);
14139            LogPNGChunk(logging,mng_FRAM,10L);
14140            chunk[4]=(unsigned char) mng_info->framing_mode;
14141            chunk[5]=0;  /* frame name separator (no name) */
14142            chunk[6]=2;  /* flag for changing default delay */
14143            chunk[7]=0;  /* flag for changing frame timeout */
14144            chunk[8]=0;  /* flag for changing frame clipping */
14145            chunk[9]=0;  /* flag for changing frame sync_id */
14146            PNGLong(chunk+10,(png_uint_32)
14147              ((mng_info->ticks_per_second*
14148              image->delay)/MagickMax(image->ticks_per_second,1)));
14149            (void) WriteBlob(image,14,chunk);
14150            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
14151            mng_info->delay=(png_uint_32) image->delay;
14152          }
14153        mng_info->old_framing_mode=mng_info->framing_mode;
14154      }
14155
14156 #if defined(JNG_SUPPORTED)
14157    if (image_info->compression == JPEGCompression)
14158      {
14159        ImageInfo
14160          *write_info;
14161
14162        if (logging != MagickFalse)
14163          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14164            "  Writing JNG object.");
14165        /* To do: specify the desired alpha compression method. */
14166        write_info=CloneImageInfo(image_info);
14167        write_info->compression=UndefinedCompression;
14168        status=WriteOneJNGImage(mng_info,write_info,image,exception);
14169        write_info=DestroyImageInfo(write_info);
14170      }
14171    else
14172 #endif
14173      {
14174        if (logging != MagickFalse)
14175          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14176            "  Writing PNG object.");
14177
14178        mng_info->need_blob = MagickFalse;
14179        mng_info->ping_preserve_colormap = MagickFalse;
14180
14181        /* We don't want any ancillary chunks written */
14182        mng_info->ping_exclude_bKGD=MagickTrue;
14183        mng_info->ping_exclude_caNv=MagickTrue;
14184        mng_info->ping_exclude_cHRM=MagickTrue;
14185        mng_info->ping_exclude_date=MagickTrue;
14186        mng_info->ping_exclude_EXIF=MagickTrue;
14187        mng_info->ping_exclude_gAMA=MagickTrue;
14188        mng_info->ping_exclude_iCCP=MagickTrue;
14189        /* mng_info->ping_exclude_iTXt=MagickTrue; */
14190        mng_info->ping_exclude_oFFs=MagickTrue;
14191        mng_info->ping_exclude_pHYs=MagickTrue;
14192        mng_info->ping_exclude_sRGB=MagickTrue;
14193        mng_info->ping_exclude_tEXt=MagickTrue;
14194        mng_info->ping_exclude_tRNS=MagickTrue;
14195        mng_info->ping_exclude_zCCP=MagickTrue;
14196        mng_info->ping_exclude_zTXt=MagickTrue;
14197
14198        status=WriteOnePNGImage(mng_info,image_info,image,exception);
14199      }
14200
14201     if (status == MagickFalse)
14202       {
14203         mng_info=MngInfoFreeStruct(mng_info);
14204         (void) CloseBlob(image);
14205         return(MagickFalse);
14206       }
14207     (void) CatchImageException(image);
14208     if (GetNextImageInList(image) == (Image *) NULL)
14209       break;
14210     image=SyncNextImageInList(image);
14211     status=SetImageProgress(image,SaveImagesTag,scene++,imageListLength);
14212
14213     if (status == MagickFalse)
14214       break;
14215
14216   } while (mng_info->adjoin);
14217
14218   if (write_mng)
14219     {
14220       while (GetPreviousImageInList(image) != (Image *) NULL)
14221         image=GetPreviousImageInList(image);
14222       /*
14223         Write the MEND chunk.
14224       */
14225       (void) WriteBlobMSBULong(image,0x00000000L);
14226       PNGType(chunk,mng_MEND);
14227       LogPNGChunk(logging,mng_MEND,0L);
14228       (void) WriteBlob(image,4,chunk);
14229       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
14230     }
14231   /*
14232     Relinquish resources.
14233   */
14234   (void) CloseBlob(image);
14235   mng_info=MngInfoFreeStruct(mng_info);
14236
14237   if (logging != MagickFalse)
14238     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
14239
14240   return(MagickTrue);
14241 }
14242 #else /* PNG_LIBPNG_VER > 10011 */
14243
14244 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
14245   Image *image)
14246 {
14247   (void) image;
14248   printf("Your PNG library is too old: You have libpng-%s\n",
14249      PNG_LIBPNG_VER_STRING);
14250
14251   ThrowBinaryException(CoderError,"PNG library is too old",
14252      image_info->filename);
14253 }
14254
14255 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
14256   Image *image)
14257 {
14258   return(WritePNGImage(image_info,image));
14259 }
14260 #endif /* PNG_LIBPNG_VER > 10011 */
14261 #endif