]> granicus.if.org Git - imagemagick/blob - coders/png.c
Fixed bug with -strip (-define png:exclude-chunk)
[imagemagick] / coders / png.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            PPPP   N   N   GGGG                              %
7 %                            P   P  NN  N  G                                  %
8 %                            PPPP   N N N  G  GG                              %
9 %                            P      N  NN  G   G                              %
10 %                            P      N   N   GGG                               %
11 %                                                                             %
12 %                                                                             %
13 %              Read/Write Portable Network Graphics Image Format              %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                           Glenn Randers-Pehrson                             %
18 %                               November 1997                                 %
19 %                                                                             %
20 %                                                                             %
21 %  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
22 %  dedicated to making software imaging solutions freely available.           %
23 %                                                                             %
24 %  You may not use this file except in compliance with the License.  You may  %
25 %  obtain a copy of the License at                                            %
26 %                                                                             %
27 %    http://www.imagemagick.org/script/license.php                            %
28 %                                                                             %
29 %  Unless required by applicable law or agreed to in writing, software        %
30 %  distributed under the License is distributed on an "AS IS" BASIS,          %
31 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32 %  See the License for the specific language governing permissions and        %
33 %  limitations under the License.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39
40 \f
41 /*
42   Include declarations.
43 */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
52 #include "MagickCore/color-private.h"
53 #include "MagickCore/colormap.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/histogram.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/layer.h"
65 #include "MagickCore/list.h"
66 #include "MagickCore/log.h"
67 #include "MagickCore/MagickCore.h"
68 #include "MagickCore/memory_.h"
69 #include "MagickCore/module.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/profile.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/static.h"
82 #include "MagickCore/statistic.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/utility.h"
87 #if defined(MAGICKCORE_PNG_DELEGATE)
88
89 /* Suppress libpng pedantic warnings that were added in
90  * libpng-1.2.41 and libpng-1.4.0.  If you are working on
91  * migration to libpng-1.5, remove these defines and then
92  * fix any code that generates warnings.
93  */
94 /* #define PNG_DEPRECATED   Use of this function is deprecated */
95 /* #define PNG_USE_RESULT   The result of this function must be checked */
96 /* #define PNG_NORETURN     This function does not return */
97 /* #define PNG_ALLOCATED    The result of the function is new memory */
98 /* #define PNG_DEPSTRUCT    Access to this struct member is deprecated */
99
100 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
101 #define PNG_PTR_NORETURN
102
103 #include "png.h"
104 #include "zlib.h"
105 \f
106 /* ImageMagick differences */
107 #define first_scene scene
108
109 #if PNG_LIBPNG_VER > 10011
110 /*
111   Optional declarations. Define or undefine them as you like.
112 */
113 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
114
115 /*
116   Features under construction.  Define these to work on them.
117 */
118 #undef MNG_OBJECT_BUFFERS
119 #undef MNG_BASI_SUPPORTED
120 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121 #define MNG_INSERT_LAYERS   /* Troublesome, but seem to work as of 5.4.4 */
122 #if defined(MAGICKCORE_JPEG_DELEGATE)
123 #  define JNG_SUPPORTED /* Not finished as of 5.5.2.  See "To do" comments. */
124 #endif
125 #if !defined(RGBColorMatchExact)
126 #define IsPNGColorEqual(color,target) \
127        (((color).red == (target).red) && \
128         ((color).green == (target).green) && \
129         ((color).blue == (target).blue))
130 #endif
131
132 /* Table of recognized sRGB ICC profiles */
133 struct sRGB_info_struct
134 {
135     png_uint_32 len;
136     png_uint_32 crc;
137     png_byte intent;
138 };
139
140 const struct sRGB_info_struct sRGB_info[] =
141
142     /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
143     { 3048, 0x3b8772b9UL, 0},
144
145     /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
146     { 3052, 0x427ebb21UL, 1},
147
148     /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
149     {60988, 0x306fd8aeUL, 0},
150
151     /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
152      {60960, 0xbbef7812UL, 0},
153
154     /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
155      { 3024, 0x5d5129ceUL, 1},
156
157      /* HP-Microsoft sRGB v2 perceptual */
158      { 3144, 0x182ea552UL, 0},
159
160      /* HP-Microsoft sRGB v2 media-relative */
161      { 3144, 0xf29e526dUL, 1},
162
163      /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
164      {  524, 0xd4938c39UL, 0},
165
166      /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
167      { 3212, 0x034af5a1UL, 0},
168
169      /* Not recognized */
170      {    0, 0x00000000UL, 0},
171 };
172
173 /* Macros for left-bit-replication to ensure that pixels
174  * and PixelInfos all have the same image->depth, and for use
175  * in PNG8 quantization.
176  */
177
178 /* LBR01: Replicate top bit */
179
180 #define LBR01PacketRed(pixelpacket) \
181      (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
182         0 : QuantumRange);
183
184 #define LBR01PacketGreen(pixelpacket) \
185      (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
186         0 : QuantumRange);
187
188 #define LBR01PacketBlue(pixelpacket) \
189      (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
190         0 : QuantumRange);
191
192 #define LBR01PacketAlpha(pixelpacket) \
193      (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
194         0 : QuantumRange);
195
196 #define LBR01PacketRGB(pixelpacket) \
197         { \
198         LBR01PacketRed((pixelpacket)); \
199         LBR01PacketGreen((pixelpacket)); \
200         LBR01PacketBlue((pixelpacket)); \
201         }
202
203 #define LBR01PacketRGBO(pixelpacket) \
204         { \
205         LBR01PacketRGB((pixelpacket)); \
206         LBR01PacketAlpha((pixelpacket)); \
207         }
208
209 #define LBR01PixelRed(pixel) \
210         (SetPixelRed(image, \
211         ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
212         0 : QuantumRange,(pixel)));
213
214 #define LBR01PixelGreen(pixel) \
215         (SetPixelGreen(image, \
216         ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
217         0 : QuantumRange,(pixel)));
218
219 #define LBR01PixelBlue(pixel) \
220         (SetPixelBlue(image, \
221         ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
222         0 : QuantumRange,(pixel)));
223
224 #define LBR01PixelAlpha(pixel) \
225         (SetPixelAlpha(image, \
226         ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
227         0 : QuantumRange,(pixel)));
228
229 #define LBR01PixelRGB(pixel) \
230         { \
231         LBR01PixelRed((pixel)); \
232         LBR01PixelGreen((pixel)); \
233         LBR01PixelBlue((pixel)); \
234         }
235
236 #define LBR01PixelRGBA(pixel) \
237         { \
238         LBR01PixelRGB((pixel)); \
239         LBR01PixelAlpha((pixel)); \
240         }
241
242 /* LBR02: Replicate top 2 bits */
243
244 #define LBR02PacketRed(pixelpacket) \
245    { \
246      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
247      (pixelpacket).red=ScaleCharToQuantum( \
248        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
249    }
250 #define LBR02PacketGreen(pixelpacket) \
251    { \
252      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
253      (pixelpacket).green=ScaleCharToQuantum( \
254        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
255    }
256 #define LBR02PacketBlue(pixelpacket) \
257    { \
258      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
259      (pixelpacket).blue=ScaleCharToQuantum( \
260        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
261    }
262 #define LBR02PacketAlpha(pixelpacket) \
263    { \
264      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
265      (pixelpacket).alpha=ScaleCharToQuantum( \
266        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
267    }
268
269 #define LBR02PacketRGB(pixelpacket) \
270         { \
271         LBR02PacketRed((pixelpacket)); \
272         LBR02PacketGreen((pixelpacket)); \
273         LBR02PacketBlue((pixelpacket)); \
274         }
275
276 #define LBR02PacketRGBO(pixelpacket) \
277         { \
278         LBR02PacketRGB((pixelpacket)); \
279         LBR02PacketAlpha((pixelpacket)); \
280         }
281
282 #define LBR02PixelRed(pixel) \
283    { \
284      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
285        & 0xc0; \
286      SetPixelRed(image, ScaleCharToQuantum( \
287        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
288        (pixel)); \
289    }
290 #define LBR02PixelGreen(pixel) \
291    { \
292      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
293        & 0xc0; \
294      SetPixelGreen(image, ScaleCharToQuantum( \
295        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
296        (pixel)); \
297    }
298 #define LBR02PixelBlue(pixel) \
299    { \
300      unsigned char lbr_bits= \
301        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
302      SetPixelBlue(image, ScaleCharToQuantum( \
303        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
304        (pixel)); \
305    }
306 #define LBR02PixelAlpha(pixel) \
307    { \
308      unsigned char lbr_bits= \
309        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
310      SetPixelAlpha(image, ScaleCharToQuantum( \
311        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
312        (pixel) ); \
313    }
314
315 #define LBR02PixelRGB(pixel) \
316         { \
317         LBR02PixelRed((pixel)); \
318         LBR02PixelGreen((pixel)); \
319         LBR02PixelBlue((pixel)); \
320         }
321
322 #define LBR02PixelRGBA(pixel) \
323         { \
324         LBR02PixelRGB((pixel)); \
325         LBR02PixelAlpha((pixel)); \
326         }
327
328 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
329    PNG8 quantization) */
330
331 #define LBR03PacketRed(pixelpacket) \
332    { \
333      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
334      (pixelpacket).red=ScaleCharToQuantum( \
335        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
336    }
337 #define LBR03PacketGreen(pixelpacket) \
338    { \
339      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
340      (pixelpacket).green=ScaleCharToQuantum( \
341        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
342    }
343 #define LBR03PacketBlue(pixelpacket) \
344    { \
345      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
346      (pixelpacket).blue=ScaleCharToQuantum( \
347        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
348    }
349
350 #define LBR03PacketRGB(pixelpacket) \
351         { \
352         LBR03PacketRed((pixelpacket)); \
353         LBR03PacketGreen((pixelpacket)); \
354         LBR03PacketBlue((pixelpacket)); \
355         }
356
357 #define LBR03PixelRed(pixel) \
358    { \
359      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
360        & 0xe0; \
361      SetPixelRed(image, ScaleCharToQuantum( \
362        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
363    }
364 #define LBR03Green(pixel) \
365    { \
366      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
367        & 0xe0; \
368      SetPixelGreen(image, ScaleCharToQuantum( \
369        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
370    }
371 #define LBR03Blue(pixel) \
372    { \
373      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
374        & 0xe0; \
375      SetPixelBlue(image, ScaleCharToQuantum( \
376        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
377    }
378
379 #define LBR03RGB(pixel) \
380         { \
381         LBR03PixelRed((pixel)); \
382         LBR03Green((pixel)); \
383         LBR03Blue((pixel)); \
384         }
385
386 /* LBR04: Replicate top 4 bits */
387
388 #define LBR04PacketRed(pixelpacket) \
389    { \
390      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
391      (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
392    }
393 #define LBR04PacketGreen(pixelpacket) \
394    { \
395      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
396      (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
397    }
398 #define LBR04PacketBlue(pixelpacket) \
399    { \
400      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
401      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
402    }
403 #define LBR04PacketAlpha(pixelpacket) \
404    { \
405      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
406      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
407    }
408
409 #define LBR04PacketRGB(pixelpacket) \
410         { \
411         LBR04PacketRed((pixelpacket)); \
412         LBR04PacketGreen((pixelpacket)); \
413         LBR04PacketBlue((pixelpacket)); \
414         }
415
416 #define LBR04PacketRGBO(pixelpacket) \
417         { \
418         LBR04PacketRGB((pixelpacket)); \
419         LBR04PacketAlpha((pixelpacket)); \
420         }
421
422 #define LBR04PixelRed(pixel) \
423    { \
424      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
425        & 0xf0; \
426      SetPixelRed(image,\
427        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
428    }
429 #define LBR04PixelGreen(pixel) \
430    { \
431      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
432        & 0xf0; \
433      SetPixelGreen(image,\
434        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
435    }
436 #define LBR04PixelBlue(pixel) \
437    { \
438      unsigned char lbr_bits= \
439        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
440      SetPixelBlue(image,\
441        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
442    }
443 #define LBR04PixelAlpha(pixel) \
444    { \
445      unsigned char lbr_bits= \
446        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
447      SetPixelAlpha(image,\
448        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
449    }
450
451 #define LBR04PixelRGB(pixel) \
452         { \
453         LBR04PixelRed((pixel)); \
454         LBR04PixelGreen((pixel)); \
455         LBR04PixelBlue((pixel)); \
456         }
457
458 #define LBR04PixelRGBA(pixel) \
459         { \
460         LBR04PixelRGB((pixel)); \
461         LBR04PixelAlpha((pixel)); \
462         }
463
464
465 #if MAGICKCORE_QUANTUM_DEPTH > 8
466 /* LBR08: Replicate top 8 bits */
467
468 #define LBR08PacketRed(pixelpacket) \
469    { \
470      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
471      (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
472    }
473 #define LBR08PacketGreen(pixelpacket) \
474    { \
475      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
476      (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
477    }
478 #define LBR08PacketBlue(pixelpacket) \
479    { \
480      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
481      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
482    }
483 #define LBR08PacketAlpha(pixelpacket) \
484    { \
485      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
486      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
487    }
488
489 #define LBR08PacketRGB(pixelpacket) \
490         { \
491         LBR08PacketRed((pixelpacket)); \
492         LBR08PacketGreen((pixelpacket)); \
493         LBR08PacketBlue((pixelpacket)); \
494         }
495
496 #define LBR08PacketRGBO(pixelpacket) \
497         { \
498         LBR08PacketRGB((pixelpacket)); \
499         LBR08PacketAlpha((pixelpacket)); \
500         }
501
502 #define LBR08PixelRed(pixel) \
503    { \
504      unsigned char lbr_bits= \
505        ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
506      SetPixelRed(image,\
507        ScaleCharToQuantum((lbr_bits)), (pixel)); \
508    }
509 #define LBR08PixelGreen(pixel) \
510    { \
511      unsigned char lbr_bits= \
512        ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
513      SetPixelGreen(image,\
514        ScaleCharToQuantum((lbr_bits)), (pixel)); \
515    }
516 #define LBR08PixelBlue(pixel) \
517    { \
518      unsigned char lbr_bits= \
519        ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
520      SetPixelBlue(image,\
521        ScaleCharToQuantum((lbr_bits)), (pixel)); \
522    }
523 #define LBR08PixelAlpha(pixel) \
524    { \
525      unsigned char lbr_bits= \
526        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
527      SetPixelAlpha(image,\
528        ScaleCharToQuantum((lbr_bits)), (pixel)); \
529    }
530
531 #define LBR08PixelRGB(pixel) \
532         { \
533         LBR08PixelRed((pixel)); \
534         LBR08PixelGreen((pixel)); \
535         LBR08PixelBlue((pixel)); \
536         }
537
538 #define LBR08PixelRGBA(pixel) \
539         { \
540         LBR08PixelRGB((pixel)); \
541         LBR08PixelAlpha((pixel)); \
542         }
543 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
544
545
546 #if MAGICKCORE_QUANTUM_DEPTH > 16
547 /* LBR16: Replicate top 16 bits */
548
549 #define LBR16PacketRed(pixelpacket) \
550    { \
551      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
552      (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
553    }
554 #define LBR16PacketGreen(pixelpacket) \
555    { \
556      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
557      (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
558    }
559 #define LBR16PacketBlue(pixelpacket) \
560    { \
561      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
562      (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
563    }
564 #define LBR16PacketAlpha(pixelpacket) \
565    { \
566      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
567      (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
568    }
569
570 #define LBR16PacketRGB(pixelpacket) \
571         { \
572         LBR16PacketRed((pixelpacket)); \
573         LBR16PacketGreen((pixelpacket)); \
574         LBR16PacketBlue((pixelpacket)); \
575         }
576
577 #define LBR16PacketRGBO(pixelpacket) \
578         { \
579         LBR16PacketRGB((pixelpacket)); \
580         LBR16PacketAlpha((pixelpacket)); \
581         }
582
583 #define LBR16PixelRed(pixel) \
584    { \
585      unsigned short lbr_bits= \
586        ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
587      SetPixelRed(image,\
588        ScaleShortToQuantum((lbr_bits)),(pixel)); \
589    }
590 #define LBR16PixelGreen(pixel) \
591    { \
592      unsigned short lbr_bits= \
593        ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
594      SetPixelGreen(image,\
595        ScaleShortToQuantum((lbr_bits)),(pixel)); \
596    }
597 #define LBR16PixelBlue(pixel) \
598    { \
599      unsigned short lbr_bits= \
600        ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
601      SetPixelBlue(image,\
602        ScaleShortToQuantum((lbr_bits)),(pixel)); \
603    }
604 #define LBR16PixelAlpha(pixel) \
605    { \
606      unsigned short lbr_bits= \
607        ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
608      SetPixelAlpha(image,\
609        ScaleShortToQuantum((lbr_bits)),(pixel)); \
610    }
611
612 #define LBR16PixelRGB(pixel) \
613         { \
614         LBR16PixelRed((pixel)); \
615         LBR16PixelGreen((pixel)); \
616         LBR16PixelBlue((pixel)); \
617         }
618
619 #define LBR16PixelRGBA(pixel) \
620         { \
621         LBR16PixelRGB((pixel)); \
622         LBR16PixelAlpha((pixel)); \
623         }
624 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
625
626 /*
627   Establish thread safety.
628   setjmp/longjmp is claimed to be safe on these platforms:
629   setjmp/longjmp is alleged to be unsafe on these platforms:
630 */
631 #ifdef PNG_SETJMP_SUPPORTED
632 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
633 #   define IMPNG_SETJMP_NOT_THREAD_SAFE
634 # endif
635
636 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
637 static SemaphoreInfo
638   *ping_semaphore = (SemaphoreInfo *) NULL;
639 # endif
640 #endif
641
642 /*
643   This temporary until I set up malloc'ed object attributes array.
644   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
645   waste more memory.
646 */
647 #define MNG_MAX_OBJECTS 256
648
649 /*
650   If this not defined, spec is interpreted strictly.  If it is
651   defined, an attempt will be made to recover from some errors,
652   including
653       o global PLTE too short
654 */
655 #undef MNG_LOOSE
656
657 /*
658   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
659   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
660   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
661   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
662   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
663   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
664   will be enabled by default in libpng-1.2.0.
665 */
666 #ifdef PNG_MNG_FEATURES_SUPPORTED
667 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
668 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
669 #  endif
670 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
671 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
672 #  endif
673 #endif
674
675 /*
676   Maximum valid size_t in PNG/MNG chunks is (2^31)-1
677   This macro is only defined in libpng-1.0.3 and later.
678   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
679 */
680 #ifndef PNG_UINT_31_MAX
681 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
682 #endif
683
684 /*
685   Constant strings for known chunk types.  If you need to add a chunk,
686   add a string holding the name here.   To make the code more
687   portable, we use ASCII numbers like this, not characters.
688 */
689
690 static png_byte mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
691 static png_byte mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
692 static png_byte mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
693 static png_byte mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
694 static png_byte mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
695 static png_byte mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
696 static png_byte mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
697 static png_byte mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
698 static png_byte mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
699 static png_byte mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
700 static png_byte mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
701 static png_byte mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
702 static png_byte mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
703 static png_byte mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
704 static png_byte mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
705 static png_byte mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
706 static png_byte mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
707 static png_byte mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
708 static png_byte mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
709 static png_byte mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
710 static png_byte mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
711 static png_byte mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
712 static png_byte mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
713 static png_byte mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
714 static png_byte mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
715 static png_byte mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
716 static png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
717 static png_byte mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
718 static png_byte mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
719 static png_byte mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
720 static png_byte mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
721 static png_byte mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
722 static png_byte mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
723 static png_byte mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
724
725 #if defined(JNG_SUPPORTED)
726 static png_byte mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
727 static png_byte mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
728 static png_byte mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
729 static png_byte mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
730 static png_byte mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
731 static png_byte mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
732 #endif
733
734 /*
735 Other known chunks that are not yet supported by ImageMagick:
736 static png_byte mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
737 static png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
738 static png_byte mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
739 static png_byte mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
740 static png_byte mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
741 static png_byte mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
742 static png_byte mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
743 static png_byte mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
744 */
745
746 typedef struct _MngBox
747 {
748   long
749     left,
750     right,
751     top,
752     bottom;
753 } MngBox;
754
755 typedef struct _MngPair
756 {
757   volatile long
758     a,
759     b;
760 } MngPair;
761
762 #ifdef MNG_OBJECT_BUFFERS
763 typedef struct _MngBuffer
764 {
765
766   size_t
767     height,
768     width;
769
770   Image
771     *image;
772
773   png_color
774     plte[256];
775
776   int
777     reference_count;
778
779   unsigned char
780     alpha_sample_depth,
781     compression_method,
782     color_type,
783     concrete,
784     filter_method,
785     frozen,
786     image_type,
787     interlace_method,
788     pixel_sample_depth,
789     plte_length,
790     sample_depth,
791     viewable;
792 } MngBuffer;
793 #endif
794
795 typedef struct _MngInfo
796 {
797
798 #ifdef MNG_OBJECT_BUFFERS
799   MngBuffer
800     *ob[MNG_MAX_OBJECTS];
801 #endif
802
803   Image *
804     image;
805
806   RectangleInfo
807     page;
808
809   int
810     adjoin,
811 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
812     bytes_in_read_buffer,
813     found_empty_plte,
814 #endif
815     equal_backgrounds,
816     equal_chrms,
817     equal_gammas,
818 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
819     defined(PNG_MNG_FEATURES_SUPPORTED)
820     equal_palettes,
821 #endif
822     equal_physs,
823     equal_srgbs,
824     framing_mode,
825     have_global_bkgd,
826     have_global_chrm,
827     have_global_gama,
828     have_global_phys,
829     have_global_sbit,
830     have_global_srgb,
831     have_saved_bkgd_index,
832     have_write_global_chrm,
833     have_write_global_gama,
834     have_write_global_plte,
835     have_write_global_srgb,
836     need_fram,
837     object_id,
838     old_framing_mode,
839     saved_bkgd_index;
840
841   int
842     new_number_colors;
843
844   ssize_t
845     image_found,
846     loop_count[256],
847     loop_iteration[256],
848     scenes_found,
849     x_off[MNG_MAX_OBJECTS],
850     y_off[MNG_MAX_OBJECTS];
851
852   MngBox
853     clip,
854     frame,
855     image_box,
856     object_clip[MNG_MAX_OBJECTS];
857
858   unsigned char
859     /* These flags could be combined into one byte */
860     exists[MNG_MAX_OBJECTS],
861     frozen[MNG_MAX_OBJECTS],
862     loop_active[256],
863     invisible[MNG_MAX_OBJECTS],
864     viewable[MNG_MAX_OBJECTS];
865
866   MagickOffsetType
867     loop_jump[256];
868
869   png_colorp
870     global_plte;
871
872   png_color_8
873     global_sbit;
874
875   png_byte
876 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
877     read_buffer[8],
878 #endif
879     global_trns[256];
880
881   float
882     global_gamma;
883
884   ChromaticityInfo
885     global_chrm;
886
887   RenderingIntent
888     global_srgb_intent;
889
890   unsigned int
891     delay,
892     global_plte_length,
893     global_trns_length,
894     global_x_pixels_per_unit,
895     global_y_pixels_per_unit,
896     mng_width,
897     mng_height,
898     ticks_per_second;
899
900   MagickBooleanType
901     need_blob;
902
903   unsigned int
904     IsPalette,
905     global_phys_unit_type,
906     basi_warning,
907     clon_warning,
908     dhdr_warning,
909     jhdr_warning,
910     magn_warning,
911     past_warning,
912     phyg_warning,
913     phys_warning,
914     sbit_warning,
915     show_warning,
916     mng_type,
917     write_mng,
918     write_png_colortype,
919     write_png_depth,
920     write_png_compression_level,
921     write_png_compression_strategy,
922     write_png_compression_filter,
923     write_png8,
924     write_png24,
925     write_png32,
926     write_png48,
927     write_png64;
928
929 #ifdef MNG_BASI_SUPPORTED
930   size_t
931     basi_width,
932     basi_height;
933
934   unsigned int
935     basi_depth,
936     basi_color_type,
937     basi_compression_method,
938     basi_filter_type,
939     basi_interlace_method,
940     basi_red,
941     basi_green,
942     basi_blue,
943     basi_alpha,
944     basi_viewable;
945 #endif
946
947   png_uint_16
948     magn_first,
949     magn_last,
950     magn_mb,
951     magn_ml,
952     magn_mr,
953     magn_mt,
954     magn_mx,
955     magn_my,
956     magn_methx,
957     magn_methy;
958
959   PixelInfo
960     mng_global_bkgd;
961
962   /* Added at version 6.6.6-7 */
963   MagickBooleanType
964     ping_exclude_bKGD,
965     ping_exclude_cHRM,
966     ping_exclude_date,
967     ping_exclude_EXIF,
968     ping_exclude_gAMA,
969     ping_exclude_iCCP,
970     /* ping_exclude_iTXt, */
971     ping_exclude_oFFs,
972     ping_exclude_pHYs,
973     ping_exclude_sRGB,
974     ping_exclude_tEXt,
975     ping_exclude_tRNS,
976     ping_exclude_vpAg,
977     ping_exclude_zCCP, /* hex-encoded iCCP */
978     ping_exclude_zTXt,
979     ping_preserve_colormap,
980   /* Added at version 6.8.5-7 */
981     ping_preserve_iCCP;
982
983 } MngInfo;
984 #endif /* VER */
985 \f
986 /*
987   Forward declarations.
988 */
989 static MagickBooleanType
990   WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
991
992 static MagickBooleanType
993   WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
994
995 #if defined(JNG_SUPPORTED)
996 static MagickBooleanType
997   WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
998 #endif
999
1000 #if PNG_LIBPNG_VER > 10011
1001
1002
1003 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
1004 static MagickBooleanType
1005 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
1006 {
1007     /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
1008      *
1009      * This is true if the high byte and the next highest byte of
1010      * each sample of the image, the colormap, and the background color
1011      * are equal to each other.  We check this by seeing if the samples
1012      * are unchanged when we scale them down to 8 and back up to Quantum.
1013      *
1014      * We don't use the method GetImageDepth() because it doesn't check
1015      * background and doesn't handle PseudoClass specially.
1016      */
1017
1018 #define QuantumToCharToQuantumEqQuantum(quantum) \
1019   ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
1020
1021     MagickBooleanType
1022       ok_to_reduce=MagickFalse;
1023
1024     if (image->depth >= 16)
1025       {
1026
1027         const Quantum
1028           *p;
1029
1030         ok_to_reduce=
1031            QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
1032            QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
1033            QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
1034            MagickTrue : MagickFalse;
1035
1036         if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
1037           {
1038             int indx;
1039
1040             for (indx=0; indx < (ssize_t) image->colors; indx++)
1041               {
1042                 ok_to_reduce=(
1043                    QuantumToCharToQuantumEqQuantum(
1044                    image->colormap[indx].red) &&
1045                    QuantumToCharToQuantumEqQuantum(
1046                    image->colormap[indx].green) &&
1047                    QuantumToCharToQuantumEqQuantum(
1048                    image->colormap[indx].blue)) ?
1049                    MagickTrue : MagickFalse;
1050
1051                 if (ok_to_reduce == MagickFalse)
1052                    break;
1053               }
1054           }
1055
1056         if ((ok_to_reduce != MagickFalse) &&
1057             (image->storage_class != PseudoClass))
1058           {
1059             ssize_t
1060               y;
1061
1062             register ssize_t
1063               x;
1064
1065             for (y=0; y < (ssize_t) image->rows; y++)
1066             {
1067               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1068
1069               if (p == (const Quantum *) NULL)
1070                 {
1071                   ok_to_reduce = MagickFalse;
1072                   break;
1073                 }
1074
1075               for (x=(ssize_t) image->columns-1; x >= 0; x--)
1076               {
1077                 ok_to_reduce=
1078                    QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1079                    QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1080                    QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
1081                    MagickTrue : MagickFalse;
1082
1083                 if (ok_to_reduce == MagickFalse)
1084                   break;
1085
1086                 p+=GetPixelChannels(image);
1087               }
1088               if (x >= 0)
1089                 break;
1090             }
1091           }
1092
1093         if (ok_to_reduce != MagickFalse)
1094           {
1095             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1096                 "    OK to reduce PNG bit depth to 8 without loss of info");
1097           }
1098         else
1099           {
1100             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1101                 "    Not OK to reduce PNG bit depth to 8 without loss of info");
1102           }
1103       }
1104
1105     return ok_to_reduce;
1106 }
1107 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1108
1109 static const char* PngColorTypeToString(const unsigned int color_type)
1110 {
1111   const char
1112     *result = "Unknown";
1113
1114   switch (color_type)
1115     {
1116     case PNG_COLOR_TYPE_GRAY:
1117       result = "Gray";
1118       break;
1119     case PNG_COLOR_TYPE_GRAY_ALPHA:
1120       result = "Gray+Alpha";
1121       break;
1122     case PNG_COLOR_TYPE_PALETTE:
1123       result = "Palette";
1124       break;
1125     case PNG_COLOR_TYPE_RGB:
1126       result = "RGB";
1127       break;
1128     case PNG_COLOR_TYPE_RGB_ALPHA:
1129       result = "RGB+Alpha";
1130       break;
1131     }
1132
1133   return result;
1134 }
1135
1136 static int
1137 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1138 {
1139   switch (intent)
1140   {
1141     case PerceptualIntent:
1142        return 0;
1143
1144     case RelativeIntent:
1145        return 1;
1146
1147     case SaturationIntent:
1148        return 2;
1149
1150     case AbsoluteIntent:
1151        return 3;
1152
1153     default:
1154        return -1;
1155   }
1156 }
1157
1158 static RenderingIntent
1159 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1160 {
1161   switch (ping_intent)
1162   {
1163     case 0:
1164       return PerceptualIntent;
1165
1166     case 1:
1167       return RelativeIntent;
1168
1169     case 2:
1170       return SaturationIntent;
1171
1172     case 3:
1173       return AbsoluteIntent;
1174
1175     default:
1176       return UndefinedIntent;
1177     }
1178 }
1179
1180 static const char *
1181 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1182 {
1183   switch (ping_intent)
1184   {
1185     case 0:
1186       return "Perceptual Intent";
1187
1188     case 1:
1189       return "Relative Intent";
1190
1191     case 2:
1192       return "Saturation Intent";
1193
1194     case 3:
1195       return "Absolute Intent";
1196
1197     default:
1198       return "Undefined Intent";
1199     }
1200 }
1201
1202 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1203 {
1204   if (x > y)
1205     return(x);
1206
1207   return(y);
1208 }
1209
1210 static const char *
1211 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1212 {
1213   switch (ping_colortype)
1214   {
1215     case 0:
1216       return "Grayscale";
1217
1218     case 2:
1219       return "Truecolor";
1220
1221     case 3:
1222       return "Indexed";
1223
1224     case 4:
1225       return "GrayAlpha";
1226
1227     case 6:
1228       return "RGBA";
1229
1230     default:
1231       return "UndefinedColorType";
1232     }
1233 }
1234
1235
1236 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1237 {
1238   if (x < y)
1239     return(x);
1240
1241   return(y);
1242 }
1243 #endif /* PNG_LIBPNG_VER > 10011 */
1244 #endif /* MAGICKCORE_PNG_DELEGATE */
1245 \f
1246 /*
1247 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1248 %                                                                             %
1249 %                                                                             %
1250 %                                                                             %
1251 %   I s M N G                                                                 %
1252 %                                                                             %
1253 %                                                                             %
1254 %                                                                             %
1255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1256 %
1257 %  IsMNG() returns MagickTrue if the image format type, identified by the
1258 %  magick string, is MNG.
1259 %
1260 %  The format of the IsMNG method is:
1261 %
1262 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1263 %
1264 %  A description of each parameter follows:
1265 %
1266 %    o magick: compare image format pattern against these bytes.
1267 %
1268 %    o length: Specifies the length of the magick string.
1269 %
1270 %
1271 */
1272 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1273 {
1274   if (length < 8)
1275     return(MagickFalse);
1276
1277   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1278     return(MagickTrue);
1279
1280   return(MagickFalse);
1281 }
1282 \f
1283 /*
1284 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1285 %                                                                             %
1286 %                                                                             %
1287 %                                                                             %
1288 %   I s J N G                                                                 %
1289 %                                                                             %
1290 %                                                                             %
1291 %                                                                             %
1292 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1293 %
1294 %  IsJNG() returns MagickTrue if the image format type, identified by the
1295 %  magick string, is JNG.
1296 %
1297 %  The format of the IsJNG method is:
1298 %
1299 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1300 %
1301 %  A description of each parameter follows:
1302 %
1303 %    o magick: compare image format pattern against these bytes.
1304 %
1305 %    o length: Specifies the length of the magick string.
1306 %
1307 %
1308 */
1309 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1310 {
1311   if (length < 8)
1312     return(MagickFalse);
1313
1314   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1315     return(MagickTrue);
1316
1317   return(MagickFalse);
1318 }
1319 \f
1320 /*
1321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1322 %                                                                             %
1323 %                                                                             %
1324 %                                                                             %
1325 %   I s P N G                                                                 %
1326 %                                                                             %
1327 %                                                                             %
1328 %                                                                             %
1329 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1330 %
1331 %  IsPNG() returns MagickTrue if the image format type, identified by the
1332 %  magick string, is PNG.
1333 %
1334 %  The format of the IsPNG method is:
1335 %
1336 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1337 %
1338 %  A description of each parameter follows:
1339 %
1340 %    o magick: compare image format pattern against these bytes.
1341 %
1342 %    o length: Specifies the length of the magick string.
1343 %
1344 */
1345 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1346 {
1347   if (length < 8)
1348     return(MagickFalse);
1349
1350   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1351     return(MagickTrue);
1352
1353   return(MagickFalse);
1354 }
1355 \f
1356 #if defined(MAGICKCORE_PNG_DELEGATE)
1357 #if defined(__cplusplus) || defined(c_plusplus)
1358 extern "C" {
1359 #endif
1360
1361 #if (PNG_LIBPNG_VER > 10011)
1362 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1363 {
1364   unsigned char
1365     buffer[4];
1366
1367   assert(image != (Image *) NULL);
1368   assert(image->signature == MagickSignature);
1369   buffer[0]=(unsigned char) (value >> 24);
1370   buffer[1]=(unsigned char) (value >> 16);
1371   buffer[2]=(unsigned char) (value >> 8);
1372   buffer[3]=(unsigned char) value;
1373   return((size_t) WriteBlob(image,4,buffer));
1374 }
1375
1376 static void PNGLong(png_bytep p,png_uint_32 value)
1377 {
1378   *p++=(png_byte) ((value >> 24) & 0xff);
1379   *p++=(png_byte) ((value >> 16) & 0xff);
1380   *p++=(png_byte) ((value >> 8) & 0xff);
1381   *p++=(png_byte) (value & 0xff);
1382 }
1383
1384 #if defined(JNG_SUPPORTED)
1385 static void PNGsLong(png_bytep p,png_int_32 value)
1386 {
1387   *p++=(png_byte) ((value >> 24) & 0xff);
1388   *p++=(png_byte) ((value >> 16) & 0xff);
1389   *p++=(png_byte) ((value >> 8) & 0xff);
1390   *p++=(png_byte) (value & 0xff);
1391 }
1392 #endif
1393
1394 static void PNGShort(png_bytep p,png_uint_16 value)
1395 {
1396   *p++=(png_byte) ((value >> 8) & 0xff);
1397   *p++=(png_byte) (value & 0xff);
1398 }
1399
1400 static void PNGType(png_bytep p,png_bytep type)
1401 {
1402   (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1403 }
1404
1405 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1406    size_t length)
1407 {
1408   if (logging != MagickFalse)
1409     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1410       "  Writing %c%c%c%c chunk, length: %.20g",
1411       type[0],type[1],type[2],type[3],(double) length);
1412 }
1413 #endif /* PNG_LIBPNG_VER > 10011 */
1414
1415 #if defined(__cplusplus) || defined(c_plusplus)
1416 }
1417 #endif
1418
1419 #if PNG_LIBPNG_VER > 10011
1420 /*
1421 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1422 %                                                                             %
1423 %                                                                             %
1424 %                                                                             %
1425 %   R e a d P N G I m a g e                                                   %
1426 %                                                                             %
1427 %                                                                             %
1428 %                                                                             %
1429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1430 %
1431 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1432 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1433 %  allocates the memory necessary for the new Image structure and returns a
1434 %  pointer to the new image or set of images.
1435 %
1436 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1437 %
1438 %  The format of the ReadPNGImage method is:
1439 %
1440 %      Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1441 %
1442 %  A description of each parameter follows:
1443 %
1444 %    o image_info: the image info.
1445 %
1446 %    o exception: return any errors or warnings in this structure.
1447 %
1448 %  To do, more or less in chronological order (as of version 5.5.2,
1449 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1450 %
1451 %    Get 16-bit cheap transparency working.
1452 %
1453 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1454 %
1455 %    Preserve all unknown and not-yet-handled known chunks found in input
1456 %    PNG file and copy them into output PNG files according to the PNG
1457 %    copying rules.
1458 %
1459 %    (At this point, PNG encoding should be in full MNG compliance)
1460 %
1461 %    Provide options for choice of background to use when the MNG BACK
1462 %    chunk is not present or is not mandatory (i.e., leave transparent,
1463 %    user specified, MNG BACK, PNG bKGD)
1464 %
1465 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1466 %    efficiently by linking in the duplicate frames.].
1467 %
1468 %    Decode and act on the MHDR simplicity profile (offer option to reject
1469 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1470 %
1471 %    Upgrade to full MNG without Delta-PNG.
1472 %
1473 %        o  BACK [done a while ago except for background image ID]
1474 %        o  MOVE [done 15 May 1999]
1475 %        o  CLIP [done 15 May 1999]
1476 %        o  DISC [done 19 May 1999]
1477 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1478 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1479 %        o  SHOW
1480 %        o  PAST
1481 %        o  BASI
1482 %        o  MNG-level tEXt/iTXt/zTXt
1483 %        o  pHYg
1484 %        o  pHYs
1485 %        o  sBIT
1486 %        o  bKGD
1487 %        o  iTXt (wait for libpng implementation).
1488 %
1489 %    Use the scene signature to discover when an identical scene is
1490 %    being reused, and just point to the original image->exception instead
1491 %    of storing another set of pixels.  This not specific to MNG
1492 %    but could be applied generally.
1493 %
1494 %    Upgrade to full MNG with Delta-PNG.
1495 %
1496 %    JNG tEXt/iTXt/zTXt
1497 %
1498 %    We will not attempt to read files containing the CgBI chunk.
1499 %    They are really Xcode files meant for display on the iPhone.
1500 %    These are not valid PNG files and it is impossible to recover
1501 %    the original PNG from files that have been converted to Xcode-PNG,
1502 %    since irretrievable loss of color data has occurred due to the
1503 %    use of premultiplied alpha.
1504 */
1505
1506 #if defined(__cplusplus) || defined(c_plusplus)
1507 extern "C" {
1508 #endif
1509
1510 /*
1511   This the function that does the actual reading of data.  It is
1512   the same as the one supplied in libpng, except that it receives the
1513   datastream from the ReadBlob() function instead of standard input.
1514 */
1515 static void png_get_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)
1522     {
1523       png_size_t
1524         check;
1525
1526       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1527       if (check != length)
1528         {
1529           char
1530             msg[MaxTextExtent];
1531
1532           (void) FormatLocaleString(msg,MaxTextExtent,
1533             "Expected %.20g bytes; found %.20g bytes",(double) length,
1534             (double) check);
1535           png_warning(png_ptr,msg);
1536           png_error(png_ptr,"Read Exception");
1537         }
1538     }
1539 }
1540
1541 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1542     !defined(PNG_MNG_FEATURES_SUPPORTED)
1543 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1544  * older than libpng-1.0.3a, which was the first to allow the empty
1545  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1546  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1547  * encountered after an empty PLTE, so we have to look ahead for bKGD
1548  * chunks and remove them from the datastream that is passed to libpng,
1549  * and store their contents for later use.
1550  */
1551 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1552 {
1553   MngInfo
1554     *mng_info;
1555
1556   Image
1557     *image;
1558
1559   png_size_t
1560     check;
1561
1562   register ssize_t
1563     i;
1564
1565   i=0;
1566   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1567   image=(Image *) mng_info->image;
1568   while (mng_info->bytes_in_read_buffer && length)
1569   {
1570     data[i]=mng_info->read_buffer[i];
1571     mng_info->bytes_in_read_buffer--;
1572     length--;
1573     i++;
1574   }
1575   if (length)
1576     {
1577       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1578
1579       if (check != length)
1580         png_error(png_ptr,"Read Exception");
1581
1582       if (length == 4)
1583         {
1584           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1585               (data[3] == 0))
1586             {
1587               check=(png_size_t) ReadBlob(image,(size_t) length,
1588                 (char *) mng_info->read_buffer);
1589               mng_info->read_buffer[4]=0;
1590               mng_info->bytes_in_read_buffer=4;
1591               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1592                 mng_info->found_empty_plte=MagickTrue;
1593               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1594                 {
1595                   mng_info->found_empty_plte=MagickFalse;
1596                   mng_info->have_saved_bkgd_index=MagickFalse;
1597                 }
1598             }
1599
1600           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1601               (data[3] == 1))
1602             {
1603               check=(png_size_t) ReadBlob(image,(size_t) length,
1604                 (char *) mng_info->read_buffer);
1605               mng_info->read_buffer[4]=0;
1606               mng_info->bytes_in_read_buffer=4;
1607               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1608                 if (mng_info->found_empty_plte)
1609                   {
1610                     /*
1611                       Skip the bKGD data byte and CRC.
1612                     */
1613                     check=(png_size_t)
1614                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1615                     check=(png_size_t) ReadBlob(image,(size_t) length,
1616                       (char *) mng_info->read_buffer);
1617                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1618                     mng_info->have_saved_bkgd_index=MagickTrue;
1619                     mng_info->bytes_in_read_buffer=0;
1620                   }
1621             }
1622         }
1623     }
1624 }
1625 #endif
1626
1627 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1628 {
1629   Image
1630     *image;
1631
1632   image=(Image *) png_get_io_ptr(png_ptr);
1633   if (length)
1634     {
1635       png_size_t
1636         check;
1637
1638       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1639
1640       if (check != length)
1641         png_error(png_ptr,"WriteBlob Failed");
1642     }
1643 }
1644
1645 static void png_flush_data(png_structp png_ptr)
1646 {
1647   (void) png_ptr;
1648 }
1649
1650 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1651 static int PalettesAreEqual(Image *a,Image *b)
1652 {
1653   ssize_t
1654     i;
1655
1656   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1657     return((int) MagickFalse);
1658
1659   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1660     return((int) MagickFalse);
1661
1662   if (a->colors != b->colors)
1663     return((int) MagickFalse);
1664
1665   for (i=0; i < (ssize_t) a->colors; i++)
1666   {
1667     if ((a->colormap[i].red != b->colormap[i].red) ||
1668         (a->colormap[i].green != b->colormap[i].green) ||
1669         (a->colormap[i].blue != b->colormap[i].blue))
1670       return((int) MagickFalse);
1671   }
1672
1673   return((int) MagickTrue);
1674 }
1675 #endif
1676
1677 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1678 {
1679   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1680       mng_info->exists[i] && !mng_info->frozen[i])
1681     {
1682 #ifdef MNG_OBJECT_BUFFERS
1683       if (mng_info->ob[i] != (MngBuffer *) NULL)
1684         {
1685           if (mng_info->ob[i]->reference_count > 0)
1686             mng_info->ob[i]->reference_count--;
1687
1688           if (mng_info->ob[i]->reference_count == 0)
1689             {
1690               if (mng_info->ob[i]->image != (Image *) NULL)
1691                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1692
1693               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1694             }
1695         }
1696       mng_info->ob[i]=(MngBuffer *) NULL;
1697 #endif
1698       mng_info->exists[i]=MagickFalse;
1699       mng_info->invisible[i]=MagickFalse;
1700       mng_info->viewable[i]=MagickFalse;
1701       mng_info->frozen[i]=MagickFalse;
1702       mng_info->x_off[i]=0;
1703       mng_info->y_off[i]=0;
1704       mng_info->object_clip[i].left=0;
1705       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1706       mng_info->object_clip[i].top=0;
1707       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1708     }
1709 }
1710
1711 static void MngInfoFreeStruct(MngInfo *mng_info,
1712     MagickBooleanType *have_mng_structure)
1713 {
1714   if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1715     {
1716       register ssize_t
1717         i;
1718
1719       for (i=1; i < MNG_MAX_OBJECTS; i++)
1720         MngInfoDiscardObject(mng_info,i);
1721
1722       if (mng_info->global_plte != (png_colorp) NULL)
1723         mng_info->global_plte=(png_colorp)
1724           RelinquishMagickMemory(mng_info->global_plte);
1725
1726       mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1727       *have_mng_structure=MagickFalse;
1728     }
1729 }
1730
1731 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1732 {
1733   MngBox
1734     box;
1735
1736   box=box1;
1737   if (box.left < box2.left)
1738     box.left=box2.left;
1739
1740   if (box.top < box2.top)
1741     box.top=box2.top;
1742
1743   if (box.right > box2.right)
1744     box.right=box2.right;
1745
1746   if (box.bottom > box2.bottom)
1747     box.bottom=box2.bottom;
1748
1749   return box;
1750 }
1751
1752 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1753 {
1754    MngBox
1755       box;
1756
1757   /*
1758     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1759   */
1760   box.left=(ssize_t) ((p[0]  << 24) | (p[1]  << 16) | (p[2]  << 8) | p[3]);
1761   box.right=(ssize_t) ((p[4]  << 24) | (p[5]  << 16) | (p[6]  << 8) | p[7]);
1762   box.top=(ssize_t) ((p[8]  << 24) | (p[9]  << 16) | (p[10] << 8) | p[11]);
1763   box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1764   if (delta_type != 0)
1765     {
1766       box.left+=previous_box.left;
1767       box.right+=previous_box.right;
1768       box.top+=previous_box.top;
1769       box.bottom+=previous_box.bottom;
1770     }
1771
1772   return(box);
1773 }
1774
1775 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1776   unsigned char *p)
1777 {
1778   MngPair
1779     pair;
1780   /*
1781     Read two ssize_ts from CLON, MOVE or PAST chunk
1782   */
1783   pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1784   pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1785
1786   if (delta_type != 0)
1787     {
1788       pair.a+=previous_pair.a;
1789       pair.b+=previous_pair.b;
1790     }
1791
1792   return(pair);
1793 }
1794
1795 static long mng_get_long(unsigned char *p)
1796 {
1797   return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1798 }
1799
1800 typedef struct _PNGErrorInfo
1801 {
1802   Image
1803     *image;
1804
1805   ExceptionInfo
1806     *exception;
1807 } PNGErrorInfo;
1808
1809 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1810 {
1811   ExceptionInfo
1812     *exception;
1813
1814   Image
1815     *image;
1816
1817   PNGErrorInfo
1818     *error_info;
1819
1820   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1821   image=error_info->image;
1822   exception=error_info->exception;
1823
1824   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1825     "  libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1826
1827   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1828     "`%s'",image->filename);
1829
1830 #if (PNG_LIBPNG_VER < 10500)
1831   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1832    * are building with libpng-1.4.x and can be ignored.
1833    */
1834   longjmp(ping->jmpbuf,1);
1835 #else
1836   png_longjmp(ping,1);
1837 #endif
1838 }
1839
1840 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1841 {
1842   ExceptionInfo
1843     *exception;
1844
1845   Image
1846     *image;
1847
1848   PNGErrorInfo
1849     *error_info;
1850
1851   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1852     png_error(ping, message);
1853
1854   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1855   image=error_info->image;
1856   exception=error_info->exception;
1857   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1858     "  libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1859
1860   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1861     message,"`%s'",image->filename);
1862 }
1863
1864 #ifdef PNG_USER_MEM_SUPPORTED
1865 #if PNG_LIBPNG_VER >= 10400
1866 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1867 #else
1868 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1869 #endif
1870 {
1871   (void) png_ptr;
1872   return((png_voidp) AcquireMagickMemory((size_t) size));
1873 }
1874
1875 /*
1876   Free a pointer.  It is removed from the list at the same time.
1877 */
1878 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1879 {
1880   (void) png_ptr;
1881   ptr=RelinquishMagickMemory(ptr);
1882   return((png_free_ptr) NULL);
1883 }
1884 #endif
1885
1886 #if defined(__cplusplus) || defined(c_plusplus)
1887 }
1888 #endif
1889
1890 static int
1891 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1892    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1893 {
1894   register ssize_t
1895     i;
1896
1897   register unsigned char
1898     *dp;
1899
1900   register png_charp
1901     sp;
1902
1903   png_uint_32
1904     length,
1905     nibbles;
1906
1907   StringInfo
1908     *profile;
1909
1910   const unsigned char
1911     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1912                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1913                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1914                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1915                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1916                  13,14,15};
1917
1918   sp=text[ii].text+1;
1919   /* look for newline */
1920   while (*sp != '\n')
1921      sp++;
1922
1923   /* look for length */
1924   while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1925      sp++;
1926
1927   length=(png_uint_32) StringToLong(sp);
1928
1929   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1930        "      length: %lu",(unsigned long) length);
1931
1932   while (*sp != ' ' && *sp != '\n')
1933      sp++;
1934
1935   /* allocate space */
1936   if (length == 0)
1937   {
1938     png_warning(ping,"invalid profile length");
1939     return(MagickFalse);
1940   }
1941
1942   profile=BlobToStringInfo((const void *) NULL,length);
1943
1944   if (profile == (StringInfo *) NULL)
1945   {
1946     png_warning(ping, "unable to copy profile");
1947     return(MagickFalse);
1948   }
1949
1950   /* copy profile, skipping white space and column 1 "=" signs */
1951   dp=GetStringInfoDatum(profile);
1952   nibbles=length*2;
1953
1954   for (i=0; i < (ssize_t) nibbles; i++)
1955   {
1956     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1957     {
1958       if (*sp == '\0')
1959         {
1960           png_warning(ping, "ran out of profile data");
1961           profile=DestroyStringInfo(profile);
1962           return(MagickFalse);
1963         }
1964       sp++;
1965     }
1966
1967     if (i%2 == 0)
1968       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1969
1970     else
1971       (*dp++)+=unhex[(int) *sp++];
1972   }
1973   /*
1974     We have already read "Raw profile type.
1975   */
1976   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1977   profile=DestroyStringInfo(profile);
1978
1979   if (image_info->verbose)
1980     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1981
1982   return MagickTrue;
1983 }
1984
1985 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1986 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1987 {
1988   Image
1989     *image;
1990
1991
1992   /* The unknown chunk structure contains the chunk data:
1993      png_byte name[5];
1994      png_byte *data;
1995      png_size_t size;
1996
1997      Note that libpng has already taken care of the CRC handling.
1998   */
1999
2000   LogMagickEvent(CoderEvent,GetMagickModule(),
2001      " read_vpag_chunk: found %c%c%c%c chunk",
2002        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
2003
2004   if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
2005       chunk->name[2] != 65 ||chunk-> name[3] != 103)
2006     return(0); /* Did not recognize */
2007
2008   /* recognized vpAg */
2009
2010   if (chunk->size != 9)
2011     return(-1); /* Error return */
2012
2013   if (chunk->data[8] != 0)
2014     return(0);  /* ImageMagick requires pixel units */
2015
2016   image=(Image *) png_get_user_chunk_ptr(ping);
2017
2018   image->page.width=(size_t) ((chunk->data[0] << 24) |
2019      (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
2020
2021   image->page.height=(size_t) ((chunk->data[4] << 24) |
2022      (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
2023
2024   /* Return one of the following: */
2025      /* return(-n);  chunk had an error */
2026      /* return(0);  did not recognize */
2027      /* return(n);  success */
2028
2029   return(1);
2030
2031 }
2032 #endif
2033
2034 /*
2035 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2036 %                                                                             %
2037 %                                                                             %
2038 %                                                                             %
2039 %   R e a d O n e P N G I m a g e                                             %
2040 %                                                                             %
2041 %                                                                             %
2042 %                                                                             %
2043 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2044 %
2045 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2046 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
2047 %  necessary for the new Image structure and returns a pointer to the new
2048 %  image.
2049 %
2050 %  The format of the ReadOnePNGImage method is:
2051 %
2052 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2053 %         ExceptionInfo *exception)
2054 %
2055 %  A description of each parameter follows:
2056 %
2057 %    o mng_info: Specifies a pointer to a MngInfo structure.
2058 %
2059 %    o image_info: the image info.
2060 %
2061 %    o exception: return any errors or warnings in this structure.
2062 %
2063 */
2064 static Image *ReadOnePNGImage(MngInfo *mng_info,
2065     const ImageInfo *image_info, ExceptionInfo *exception)
2066 {
2067   /* Read one PNG image */
2068
2069   /* To do: Read the tIME chunk into the date:modify property */
2070   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2071
2072   Image
2073     *image;
2074
2075   char
2076     im_vers[32],
2077     libpng_runv[32],
2078     libpng_vers[32],
2079     zlib_runv[32],
2080     zlib_vers[32];
2081
2082   int
2083     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2084     num_raw_profiles,
2085     num_text,
2086     num_text_total,
2087     num_passes,
2088     number_colors,
2089     pass,
2090     ping_bit_depth,
2091     ping_color_type,
2092     ping_file_depth,
2093     ping_interlace_method,
2094     ping_compression_method,
2095     ping_filter_method,
2096     ping_num_trans,
2097     unit_type;
2098
2099   double
2100     file_gamma;
2101
2102   MagickBooleanType
2103     logging,
2104     ping_found_cHRM,
2105     ping_found_gAMA,
2106     ping_found_iCCP,
2107     ping_found_sRGB,
2108     ping_found_sRGB_cHRM,
2109     ping_preserve_iCCP,
2110     status;
2111
2112   MemoryInfo
2113     *volatile pixel_info;
2114
2115   PixelInfo
2116     transparent_color;
2117
2118   PNGErrorInfo
2119     error_info;
2120
2121   png_bytep
2122      ping_trans_alpha;
2123
2124   png_color_16p
2125      ping_background,
2126      ping_trans_color;
2127
2128   png_info
2129     *end_info,
2130     *ping_info;
2131
2132   png_struct
2133     *ping;
2134
2135   png_textp
2136     text;
2137
2138   png_uint_32
2139     ping_height,
2140     ping_width,
2141     x_resolution,
2142     y_resolution;
2143
2144   QuantumInfo
2145     *quantum_info;
2146
2147   ssize_t
2148     ping_rowbytes,
2149     y;
2150
2151   register unsigned char
2152     *p;
2153
2154   register ssize_t
2155     i,
2156     x;
2157
2158   register Quantum
2159     *q;
2160
2161   size_t
2162     length,
2163     row_offset;
2164
2165   ssize_t
2166     j;
2167
2168   unsigned char
2169     *ping_pixels;
2170
2171 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2172   png_byte unused_chunks[]=
2173   {
2174     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2175     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2176     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2177     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2178     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2179     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2180 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2181                           /* ignore the APNG chunks */
2182      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2183     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2184     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2185 #endif
2186   };
2187 #endif
2188
2189   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2190     "  Enter ReadOnePNGImage()");
2191
2192   /* Define these outside of the following "if logging()" block so they will
2193    * show in debuggers.
2194    */
2195   *im_vers='\0';
2196   (void) ConcatenateMagickString(im_vers,
2197          MagickLibVersionText,32);
2198   (void) ConcatenateMagickString(im_vers,
2199          MagickLibAddendum,32);
2200
2201   *libpng_vers='\0';
2202   (void) ConcatenateMagickString(libpng_vers,
2203          PNG_LIBPNG_VER_STRING,32);
2204   *libpng_runv='\0';
2205   (void) ConcatenateMagickString(libpng_runv,
2206          png_get_libpng_ver(NULL),32);
2207
2208   *zlib_vers='\0';
2209   (void) ConcatenateMagickString(zlib_vers,
2210          ZLIB_VERSION,32);
2211   *zlib_runv='\0';
2212   (void) ConcatenateMagickString(zlib_runv,
2213          zlib_version,32);
2214
2215   if (logging)
2216     {
2217        LogMagickEvent(CoderEvent,GetMagickModule(),"    IM version     = %s",
2218            im_vers);
2219        LogMagickEvent(CoderEvent,GetMagickModule(),"    Libpng version = %s",
2220            libpng_vers);
2221        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2222        {
2223        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2224            libpng_runv);
2225        }
2226        LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
2227            zlib_vers);
2228        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2229        {
2230        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2231            zlib_runv);
2232        }
2233     }
2234
2235 #if (PNG_LIBPNG_VER < 10200)
2236   if (image_info->verbose)
2237     printf("Your PNG library (libpng-%s) is rather old.\n",
2238        PNG_LIBPNG_VER_STRING);
2239 #endif
2240
2241 #if (PNG_LIBPNG_VER >= 10400)
2242 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
2243   if (image_info->verbose)
2244     {
2245       printf("Your PNG library (libpng-%s) is an old beta version.\n",
2246            PNG_LIBPNG_VER_STRING);
2247       printf("Please update it.\n");
2248     }
2249 #  endif
2250 #endif
2251
2252
2253   quantum_info = (QuantumInfo *) NULL;
2254   image=mng_info->image;
2255
2256   if (logging != MagickFalse)
2257   {
2258     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2259       "    Before reading:");
2260
2261     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2262       "      image->alpha_trait=%d",(int) image->alpha_trait);
2263
2264     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2265       "      image->rendering_intent=%d",(int) image->rendering_intent);
2266
2267     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2268       "      image->colorspace=%d",(int) image->colorspace);
2269
2270     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2271       "      image->gamma=%f", image->gamma);
2272   }
2273   intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2274
2275   /* Set to an out-of-range color unless tRNS chunk is present */
2276   transparent_color.red=65537;
2277   transparent_color.green=65537;
2278   transparent_color.blue=65537;
2279   transparent_color.alpha=65537;
2280
2281   number_colors=0;
2282   num_text = 0;
2283   num_text_total = 0;
2284   num_raw_profiles = 0;
2285
2286   ping_found_cHRM = MagickFalse;
2287   ping_found_gAMA = MagickFalse;
2288   ping_found_iCCP = MagickFalse;
2289   ping_found_sRGB = MagickFalse;
2290   ping_found_sRGB_cHRM = MagickFalse;
2291   ping_preserve_iCCP = MagickFalse;
2292
2293   {
2294     const char
2295       *value;
2296
2297     value=GetImageOption(image_info,"png:preserve-iCCP");
2298
2299     if (value == NULL)
2300        value=GetImageArtifact(image,"png:preserve-iCCP");
2301
2302     if (value != NULL)
2303        ping_preserve_iCCP=MagickTrue;
2304   }
2305
2306   /*
2307     Allocate the PNG structures
2308   */
2309 #ifdef PNG_USER_MEM_SUPPORTED
2310  error_info.image=image;
2311  error_info.exception=exception;
2312  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2313    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2314    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2315 #else
2316   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2317     MagickPNGErrorHandler,MagickPNGWarningHandler);
2318 #endif
2319   if (ping == (png_struct *) NULL)
2320     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2321
2322   ping_info=png_create_info_struct(ping);
2323
2324   if (ping_info == (png_info *) NULL)
2325     {
2326       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2327       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2328     }
2329
2330   end_info=png_create_info_struct(ping);
2331
2332   if (end_info == (png_info *) NULL)
2333     {
2334       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2335       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2336     }
2337
2338   pixel_info=(MemoryInfo *) NULL;
2339
2340   if (setjmp(png_jmpbuf(ping)))
2341     {
2342       /*
2343         PNG image is corrupt.
2344       */
2345       png_destroy_read_struct(&ping,&ping_info,&end_info);
2346
2347 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2348       UnlockSemaphoreInfo(ping_semaphore);
2349 #endif
2350
2351       if (pixel_info != (MemoryInfo *) NULL)
2352         pixel_info=RelinquishVirtualMemory(pixel_info);
2353
2354       if (logging != MagickFalse)
2355         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2356           "  exit ReadOnePNGImage() with error.");
2357
2358       if (image != (Image *) NULL)
2359         {
2360           InheritException(exception,exception);
2361           image->columns=0;
2362         }
2363
2364       return(GetFirstImageInList(image));
2365     }
2366
2367   /* {  For navigation to end of SETJMP-protected block.  Within this
2368    *    block, use png_error() instead of Throwing an Exception, to ensure
2369    *    that libpng is able to clean up, and that the semaphore is unlocked.
2370    */
2371
2372 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2373   LockSemaphoreInfo(ping_semaphore);
2374 #endif
2375
2376 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2377   /* Allow benign errors */
2378   png_set_benign_errors(ping, 1);
2379 #endif
2380
2381   /*
2382     Prepare PNG for reading.
2383   */
2384
2385   mng_info->image_found++;
2386   png_set_sig_bytes(ping,8);
2387
2388   if (LocaleCompare(image_info->magick,"MNG") == 0)
2389     {
2390 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2391       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2392       png_set_read_fn(ping,image,png_get_data);
2393 #else
2394 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2395       png_permit_empty_plte(ping,MagickTrue);
2396       png_set_read_fn(ping,image,png_get_data);
2397 #else
2398       mng_info->image=image;
2399       mng_info->bytes_in_read_buffer=0;
2400       mng_info->found_empty_plte=MagickFalse;
2401       mng_info->have_saved_bkgd_index=MagickFalse;
2402       png_set_read_fn(ping,mng_info,mng_get_data);
2403 #endif
2404 #endif
2405     }
2406
2407   else
2408     png_set_read_fn(ping,image,png_get_data);
2409
2410 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2411   /* Ignore unused chunks and all unknown chunks except for vpAg */
2412 #if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2413   png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2414 #else
2415   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2416 #endif
2417   png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2418   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2419      (int)sizeof(unused_chunks)/5);
2420   /* Callback for other unknown chunks */
2421   png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2422 #endif
2423
2424 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2425 #  if (PNG_LIBPNG_VER >= 10400)
2426     /* Limit the size of the chunk storage cache used for sPLT, text,
2427      * and unknown chunks.
2428      */
2429     png_set_chunk_cache_max(ping, 32767);
2430 #  endif
2431 #endif
2432
2433 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2434     /* Disable new libpng-1.5.10 feature */
2435     png_set_check_for_invalid_index (ping, 0);
2436 #endif
2437
2438 #if (PNG_LIBPNG_VER < 10400)
2439 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2440    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2441   /* Disable thread-unsafe features of pnggccrd */
2442   if (png_access_version_number() >= 10200)
2443   {
2444     png_uint_32 mmx_disable_mask=0;
2445     png_uint_32 asm_flags;
2446
2447     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2448                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2449                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2450                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2451     asm_flags=png_get_asm_flags(ping);
2452     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2453   }
2454 #  endif
2455 #endif
2456
2457   png_read_info(ping,ping_info);
2458
2459   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2460                &ping_bit_depth,&ping_color_type,
2461                &ping_interlace_method,&ping_compression_method,
2462                &ping_filter_method);
2463
2464   ping_file_depth = ping_bit_depth;
2465
2466   /* Swap bytes if requested */
2467   if (ping_file_depth == 16)
2468   {
2469      const char
2470        *value;
2471  
2472      value=GetImageOption(image_info,"png:swap-bytes");
2473  
2474      if (value == NULL)
2475         value=GetImageArtifact(image,"png:swap-bytes");
2476  
2477      if (value != NULL)
2478         png_set_swap(ping);
2479   }
2480
2481   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2482   {
2483       char
2484         msg[MaxTextExtent];
2485
2486       (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2487       (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2488
2489       (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2490       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2491   }
2492
2493   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2494                       &ping_trans_color);
2495
2496   (void) png_get_bKGD(ping, ping_info, &ping_background);
2497
2498   if (ping_bit_depth < 8)
2499     {
2500        png_set_packing(ping);
2501        ping_bit_depth = 8;
2502     }
2503
2504   image->depth=ping_bit_depth;
2505   image->depth=GetImageQuantumDepth(image,MagickFalse);
2506   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2507
2508   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2509       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2510     {
2511       image->rendering_intent=UndefinedIntent;
2512       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2513       image->gamma=1.000;
2514       (void) ResetMagickMemory(&image->chromaticity,0,
2515         sizeof(image->chromaticity));
2516     }
2517
2518   if (logging != MagickFalse)
2519     {
2520       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2521         "    PNG width: %.20g, height: %.20g",
2522         (double) ping_width, (double) ping_height);
2523
2524       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2525         "    PNG color_type: %d, bit_depth: %d",
2526         ping_color_type, ping_bit_depth);
2527
2528       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2529         "    PNG compression_method: %d",
2530         ping_compression_method);
2531
2532       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2533         "    PNG interlace_method: %d, filter_method: %d",
2534         ping_interlace_method,ping_filter_method);
2535     }
2536
2537   if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2538     {
2539       ping_found_iCCP=MagickTrue;
2540       if (logging != MagickFalse)
2541         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2542           "    Found PNG iCCP chunk.");
2543     }
2544
2545   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2546     {
2547       ping_found_gAMA=MagickTrue;
2548       if (logging != MagickFalse)
2549         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2550           "    Found PNG gAMA chunk.");
2551     }
2552
2553   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2554     {
2555       ping_found_cHRM=MagickTrue;
2556       if (logging != MagickFalse)
2557         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2558           "    Found PNG cHRM chunk.");
2559     }
2560
2561   if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2562       PNG_INFO_sRGB))
2563     {
2564       ping_found_sRGB=MagickTrue;
2565       if (logging != MagickFalse)
2566         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2567           "    Found PNG sRGB chunk.");
2568     }
2569
2570 #ifdef PNG_READ_iCCP_SUPPORTED
2571   if (ping_found_iCCP !=MagickTrue &&
2572       ping_found_sRGB != MagickTrue &&
2573       png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2574     {
2575       ping_found_iCCP=MagickTrue;
2576       if (logging != MagickFalse)
2577         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2578           "    Found PNG iCCP chunk.");
2579     }
2580
2581   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2582     {
2583       int
2584         compression;
2585
2586 #if (PNG_LIBPNG_VER < 10500)
2587       png_charp
2588         info;
2589 #else
2590       png_bytep
2591         info;
2592 #endif
2593
2594       png_charp
2595         name;
2596
2597       png_uint_32
2598         profile_length;
2599
2600       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2601         &profile_length);
2602
2603       if (profile_length != 0)
2604         {
2605           StringInfo
2606             *profile;
2607
2608           if (logging != MagickFalse)
2609             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2610               "    Reading PNG iCCP chunk.");
2611
2612           profile=BlobToStringInfo(info,profile_length);
2613
2614           if (profile == (StringInfo *) NULL)
2615           {
2616             png_warning(ping, "ICC profile is NULL");
2617             profile=DestroyStringInfo(profile);
2618           }
2619           else
2620           {
2621             if (ping_preserve_iCCP == MagickFalse)
2622             {
2623                  int
2624                    icheck,
2625                    got_crc=0;
2626
2627
2628                  png_uint_32
2629                    length,
2630                    profile_crc=0;
2631
2632                  unsigned char
2633                    *data;
2634
2635                  length=(png_uint_32) GetStringInfoLength(profile);
2636
2637                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2638                  {
2639                    if (length == sRGB_info[icheck].len)
2640                    {
2641                      if (got_crc == 0)
2642                      {
2643                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2644                          "    Got a %lu-byte ICC profile (potentially sRGB)",
2645                          (unsigned long) length);
2646
2647                        data=GetStringInfoDatum(profile);
2648                        profile_crc=crc32(0,data,length);
2649
2650                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2651                            "      with crc=%8x",(unsigned int) profile_crc);
2652                        got_crc++;
2653                      }
2654
2655                      if (profile_crc == sRGB_info[icheck].crc)
2656                      {
2657                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2658                             "      It is sRGB with rendering intent = %s",
2659                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
2660                              sRGB_info[icheck].intent));
2661                         if (image->rendering_intent==UndefinedIntent)
2662                         {
2663                           image->rendering_intent=
2664                           Magick_RenderingIntent_from_PNG_RenderingIntent(
2665                              sRGB_info[icheck].intent);
2666                         }
2667                         break;
2668                      }
2669                    }
2670                  }
2671                  if (sRGB_info[icheck].len == 0)
2672                  {
2673                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2674                         "    Got a %lu-byte ICC profile not recognized as sRGB",
2675                         (unsigned long) length);
2676                     (void) SetImageProfile(image,"icc",profile,exception);
2677                  }
2678             }
2679             else /* Preserve-iCCP */
2680             {
2681                     (void) SetImageProfile(image,"icc",profile,exception);
2682             }
2683
2684             profile=DestroyStringInfo(profile);
2685           }
2686       }
2687     }
2688 #endif
2689
2690 #if defined(PNG_READ_sRGB_SUPPORTED)
2691   {
2692     if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2693         PNG_INFO_sRGB))
2694     {
2695       if (png_get_sRGB(ping,ping_info,&intent))
2696       {
2697         if (image->rendering_intent == UndefinedIntent)
2698           image->rendering_intent=
2699              Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2700
2701         if (logging != MagickFalse)
2702           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2703             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2704       }
2705     }
2706
2707     else if (mng_info->have_global_srgb)
2708       {
2709         if (image->rendering_intent == UndefinedIntent)
2710           image->rendering_intent=
2711             Magick_RenderingIntent_from_PNG_RenderingIntent
2712             (mng_info->global_srgb_intent);
2713       }
2714   }
2715 #endif
2716
2717
2718   {
2719      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2720        if (mng_info->have_global_gama)
2721          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2722
2723      if (png_get_gAMA(ping,ping_info,&file_gamma))
2724        {
2725          image->gamma=(float) file_gamma;
2726          if (logging != MagickFalse)
2727            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2728              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2729        }
2730   }
2731
2732   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2733     {
2734       if (mng_info->have_global_chrm != MagickFalse)
2735         {
2736           (void) png_set_cHRM(ping,ping_info,
2737             mng_info->global_chrm.white_point.x,
2738             mng_info->global_chrm.white_point.y,
2739             mng_info->global_chrm.red_primary.x,
2740             mng_info->global_chrm.red_primary.y,
2741             mng_info->global_chrm.green_primary.x,
2742             mng_info->global_chrm.green_primary.y,
2743             mng_info->global_chrm.blue_primary.x,
2744             mng_info->global_chrm.blue_primary.y);
2745         }
2746     }
2747
2748   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2749     {
2750       (void) png_get_cHRM(ping,ping_info,
2751         &image->chromaticity.white_point.x,
2752         &image->chromaticity.white_point.y,
2753         &image->chromaticity.red_primary.x,
2754         &image->chromaticity.red_primary.y,
2755         &image->chromaticity.green_primary.x,
2756         &image->chromaticity.green_primary.y,
2757         &image->chromaticity.blue_primary.x,
2758         &image->chromaticity.blue_primary.y);
2759
2760        ping_found_cHRM=MagickTrue;
2761
2762        if (image->chromaticity.red_primary.x>0.6399f &&
2763            image->chromaticity.red_primary.x<0.6401f &&
2764            image->chromaticity.red_primary.y>0.3299f &&
2765            image->chromaticity.red_primary.y<0.3301f &&
2766            image->chromaticity.green_primary.x>0.2999f &&
2767            image->chromaticity.green_primary.x<0.3001f &&
2768            image->chromaticity.green_primary.y>0.5999f &&
2769            image->chromaticity.green_primary.y<0.6001f &&
2770            image->chromaticity.blue_primary.x>0.1499f &&
2771            image->chromaticity.blue_primary.x<0.1501f &&
2772            image->chromaticity.blue_primary.y>0.0599f &&
2773            image->chromaticity.blue_primary.y<0.0601f &&
2774            image->chromaticity.white_point.x>0.3126f &&
2775            image->chromaticity.white_point.x<0.3128f &&
2776            image->chromaticity.white_point.y>0.3289f &&
2777            image->chromaticity.white_point.y<0.3291f)
2778           ping_found_sRGB_cHRM=MagickTrue;
2779     }
2780
2781   if (image->rendering_intent != UndefinedIntent)
2782     {
2783       if (ping_found_sRGB != MagickTrue &&
2784           (ping_found_gAMA != MagickTrue ||
2785           (image->gamma > .45 && image->gamma < .46)) &&
2786           (ping_found_cHRM != MagickTrue ||
2787           ping_found_sRGB_cHRM != MagickFalse) &&
2788           ping_found_iCCP != MagickTrue)
2789       {
2790          png_set_sRGB(ping,ping_info,
2791             Magick_RenderingIntent_to_PNG_RenderingIntent
2792             (image->rendering_intent));
2793          file_gamma=1.000f/2.200f;
2794          ping_found_sRGB=MagickTrue;
2795          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2796            "    Setting sRGB as if in input");
2797       }
2798     }
2799
2800 #if defined(PNG_oFFs_SUPPORTED)
2801   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2802     {
2803       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2804       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2805
2806       if (logging != MagickFalse)
2807         if (image->page.x || image->page.y)
2808           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2809             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2810             image->page.x,(double) image->page.y);
2811     }
2812 #endif
2813 #if defined(PNG_pHYs_SUPPORTED)
2814   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2815     {
2816       if (mng_info->have_global_phys)
2817         {
2818           png_set_pHYs(ping,ping_info,
2819                        mng_info->global_x_pixels_per_unit,
2820                        mng_info->global_y_pixels_per_unit,
2821                        mng_info->global_phys_unit_type);
2822         }
2823     }
2824
2825   x_resolution=0;
2826   y_resolution=0;
2827   unit_type=0;
2828   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2829     {
2830       /*
2831         Set image resolution.
2832       */
2833       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2834         &unit_type);
2835       image->resolution.x=(double) x_resolution;
2836       image->resolution.y=(double) y_resolution;
2837
2838       if (unit_type == PNG_RESOLUTION_METER)
2839         {
2840           image->units=PixelsPerCentimeterResolution;
2841           image->resolution.x=(double) x_resolution/100.0;
2842           image->resolution.y=(double) y_resolution/100.0;
2843         }
2844
2845       if (logging != MagickFalse)
2846         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2847           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2848           (double) x_resolution,(double) y_resolution,unit_type);
2849     }
2850 #endif
2851
2852   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2853     {
2854       png_colorp
2855         palette;
2856
2857       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2858
2859       if ((number_colors == 0) &&
2860           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2861         {
2862           if (mng_info->global_plte_length)
2863             {
2864               png_set_PLTE(ping,ping_info,mng_info->global_plte,
2865                 (int) mng_info->global_plte_length);
2866
2867               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2868               {
2869                 if (mng_info->global_trns_length)
2870                   {
2871                     png_warning(ping,
2872                       "global tRNS has more entries than global PLTE");
2873                   }
2874                 else
2875                   {
2876                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
2877                        (int) mng_info->global_trns_length,NULL);
2878                   }
2879                }
2880 #ifdef PNG_READ_bKGD_SUPPORTED
2881               if (
2882 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2883                    mng_info->have_saved_bkgd_index ||
2884 #endif
2885                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2886                     {
2887                       png_color_16
2888                          background;
2889
2890 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2891                       if (mng_info->have_saved_bkgd_index)
2892                         background.index=mng_info->saved_bkgd_index;
2893 #endif
2894                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2895                         background.index=ping_background->index;
2896
2897                       background.red=(png_uint_16)
2898                         mng_info->global_plte[background.index].red;
2899
2900                       background.green=(png_uint_16)
2901                         mng_info->global_plte[background.index].green;
2902
2903                       background.blue=(png_uint_16)
2904                         mng_info->global_plte[background.index].blue;
2905
2906                       background.gray=(png_uint_16)
2907                         mng_info->global_plte[background.index].green;
2908
2909                       png_set_bKGD(ping,ping_info,&background);
2910                     }
2911 #endif
2912                 }
2913               else
2914                 png_error(ping,"No global PLTE in file");
2915             }
2916         }
2917
2918 #ifdef PNG_READ_bKGD_SUPPORTED
2919   if (mng_info->have_global_bkgd &&
2920           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2921       image->background_color=mng_info->mng_global_bkgd;
2922
2923   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2924     {
2925       unsigned int
2926         bkgd_scale;
2927
2928       /*
2929         Set image background color.
2930       */
2931       if (logging != MagickFalse)
2932         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2933           "    Reading PNG bKGD chunk.");
2934
2935       /* Scale background components to 16-bit, then scale
2936        * to quantum depth
2937        */
2938         if (logging != MagickFalse)
2939           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2940             "    raw ping_background=(%d,%d,%d).",ping_background->red,
2941             ping_background->green,ping_background->blue);
2942
2943         bkgd_scale = 1;
2944
2945         if (ping_file_depth == 1)
2946            bkgd_scale = 255;
2947
2948         else if (ping_file_depth == 2)
2949            bkgd_scale = 85;
2950
2951         else if (ping_file_depth == 4)
2952            bkgd_scale = 17;
2953
2954         if (ping_file_depth <= 8)
2955            bkgd_scale *= 257;
2956
2957         ping_background->red *= bkgd_scale;
2958         ping_background->green *= bkgd_scale;
2959         ping_background->blue *= bkgd_scale;
2960
2961         if (logging != MagickFalse)
2962           {
2963             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2964               "    bkgd_scale=%d.",bkgd_scale);
2965
2966             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2967               "    ping_background=(%d,%d,%d).",ping_background->red,
2968               ping_background->green,ping_background->blue);
2969           }
2970
2971         image->background_color.red=
2972             ScaleShortToQuantum(ping_background->red);
2973
2974         image->background_color.green=
2975             ScaleShortToQuantum(ping_background->green);
2976
2977         image->background_color.blue=
2978           ScaleShortToQuantum(ping_background->blue);
2979
2980         image->background_color.alpha=OpaqueAlpha;
2981
2982         if (logging != MagickFalse)
2983           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2984             "    image->background_color=(%.20g,%.20g,%.20g).",
2985             (double) image->background_color.red,
2986             (double) image->background_color.green,
2987             (double) image->background_color.blue);
2988     }
2989 #endif /* PNG_READ_bKGD_SUPPORTED */
2990
2991   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2992     {
2993       /*
2994         Image has a tRNS chunk.
2995       */
2996       int
2997         max_sample;
2998
2999       size_t
3000         one=1;
3001
3002       if (logging != MagickFalse)
3003         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3004           "    Reading PNG tRNS chunk.");
3005
3006       max_sample = (int) ((one << ping_file_depth) - 1);
3007
3008       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3009           (int)ping_trans_color->gray > max_sample) ||
3010           (ping_color_type == PNG_COLOR_TYPE_RGB &&
3011           ((int)ping_trans_color->red > max_sample ||
3012           (int)ping_trans_color->green > max_sample ||
3013           (int)ping_trans_color->blue > max_sample)))
3014         {
3015           if (logging != MagickFalse)
3016             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3017               "    Ignoring PNG tRNS chunk with out-of-range sample.");
3018           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3019           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3020           image->alpha_trait=UndefinedPixelTrait;
3021         }
3022       else
3023         {
3024           int
3025              scale_to_short;
3026
3027           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3028
3029           /* Scale transparent_color to short */
3030           transparent_color.red= scale_to_short*ping_trans_color->red;
3031           transparent_color.green= scale_to_short*ping_trans_color->green;
3032           transparent_color.blue= scale_to_short*ping_trans_color->blue;
3033           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3034
3035           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3036             {
3037               if (logging != MagickFalse)
3038               {
3039                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3040                   "    Raw tRNS graylevel is %d.",ping_trans_color->gray);
3041
3042                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3043                   "    scaled graylevel is %.20g.",transparent_color.alpha);
3044               }
3045               transparent_color.red=transparent_color.alpha;
3046               transparent_color.green=transparent_color.alpha;
3047               transparent_color.blue=transparent_color.alpha;
3048             }
3049         }
3050     }
3051 #if defined(PNG_READ_sBIT_SUPPORTED)
3052   if (mng_info->have_global_sbit)
3053     {
3054       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3055         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3056     }
3057 #endif
3058   num_passes=png_set_interlace_handling(ping);
3059
3060   png_read_update_info(ping,ping_info);
3061
3062   ping_rowbytes=png_get_rowbytes(ping,ping_info);
3063
3064   /*
3065     Initialize image structure.
3066   */
3067   mng_info->image_box.left=0;
3068   mng_info->image_box.right=(ssize_t) ping_width;
3069   mng_info->image_box.top=0;
3070   mng_info->image_box.bottom=(ssize_t) ping_height;
3071   if (mng_info->mng_type == 0)
3072     {
3073       mng_info->mng_width=ping_width;
3074       mng_info->mng_height=ping_height;
3075       mng_info->frame=mng_info->image_box;
3076       mng_info->clip=mng_info->image_box;
3077     }
3078
3079   else
3080     {
3081       image->page.y=mng_info->y_off[mng_info->object_id];
3082     }
3083
3084   image->compression=ZipCompression;
3085   image->columns=ping_width;
3086   image->rows=ping_height;
3087
3088   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3089       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3090     {
3091       double
3092         image_gamma = image->gamma;
3093
3094       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3095          "    image->gamma=%f",(float) image_gamma);
3096
3097       if (image_gamma > 0.75)
3098         {
3099           /* Set image->rendering_intent to Undefined,
3100            * image->colorspace to GRAY, and reset image->chromaticity.
3101            */
3102           image->intensity = Rec709LuminancePixelIntensityMethod;
3103           SetImageColorspace(image,GRAYColorspace,exception);
3104           image->gamma = image_gamma;
3105         }
3106     }
3107   
3108   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3109       "    image->colorspace=%d",(int) image->colorspace);
3110
3111   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3112       ((int) ping_bit_depth < 16 &&
3113       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3114     {
3115       size_t
3116         one;
3117
3118       image->storage_class=PseudoClass;
3119       one=1;
3120       image->colors=one << ping_file_depth;
3121 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3122       if (image->colors > 256)
3123         image->colors=256;
3124 #else
3125       if (image->colors > 65536L)
3126         image->colors=65536L;
3127 #endif
3128       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3129         {
3130           png_colorp
3131             palette;
3132
3133           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3134           image->colors=(size_t) number_colors;
3135
3136           if (logging != MagickFalse)
3137             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3138               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3139         }
3140     }
3141
3142   if (image->storage_class == PseudoClass)
3143     {
3144       /*
3145         Initialize image colormap.
3146       */
3147       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3148         png_error(ping,"Memory allocation failed");
3149
3150       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3151         {
3152           png_colorp
3153             palette;
3154
3155           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3156
3157           for (i=0; i < (ssize_t) number_colors; i++)
3158           {
3159             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3160             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3161             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3162           }
3163
3164           for ( ; i < (ssize_t) image->colors; i++)
3165           {
3166             image->colormap[i].red=0;
3167             image->colormap[i].green=0;
3168             image->colormap[i].blue=0;
3169           }
3170         }
3171
3172       else
3173         {
3174           size_t
3175             scale;
3176
3177           scale=(QuantumRange/((1UL << ping_file_depth)-1));
3178
3179           if (scale < 1)
3180              scale=1;
3181
3182           for (i=0; i < (ssize_t) image->colors; i++)
3183           {
3184             image->colormap[i].red=(Quantum) (i*scale);
3185             image->colormap[i].green=(Quantum) (i*scale);
3186             image->colormap[i].blue=(Quantum) (i*scale);
3187           }
3188        }
3189     }
3190
3191    /* Set some properties for reporting by "identify" */
3192     {
3193       char
3194         msg[MaxTextExtent];
3195
3196      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3197         ping_interlace_method in value */
3198
3199      (void) FormatLocaleString(msg,MaxTextExtent,
3200          "%d, %d",(int) ping_width, (int) ping_height);
3201      (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3202
3203      (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
3204      (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3205
3206      (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
3207          (int) ping_color_type,
3208          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3209      (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3210
3211      if (ping_interlace_method == 0)
3212        {
3213          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
3214             (int) ping_interlace_method);
3215        }
3216      else if (ping_interlace_method == 1)
3217        {
3218          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
3219             (int) ping_interlace_method);
3220        }
3221      else
3222        {
3223          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
3224             (int) ping_interlace_method);
3225        }
3226        (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3227
3228      if (number_colors != 0)
3229        {
3230          (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3231             (int) number_colors);
3232          (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3233             exception);
3234        }
3235    }
3236
3237   /*
3238     Read image scanlines.
3239   */
3240   if (image->delay != 0)
3241     mng_info->scenes_found++;
3242
3243   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3244       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3245       (image_info->first_scene+image_info->number_scenes))))
3246     {
3247       /* This happens later in non-ping decodes */
3248       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3249         image->storage_class=DirectClass;
3250
3251       if (logging != MagickFalse)
3252         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3253           "    Skipping PNG image data for scene %.20g",(double)
3254           mng_info->scenes_found-1);
3255       png_destroy_read_struct(&ping,&ping_info,&end_info);
3256
3257 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3258       UnlockSemaphoreInfo(ping_semaphore);
3259 #endif
3260
3261       if (logging != MagickFalse)
3262         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3263           "  exit ReadOnePNGImage().");
3264
3265       return(image);
3266     }
3267
3268   if (logging != MagickFalse)
3269     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3270       "    Reading PNG IDAT chunk(s)");
3271
3272   if (num_passes > 1)
3273     pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3274       sizeof(*ping_pixels));
3275   else
3276     pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3277
3278   if (pixel_info == (MemoryInfo *) NULL)
3279     png_error(ping,"Memory allocation failed");
3280   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3281
3282   if (logging != MagickFalse)
3283     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3284       "    Converting PNG pixels to pixel packets");
3285   /*
3286     Convert PNG pixels to pixel packets.
3287   */
3288   quantum_info=AcquireQuantumInfo(image_info,image);
3289
3290   if (quantum_info == (QuantumInfo *) NULL)
3291      png_error(ping,"Failed to allocate quantum_info");
3292
3293   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3294
3295   {
3296
3297    MagickBooleanType
3298      found_transparent_pixel;
3299
3300   found_transparent_pixel=MagickFalse;
3301
3302   if (image->storage_class == DirectClass)
3303     {
3304       for (pass=0; pass < num_passes; pass++)
3305       {
3306         /*
3307           Convert image to DirectClass pixel packets.
3308         */
3309         image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3310             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3311             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3312             BlendPixelTrait : UndefinedPixelTrait;
3313
3314         for (y=0; y < (ssize_t) image->rows; y++)
3315         {
3316           if (num_passes > 1)
3317             row_offset=ping_rowbytes*y;
3318
3319           else
3320             row_offset=0;
3321
3322           png_read_row(ping,ping_pixels+row_offset,NULL);
3323
3324           if (pass < num_passes-1)
3325             continue;
3326
3327           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3328
3329           if (q == (Quantum *) NULL)
3330             break;
3331
3332           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3333             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3334               GrayQuantum,ping_pixels+row_offset,exception);
3335
3336           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3337             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3338               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3339
3340           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3341             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3342               RGBAQuantum,ping_pixels+row_offset,exception);
3343
3344           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3345             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3346               IndexQuantum,ping_pixels+row_offset,exception);
3347
3348           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3349             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3350               RGBQuantum,ping_pixels+row_offset,exception);
3351
3352           if (found_transparent_pixel == MagickFalse)
3353             {
3354               /* Is there a transparent pixel in the row? */
3355               if (y== 0 && logging != MagickFalse)
3356                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3357                    "    Looking for cheap transparent pixel");
3358
3359               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3360               {
3361                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3362                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3363                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3364                   {
3365                     if (logging != MagickFalse)
3366                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3367                         "    ...got one.");
3368
3369                     found_transparent_pixel = MagickTrue;
3370                     break;
3371                   }
3372                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3373                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3374                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3375                     transparent_color.red &&
3376                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3377                     transparent_color.green &&
3378                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3379                     transparent_color.blue))
3380                   {
3381                     if (logging != MagickFalse)
3382                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3383                         "    ...got one.");
3384                     found_transparent_pixel = MagickTrue;
3385                     break;
3386                   }
3387                 q+=GetPixelChannels(image);
3388               }
3389             }
3390
3391           if ((image->previous == (Image *) NULL) && (num_passes == 1))
3392             {
3393               status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3394                   image->rows);
3395
3396               if (status == MagickFalse)
3397                 break;
3398             }
3399           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3400             break;
3401         }
3402
3403         if ((image->previous == (Image *) NULL) && (num_passes != 1))
3404           {
3405             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3406             if (status == MagickFalse)
3407               break;
3408           }
3409       }
3410     }
3411
3412   else /* image->storage_class != DirectClass */
3413
3414     for (pass=0; pass < num_passes; pass++)
3415     {
3416       Quantum
3417         *quantum_scanline;
3418
3419       register Quantum
3420         *r;
3421
3422       /*
3423         Convert grayscale image to PseudoClass pixel packets.
3424       */
3425       if (logging != MagickFalse)
3426         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3427           "    Converting grayscale pixels to pixel packets");
3428
3429       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3430         BlendPixelTrait : UndefinedPixelTrait;
3431
3432       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3433         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3434         sizeof(*quantum_scanline));
3435
3436       if (quantum_scanline == (Quantum *) NULL)
3437         png_error(ping,"Memory allocation failed");
3438
3439       for (y=0; y < (ssize_t) image->rows; y++)
3440       {
3441         Quantum
3442            alpha;
3443
3444         if (num_passes > 1)
3445           row_offset=ping_rowbytes*y;
3446
3447         else
3448           row_offset=0;
3449
3450         png_read_row(ping,ping_pixels+row_offset,NULL);
3451
3452         if (pass < num_passes-1)
3453           continue;
3454
3455         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3456
3457         if (q == (Quantum *) NULL)
3458           break;
3459
3460         p=ping_pixels+row_offset;
3461         r=quantum_scanline;
3462
3463         switch (ping_bit_depth)
3464         {
3465           case 8:
3466           {
3467
3468             if (ping_color_type == 4)
3469               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3470               {
3471                 *r++=*p++;
3472
3473                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3474
3475                 SetPixelAlpha(image,alpha,q);
3476
3477                 if (alpha != OpaqueAlpha)
3478                   found_transparent_pixel = MagickTrue;
3479
3480                 q+=GetPixelChannels(image);
3481               }
3482
3483             else
3484               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3485                 *r++=*p++;
3486
3487             break;
3488           }
3489
3490           case 16:
3491           {
3492             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3493             {
3494 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3495               unsigned short
3496                 quantum;
3497
3498               if (image->colors > 256)
3499                 quantum=((*p++) << 8);
3500
3501               else
3502                 quantum=0;
3503
3504               quantum|=(*p++);
3505               *r=ScaleShortToQuantum(quantum);
3506               r++;
3507
3508               if (ping_color_type == 4)
3509                 {
3510                   if (image->colors > 256)
3511                     quantum=((*p++) << 8);
3512                   else
3513                     quantum=0;
3514
3515                   quantum|=(*p++);
3516
3517                   alpha=ScaleShortToQuantum(quantum);
3518                   SetPixelAlpha(image,alpha,q);
3519
3520                   if (alpha != OpaqueAlpha)
3521                     found_transparent_pixel = MagickTrue;
3522
3523                   q+=GetPixelChannels(image);
3524                 }
3525
3526 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3527               *r++=(*p++);
3528               p++; /* strip low byte */
3529
3530               if (ping_color_type == 4)
3531                 {
3532                   SetPixelAlpha(image,*p++,q);
3533
3534                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3535                     found_transparent_pixel = MagickTrue;
3536
3537                   p++;
3538                   q+=GetPixelChannels(image);
3539                 }
3540 #endif
3541             }
3542
3543             break;
3544           }
3545
3546           default:
3547             break;
3548         }
3549
3550         /*
3551           Transfer image scanline.
3552         */
3553         r=quantum_scanline;
3554
3555         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3556
3557         if (q == (Quantum *) NULL)
3558           break;
3559         for (x=0; x < (ssize_t) image->columns; x++)
3560         {
3561           SetPixelIndex(image,*r++,q);
3562           q+=GetPixelChannels(image);
3563         }
3564
3565         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3566           break;
3567
3568         if ((image->previous == (Image *) NULL) && (num_passes == 1))
3569           {
3570             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3571               image->rows);
3572
3573             if (status == MagickFalse)
3574               break;
3575           }
3576       }
3577
3578       if ((image->previous == (Image *) NULL) && (num_passes != 1))
3579         {
3580           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3581
3582           if (status == MagickFalse)
3583             break;
3584         }
3585
3586       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3587     }
3588
3589     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3590       UndefinedPixelTrait;
3591
3592     if (logging != MagickFalse)
3593       {
3594         if (found_transparent_pixel != MagickFalse)
3595           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3596             "    Found transparent pixel");
3597         else
3598           {
3599             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3600               "    No transparent pixel was found");
3601
3602             ping_color_type&=0x03;
3603           }
3604       }
3605     }
3606
3607   if (quantum_info != (QuantumInfo *) NULL)
3608     quantum_info=DestroyQuantumInfo(quantum_info);
3609
3610   if (image->storage_class == PseudoClass)
3611     {
3612       PixelTrait
3613         alpha_trait;
3614
3615       alpha_trait=image->alpha_trait;
3616       image->alpha_trait=UndefinedPixelTrait;
3617       (void) SyncImage(image,exception);
3618       image->alpha_trait=alpha_trait;
3619     }
3620
3621   png_read_end(ping,end_info);
3622
3623   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3624       (ssize_t) image_info->first_scene && image->delay != 0)
3625     {
3626       png_destroy_read_struct(&ping,&ping_info,&end_info);
3627       pixel_info=RelinquishVirtualMemory(pixel_info);
3628       image->colors=2;
3629       (void) SetImageBackgroundColor(image,exception);
3630 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3631       UnlockSemaphoreInfo(ping_semaphore);
3632 #endif
3633       if (logging != MagickFalse)
3634         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3635           "  exit ReadOnePNGImage() early.");
3636       return(image);
3637     }
3638
3639   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3640     {
3641       ClassType
3642         storage_class;
3643
3644       /*
3645         Image has a transparent background.
3646       */
3647       storage_class=image->storage_class;
3648       image->alpha_trait=BlendPixelTrait;
3649
3650 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3651
3652       if (storage_class == PseudoClass)
3653         {
3654           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3655             {
3656               for (x=0; x < ping_num_trans; x++)
3657               {
3658                  image->colormap[x].alpha_trait=BlendPixelTrait;
3659                  image->colormap[x].alpha =
3660                    ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3661               }
3662             }
3663
3664           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3665             {
3666               for (x=0; x < (int) image->colors; x++)
3667               {
3668                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3669                      transparent_color.alpha)
3670                  {
3671                     image->colormap[x].alpha_trait=BlendPixelTrait;
3672                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3673                  }
3674               }
3675             }
3676           (void) SyncImage(image,exception);
3677         }
3678
3679 #if 1 /* Should have already been done above, but glennrp problem P10
3680        * needs this.
3681        */
3682       else
3683         {
3684           for (y=0; y < (ssize_t) image->rows; y++)
3685           {
3686             image->storage_class=storage_class;
3687             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3688
3689             if (q == (Quantum *) NULL)
3690               break;
3691
3692
3693             /* Caution: on a Q8 build, this does not distinguish between
3694              * 16-bit colors that differ only in the low byte
3695              */
3696             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3697             {
3698               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3699                   transparent_color.red &&
3700                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3701                   transparent_color.green &&
3702                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3703                   transparent_color.blue)
3704                 {
3705                   SetPixelAlpha(image,TransparentAlpha,q);
3706                 }
3707
3708 #if 0 /* I have not found a case where this is needed. */
3709               else
3710                 {
3711                   SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3712                 }
3713 #endif
3714
3715               q+=GetPixelChannels(image);
3716             }
3717
3718             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3719                break;
3720           }
3721         }
3722 #endif
3723
3724       image->storage_class=DirectClass;
3725     }
3726
3727   for (j = 0; j < 2; j++)
3728   {
3729     if (j == 0)
3730       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3731           MagickTrue : MagickFalse;
3732     else
3733       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3734           MagickTrue : MagickFalse;
3735
3736     if (status != MagickFalse)
3737       for (i=0; i < (ssize_t) num_text; i++)
3738       {
3739         /* Check for a profile */
3740
3741         if (logging != MagickFalse)
3742           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3743             "    Reading PNG text chunk");
3744
3745         if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3746           {
3747             (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3748                (int) i,exception);
3749             num_raw_profiles++;
3750           }
3751
3752         else
3753           {
3754             char
3755               *value;
3756
3757             length=text[i].text_length;
3758             value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3759               sizeof(*value));
3760             if (value == (char *) NULL)
3761               {
3762                 png_error(ping,"Memory allocation failed");
3763                 break;
3764               }
3765             *value='\0';
3766             (void) ConcatenateMagickString(value,text[i].text,length+2);
3767
3768             /* Don't save "density" or "units" property if we have a pHYs
3769              * chunk
3770              */
3771             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3772                 (LocaleCompare(text[i].key,"density") != 0 &&
3773                 LocaleCompare(text[i].key,"units") != 0))
3774                (void) SetImageProperty(image,text[i].key,value,exception);
3775
3776             if (logging != MagickFalse)
3777             {
3778               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3779                 "      length: %lu",(unsigned long) length);
3780               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3781                 "      Keyword: %s",text[i].key);
3782             }
3783
3784             value=DestroyString(value);
3785           }
3786       }
3787       num_text_total += num_text;
3788     }
3789
3790 #ifdef MNG_OBJECT_BUFFERS
3791   /*
3792     Store the object if necessary.
3793   */
3794   if (object_id && !mng_info->frozen[object_id])
3795     {
3796       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3797         {
3798           /*
3799             create a new object buffer.
3800           */
3801           mng_info->ob[object_id]=(MngBuffer *)
3802             AcquireMagickMemory(sizeof(MngBuffer));
3803
3804           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3805             {
3806               mng_info->ob[object_id]->image=(Image *) NULL;
3807               mng_info->ob[object_id]->reference_count=1;
3808             }
3809         }
3810
3811       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3812           mng_info->ob[object_id]->frozen)
3813         {
3814           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3815              png_error(ping,"Memory allocation failed");
3816
3817           if (mng_info->ob[object_id]->frozen)
3818             png_error(ping,"Cannot overwrite frozen MNG object buffer");
3819         }
3820
3821       else
3822         {
3823
3824           if (mng_info->ob[object_id]->image != (Image *) NULL)
3825             mng_info->ob[object_id]->image=DestroyImage
3826                 (mng_info->ob[object_id]->image);
3827
3828           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3829             exception);
3830
3831           if (mng_info->ob[object_id]->image != (Image *) NULL)
3832             mng_info->ob[object_id]->image->file=(FILE *) NULL;
3833
3834           else
3835             png_error(ping, "Cloning image for object buffer failed");
3836
3837           if (ping_width > 250000L || ping_height > 250000L)
3838              png_error(ping,"PNG Image dimensions are too large.");
3839
3840           mng_info->ob[object_id]->width=ping_width;
3841           mng_info->ob[object_id]->height=ping_height;
3842           mng_info->ob[object_id]->color_type=ping_color_type;
3843           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3844           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3845           mng_info->ob[object_id]->compression_method=
3846              ping_compression_method;
3847           mng_info->ob[object_id]->filter_method=ping_filter_method;
3848
3849           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3850             {
3851               png_colorp
3852                 plte;
3853
3854               /*
3855                 Copy the PLTE to the object buffer.
3856               */
3857               png_get_PLTE(ping,ping_info,&plte,&number_colors);
3858               mng_info->ob[object_id]->plte_length=number_colors;
3859
3860               for (i=0; i < number_colors; i++)
3861               {
3862                 mng_info->ob[object_id]->plte[i]=plte[i];
3863               }
3864             }
3865
3866           else
3867               mng_info->ob[object_id]->plte_length=0;
3868         }
3869     }
3870 #endif
3871
3872    /* Set image->alpha_trait to MagickTrue if the input colortype supports
3873     * alpha or if a valid tRNS chunk is present, no matter whether there
3874     * is actual transparency present.
3875     */
3876     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3877         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3878         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3879         BlendPixelTrait : UndefinedPixelTrait;
3880
3881 #if 0  /* I'm not sure what's wrong here but it does not work. */
3882     if (image->alpha_trait == BlendPixelTrait)
3883     {
3884       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3885         (void) SetImageType(image,GrayscaleMatteType,exception);
3886
3887       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3888         (void) SetImageType(image,PaletteMatteType,exception);
3889
3890       else
3891         (void) SetImageType(image,TrueColorMatteType,exception);
3892     }
3893
3894     else
3895     {
3896       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3897         (void) SetImageType(image,GrayscaleType,exception);
3898
3899       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3900         (void) SetImageType(image,PaletteType,exception);
3901
3902       else
3903         (void) SetImageType(image,TrueColorType,exception);
3904     }
3905 #endif
3906
3907    /* Set more properties for identify to retrieve */
3908    {
3909      char
3910        msg[MaxTextExtent];
3911
3912      if (num_text_total != 0)
3913        {
3914          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3915          (void) FormatLocaleString(msg,MaxTextExtent,
3916             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3917          (void) SetImageProperty(image,"png:text",msg,
3918                 exception);
3919        }
3920
3921      if (num_raw_profiles != 0)
3922        {
3923          (void) FormatLocaleString(msg,MaxTextExtent,
3924             "%d were found", num_raw_profiles);
3925          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3926                 exception);
3927        }
3928
3929      if (ping_found_cHRM != MagickFalse)
3930        {
3931          (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3932             "chunk was found (see Chromaticity, above)");
3933          (void) SetImageProperty(image,"png:cHRM",msg,
3934                 exception);
3935        }
3936
3937      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3938        {
3939          (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3940             "chunk was found (see Background color, above)");
3941          (void) SetImageProperty(image,"png:bKGD",msg,
3942                 exception);
3943        }
3944
3945      (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3946         "chunk was found");
3947
3948 #if defined(PNG_iCCP_SUPPORTED)
3949      if (ping_found_iCCP != MagickFalse)
3950         (void) SetImageProperty(image,"png:iCCP",msg,
3951                 exception);
3952 #endif
3953
3954      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3955         (void) SetImageProperty(image,"png:tRNS",msg,
3956                 exception);
3957
3958 #if defined(PNG_sRGB_SUPPORTED)
3959      if (ping_found_sRGB != MagickFalse)
3960        {
3961          (void) FormatLocaleString(msg,MaxTextExtent,
3962             "intent=%d (%s)",
3963             (int) intent,
3964             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3965          (void) SetImageProperty(image,"png:sRGB",msg,
3966                  exception);
3967        }
3968 #endif
3969
3970      if (ping_found_gAMA != MagickFalse)
3971        {
3972          (void) FormatLocaleString(msg,MaxTextExtent,
3973             "gamma=%.8g (See Gamma, above)",
3974             file_gamma);
3975          (void) SetImageProperty(image,"png:gAMA",msg,
3976                 exception);
3977        }
3978
3979 #if defined(PNG_pHYs_SUPPORTED)
3980      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3981        {
3982          (void) FormatLocaleString(msg,MaxTextExtent,
3983             "x_res=%.10g, y_res=%.10g, units=%d",
3984             (double) x_resolution,(double) y_resolution, unit_type);
3985          (void) SetImageProperty(image,"png:pHYs",msg,
3986                 exception);
3987        }
3988 #endif
3989
3990 #if defined(PNG_oFFs_SUPPORTED)
3991      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3992        {
3993          (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3994             (double) image->page.x,(double) image->page.y);
3995          (void) SetImageProperty(image,"png:oFFs",msg,
3996                 exception);
3997        }
3998 #endif
3999
4000      if ((image->page.width != 0 && image->page.width != image->columns) ||
4001          (image->page.height != 0 && image->page.height != image->rows))
4002        {
4003          (void) FormatLocaleString(msg,MaxTextExtent,
4004             "width=%.20g, height=%.20g",
4005             (double) image->page.width,(double) image->page.height);
4006          (void) SetImageProperty(image,"png:vpAg",msg,
4007                 exception);
4008        }
4009    }
4010
4011   /*
4012     Relinquish resources.
4013   */
4014   png_destroy_read_struct(&ping,&ping_info,&end_info);
4015
4016   pixel_info=RelinquishVirtualMemory(pixel_info);
4017
4018   if (logging != MagickFalse)
4019     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4020       "  exit ReadOnePNGImage()");
4021
4022 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4023   UnlockSemaphoreInfo(ping_semaphore);
4024 #endif
4025
4026   /* }  for navigation to beginning of SETJMP-protected block, revert to
4027    *    Throwing an Exception when an error occurs.
4028    */
4029
4030   return(image);
4031
4032 /* end of reading one PNG image */
4033 }
4034
4035 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4036 {
4037   Image
4038     *image,
4039     *previous;
4040
4041   MagickBooleanType
4042     have_mng_structure,
4043     logging,
4044     status;
4045
4046   MngInfo
4047     *mng_info;
4048
4049   char
4050     magic_number[MaxTextExtent];
4051
4052   ssize_t
4053     count;
4054
4055   /*
4056     Open image file.
4057   */
4058   assert(image_info != (const ImageInfo *) NULL);
4059   assert(image_info->signature == MagickSignature);
4060
4061   if (image_info->debug != MagickFalse)
4062     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4063       image_info->filename);
4064
4065   assert(exception != (ExceptionInfo *) NULL);
4066   assert(exception->signature == MagickSignature);
4067   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4068   image=AcquireImage(image_info,exception);
4069   mng_info=(MngInfo *) NULL;
4070   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4071
4072   if (status == MagickFalse)
4073     ThrowReaderException(FileOpenError,"UnableToOpenFile");
4074
4075   /*
4076     Verify PNG signature.
4077   */
4078   count=ReadBlob(image,8,(unsigned char *) magic_number);
4079
4080   if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4081     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4082
4083   /*
4084     Allocate a MngInfo structure.
4085   */
4086   have_mng_structure=MagickFalse;
4087   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4088
4089   if (mng_info == (MngInfo *) NULL)
4090     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4091
4092   /*
4093     Initialize members of the MngInfo structure.
4094   */
4095   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4096   mng_info->image=image;
4097   have_mng_structure=MagickTrue;
4098
4099   previous=image;
4100   image=ReadOnePNGImage(mng_info,image_info,exception);
4101   MngInfoFreeStruct(mng_info,&have_mng_structure);
4102
4103   if (image == (Image *) NULL)
4104     {
4105       if (previous != (Image *) NULL)
4106         {
4107           if (previous->signature != MagickSignature)
4108             ThrowReaderException(CorruptImageError,"CorruptImage");
4109
4110           (void) CloseBlob(previous);
4111           (void) DestroyImageList(previous);
4112         }
4113
4114       if (logging != MagickFalse)
4115         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4116           "exit ReadPNGImage() with error");
4117
4118       return((Image *) NULL);
4119     }
4120
4121   (void) CloseBlob(image);
4122
4123   if ((image->columns == 0) || (image->rows == 0))
4124     {
4125       if (logging != MagickFalse)
4126         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4127           "exit ReadPNGImage() with error.");
4128
4129       ThrowReaderException(CorruptImageError,"CorruptImage");
4130     }
4131
4132   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4133       ((image->gamma < .45) || (image->gamma > .46)) &&
4134            !(image->chromaticity.red_primary.x>0.6399f &&
4135            image->chromaticity.red_primary.x<0.6401f &&
4136            image->chromaticity.red_primary.y>0.3299f &&
4137            image->chromaticity.red_primary.y<0.3301f &&
4138            image->chromaticity.green_primary.x>0.2999f &&
4139            image->chromaticity.green_primary.x<0.3001f &&
4140            image->chromaticity.green_primary.y>0.5999f &&
4141            image->chromaticity.green_primary.y<0.6001f &&
4142            image->chromaticity.blue_primary.x>0.1499f &&
4143            image->chromaticity.blue_primary.x<0.1501f &&
4144            image->chromaticity.blue_primary.y>0.0599f &&
4145            image->chromaticity.blue_primary.y<0.0601f &&
4146            image->chromaticity.white_point.x>0.3126f &&
4147            image->chromaticity.white_point.x<0.3128f &&
4148            image->chromaticity.white_point.y>0.3289f &&
4149            image->chromaticity.white_point.y<0.3291f))
4150     SetImageColorspace(image,RGBColorspace,exception);
4151
4152   if (logging != MagickFalse)
4153     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4154         "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4155             (double) image->page.width,(double) image->page.height,
4156             (double) image->page.x,(double) image->page.y);
4157
4158   if (logging != MagickFalse)
4159     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4160
4161   return(image);
4162 }
4163
4164
4165
4166 #if defined(JNG_SUPPORTED)
4167 /*
4168 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4169 %                                                                             %
4170 %                                                                             %
4171 %                                                                             %
4172 %   R e a d O n e J N G I m a g e                                             %
4173 %                                                                             %
4174 %                                                                             %
4175 %                                                                             %
4176 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4177 %
4178 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4179 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
4180 %  necessary for the new Image structure and returns a pointer to the new
4181 %  image.
4182 %
4183 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4184 %
4185 %  The format of the ReadOneJNGImage method is:
4186 %
4187 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4188 %         ExceptionInfo *exception)
4189 %
4190 %  A description of each parameter follows:
4191 %
4192 %    o mng_info: Specifies a pointer to a MngInfo structure.
4193 %
4194 %    o image_info: the image info.
4195 %
4196 %    o exception: return any errors or warnings in this structure.
4197 %
4198 */
4199 static Image *ReadOneJNGImage(MngInfo *mng_info,
4200     const ImageInfo *image_info, ExceptionInfo *exception)
4201 {
4202   Image
4203     *alpha_image,
4204     *color_image,
4205     *image,
4206     *jng_image;
4207
4208   ImageInfo
4209     *alpha_image_info,
4210     *color_image_info;
4211
4212   MagickBooleanType
4213     logging;
4214
4215   ssize_t
4216     y;
4217
4218   MagickBooleanType
4219     status;
4220
4221   png_uint_32
4222     jng_height,
4223     jng_width;
4224
4225   png_byte
4226     jng_color_type,
4227     jng_image_sample_depth,
4228     jng_image_compression_method,
4229     jng_image_interlace_method,
4230     jng_alpha_sample_depth,
4231     jng_alpha_compression_method,
4232     jng_alpha_filter_method,
4233     jng_alpha_interlace_method;
4234
4235   register const Quantum
4236     *s;
4237
4238   register ssize_t
4239     i,
4240     x;
4241
4242   register Quantum
4243     *q;
4244
4245   register unsigned char
4246     *p;
4247
4248   unsigned int
4249     read_JSEP,
4250     reading_idat,
4251     skip_to_iend;
4252
4253   size_t
4254     length;
4255
4256   jng_alpha_compression_method=0;
4257   jng_alpha_sample_depth=8;
4258   jng_color_type=0;
4259   jng_height=0;
4260   jng_width=0;
4261   alpha_image=(Image *) NULL;
4262   color_image=(Image *) NULL;
4263   alpha_image_info=(ImageInfo *) NULL;
4264   color_image_info=(ImageInfo *) NULL;
4265
4266   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4267     "  Enter ReadOneJNGImage()");
4268
4269   image=mng_info->image;
4270
4271   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4272     {
4273       /*
4274         Allocate next image structure.
4275       */
4276       if (logging != MagickFalse)
4277         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4278            "  AcquireNextImage()");
4279
4280       AcquireNextImage(image_info,image,exception);
4281
4282       if (GetNextImageInList(image) == (Image *) NULL)
4283         return((Image *) NULL);
4284
4285       image=SyncNextImageInList(image);
4286     }
4287   mng_info->image=image;
4288
4289   /*
4290     Signature bytes have already been read.
4291   */
4292
4293   read_JSEP=MagickFalse;
4294   reading_idat=MagickFalse;
4295   skip_to_iend=MagickFalse;
4296   for (;;)
4297   {
4298     char
4299       type[MaxTextExtent];
4300
4301     unsigned char
4302       *chunk;
4303
4304     unsigned int
4305       count;
4306
4307     /*
4308       Read a new JNG chunk.
4309     */
4310     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4311       2*GetBlobSize(image));
4312
4313     if (status == MagickFalse)
4314       break;
4315
4316     type[0]='\0';
4317     (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4318     length=ReadBlobMSBLong(image);
4319     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4320
4321     if (logging != MagickFalse)
4322       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4323         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4324         type[0],type[1],type[2],type[3],(double) length);
4325
4326     if (length > PNG_UINT_31_MAX || count == 0)
4327       ThrowReaderException(CorruptImageError,"CorruptImage");
4328
4329     p=NULL;
4330     chunk=(unsigned char *) NULL;
4331
4332     if (length)
4333       {
4334         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4335
4336         if (chunk == (unsigned char *) NULL)
4337           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4338
4339         for (i=0; i < (ssize_t) length; i++)
4340           chunk[i]=(unsigned char) ReadBlobByte(image);
4341
4342         p=chunk;
4343       }
4344
4345     (void) ReadBlobMSBLong(image);  /* read crc word */
4346
4347     if (skip_to_iend)
4348       {
4349         if (length)
4350           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4351
4352         continue;
4353       }
4354
4355     if (memcmp(type,mng_JHDR,4) == 0)
4356       {
4357         if (length == 16)
4358           {
4359             jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4360                 (p[2] << 8) | p[3]);
4361             jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4362                 (p[6] << 8) | p[7]);
4363             jng_color_type=p[8];
4364             jng_image_sample_depth=p[9];
4365             jng_image_compression_method=p[10];
4366             jng_image_interlace_method=p[11];
4367
4368             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4369               NoInterlace;
4370
4371             jng_alpha_sample_depth=p[12];
4372             jng_alpha_compression_method=p[13];
4373             jng_alpha_filter_method=p[14];
4374             jng_alpha_interlace_method=p[15];
4375
4376             if (logging != MagickFalse)
4377               {
4378                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4379                   "    jng_width:      %16lu",(unsigned long) jng_width);
4380
4381                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4382                   "    jng_width:      %16lu",(unsigned long) jng_height);
4383
4384                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4385                   "    jng_color_type: %16d",jng_color_type);
4386
4387                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4388                   "    jng_image_sample_depth:      %3d",
4389                   jng_image_sample_depth);
4390
4391                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4392                   "    jng_image_compression_method:%3d",
4393                   jng_image_compression_method);
4394
4395                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4396                   "    jng_image_interlace_method:  %3d",
4397                   jng_image_interlace_method);
4398
4399                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4400                   "    jng_alpha_sample_depth:      %3d",
4401                   jng_alpha_sample_depth);
4402
4403                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4404                   "    jng_alpha_compression_method:%3d",
4405                   jng_alpha_compression_method);
4406
4407                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4408                   "    jng_alpha_filter_method:     %3d",
4409                   jng_alpha_filter_method);
4410
4411                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4412                   "    jng_alpha_interlace_method:  %3d",
4413                   jng_alpha_interlace_method);
4414               }
4415           }
4416
4417         if (length)
4418           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4419
4420         continue;
4421       }
4422
4423
4424     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4425         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4426          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4427       {
4428         /*
4429            o create color_image
4430            o open color_blob, attached to color_image
4431            o if (color type has alpha)
4432                open alpha_blob, attached to alpha_image
4433         */
4434
4435         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4436
4437         if (color_image_info == (ImageInfo *) NULL)
4438           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4439
4440         GetImageInfo(color_image_info);
4441         color_image=AcquireImage(color_image_info,exception);
4442
4443         if (color_image == (Image *) NULL)
4444           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4445
4446         if (logging != MagickFalse)
4447           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4448             "    Creating color_blob.");
4449
4450         (void) AcquireUniqueFilename(color_image->filename);
4451         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4452           exception);
4453
4454         if (status == MagickFalse)
4455           return((Image *) NULL);
4456
4457         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4458           {
4459             alpha_image_info=(ImageInfo *)
4460               AcquireMagickMemory(sizeof(ImageInfo));
4461
4462             if (alpha_image_info == (ImageInfo *) NULL)
4463               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4464
4465             GetImageInfo(alpha_image_info);
4466             alpha_image=AcquireImage(alpha_image_info,exception);
4467
4468             if (alpha_image == (Image *) NULL)
4469               {
4470                 alpha_image=DestroyImage(alpha_image);
4471                 ThrowReaderException(ResourceLimitError,
4472                   "MemoryAllocationFailed");
4473               }
4474
4475             if (logging != MagickFalse)
4476               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4477                 "    Creating alpha_blob.");
4478
4479             (void) AcquireUniqueFilename(alpha_image->filename);
4480             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4481               exception);
4482
4483             if (status == MagickFalse)
4484               return((Image *) NULL);
4485
4486             if (jng_alpha_compression_method == 0)
4487               {
4488                 unsigned char
4489                   data[18];
4490
4491                 if (logging != MagickFalse)
4492                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4493                     "    Writing IHDR chunk to alpha_blob.");
4494
4495                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4496                   "\211PNG\r\n\032\n");
4497
4498                 (void) WriteBlobMSBULong(alpha_image,13L);
4499                 PNGType(data,mng_IHDR);
4500                 LogPNGChunk(logging,mng_IHDR,13L);
4501                 PNGLong(data+4,jng_width);
4502                 PNGLong(data+8,jng_height);
4503                 data[12]=jng_alpha_sample_depth;
4504                 data[13]=0; /* color_type gray */
4505                 data[14]=0; /* compression method 0 */
4506                 data[15]=0; /* filter_method 0 */
4507                 data[16]=0; /* interlace_method 0 */
4508                 (void) WriteBlob(alpha_image,17,data);
4509                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4510               }
4511           }
4512         reading_idat=MagickTrue;
4513       }
4514
4515     if (memcmp(type,mng_JDAT,4) == 0)
4516       {
4517         /* Copy chunk to color_image->blob */
4518
4519         if (logging != MagickFalse)
4520           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4521             "    Copying JDAT chunk data to color_blob.");
4522
4523         (void) WriteBlob(color_image,length,chunk);
4524
4525         if (length)
4526           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4527
4528         continue;
4529       }
4530
4531     if (memcmp(type,mng_IDAT,4) == 0)
4532       {
4533         png_byte
4534            data[5];
4535
4536         /* Copy IDAT header and chunk data to alpha_image->blob */
4537
4538         if (image_info->ping == MagickFalse)
4539           {
4540             if (logging != MagickFalse)
4541               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4542                 "    Copying IDAT chunk data to alpha_blob.");
4543
4544             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4545             PNGType(data,mng_IDAT);
4546             LogPNGChunk(logging,mng_IDAT,length);
4547             (void) WriteBlob(alpha_image,4,data);
4548             (void) WriteBlob(alpha_image,length,chunk);
4549             (void) WriteBlobMSBULong(alpha_image,
4550               crc32(crc32(0,data,4),chunk,(uInt) length));
4551           }
4552
4553         if (length)
4554           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4555
4556         continue;
4557       }
4558
4559     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4560       {
4561         /* Copy chunk data to alpha_image->blob */
4562
4563         if (image_info->ping == MagickFalse)
4564           {
4565             if (logging != MagickFalse)
4566               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4567                 "    Copying JDAA chunk data to alpha_blob.");
4568
4569             (void) WriteBlob(alpha_image,length,chunk);
4570           }
4571
4572         if (length)
4573           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4574
4575         continue;
4576       }
4577
4578     if (memcmp(type,mng_JSEP,4) == 0)
4579       {
4580         read_JSEP=MagickTrue;
4581
4582         if (length)
4583           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4584
4585         continue;
4586       }
4587
4588     if (memcmp(type,mng_bKGD,4) == 0)
4589       {
4590         if (length == 2)
4591           {
4592             image->background_color.red=ScaleCharToQuantum(p[1]);
4593             image->background_color.green=image->background_color.red;
4594             image->background_color.blue=image->background_color.red;
4595           }
4596
4597         if (length == 6)
4598           {
4599             image->background_color.red=ScaleCharToQuantum(p[1]);
4600             image->background_color.green=ScaleCharToQuantum(p[3]);
4601             image->background_color.blue=ScaleCharToQuantum(p[5]);
4602           }
4603
4604         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4605         continue;
4606       }
4607
4608     if (memcmp(type,mng_gAMA,4) == 0)
4609       {
4610         if (length == 4)
4611           image->gamma=((float) mng_get_long(p))*0.00001;
4612
4613         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4614         continue;
4615       }
4616
4617     if (memcmp(type,mng_cHRM,4) == 0)
4618       {
4619         if (length == 32)
4620           {
4621             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4622             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4623             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4624             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4625             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4626             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4627             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4628             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4629           }
4630
4631         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4632         continue;
4633       }
4634
4635     if (memcmp(type,mng_sRGB,4) == 0)
4636       {
4637         if (length == 1)
4638           {
4639             image->rendering_intent=
4640               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4641             image->gamma=1.000f/2.200f;
4642             image->chromaticity.red_primary.x=0.6400f;
4643             image->chromaticity.red_primary.y=0.3300f;
4644             image->chromaticity.green_primary.x=0.3000f;
4645             image->chromaticity.green_primary.y=0.6000f;
4646             image->chromaticity.blue_primary.x=0.1500f;
4647             image->chromaticity.blue_primary.y=0.0600f;
4648             image->chromaticity.white_point.x=0.3127f;
4649             image->chromaticity.white_point.y=0.3290f;
4650           }
4651
4652         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4653         continue;
4654       }
4655
4656     if (memcmp(type,mng_oFFs,4) == 0)
4657       {
4658         if (length > 8)
4659           {
4660             image->page.x=(ssize_t) mng_get_long(p);
4661             image->page.y=(ssize_t) mng_get_long(&p[4]);
4662
4663             if ((int) p[8] != 0)
4664               {
4665                 image->page.x/=10000;
4666                 image->page.y/=10000;
4667               }
4668           }
4669
4670         if (length)
4671           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4672
4673         continue;
4674       }
4675
4676     if (memcmp(type,mng_pHYs,4) == 0)
4677       {
4678         if (length > 8)
4679           {
4680             image->resolution.x=(double) mng_get_long(p);
4681             image->resolution.y=(double) mng_get_long(&p[4]);
4682             if ((int) p[8] == PNG_RESOLUTION_METER)
4683               {
4684                 image->units=PixelsPerCentimeterResolution;
4685                 image->resolution.x=image->resolution.x/100.0f;
4686                 image->resolution.y=image->resolution.y/100.0f;
4687               }
4688           }
4689
4690         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4691         continue;
4692       }
4693
4694 #if 0
4695     if (memcmp(type,mng_iCCP,4) == 0)
4696       {
4697         /* To do: */
4698         if (length)
4699           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4700
4701         continue;
4702       }
4703 #endif
4704
4705     if (length)
4706       chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4707
4708     if (memcmp(type,mng_IEND,4))
4709       continue;
4710
4711     break;
4712   }
4713
4714
4715   /* IEND found */
4716
4717   /*
4718     Finish up reading image data:
4719
4720        o read main image from color_blob.
4721
4722        o close color_blob.
4723
4724        o if (color_type has alpha)
4725             if alpha_encoding is PNG
4726                read secondary image from alpha_blob via ReadPNG
4727             if alpha_encoding is JPEG
4728                read secondary image from alpha_blob via ReadJPEG
4729
4730        o close alpha_blob.
4731
4732        o copy intensity of secondary image into
4733          alpha samples of main image.
4734
4735        o destroy the secondary image.
4736   */
4737
4738   (void) CloseBlob(color_image);
4739
4740   if (logging != MagickFalse)
4741     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4742       "    Reading jng_image from color_blob.");
4743
4744   assert(color_image_info != (ImageInfo *) NULL);
4745   (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4746     color_image->filename);
4747
4748   color_image_info->ping=MagickFalse;   /* To do: avoid this */
4749   jng_image=ReadImage(color_image_info,exception);
4750
4751   if (jng_image == (Image *) NULL)
4752     return((Image *) NULL);
4753
4754   (void) RelinquishUniqueFileResource(color_image->filename);
4755   color_image=DestroyImage(color_image);
4756   color_image_info=DestroyImageInfo(color_image_info);
4757
4758   if (jng_image == (Image *) NULL)
4759     return((Image *) NULL);
4760
4761   if (logging != MagickFalse)
4762     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4763       "    Copying jng_image pixels to main image.");
4764
4765   image->rows=jng_height;
4766   image->columns=jng_width;
4767
4768   for (y=0; y < (ssize_t) image->rows; y++)
4769   {
4770     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4771     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4772     for (x=(ssize_t) image->columns; x != 0; x--)
4773     {
4774       SetPixelRed(image,GetPixelRed(jng_image,s),q);
4775       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4776       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4777       q+=GetPixelChannels(image);
4778       s+=GetPixelChannels(jng_image);
4779     }
4780
4781     if (SyncAuthenticPixels(image,exception) == MagickFalse)
4782       break;
4783   }
4784
4785   jng_image=DestroyImage(jng_image);
4786
4787   if (image_info->ping == MagickFalse)
4788     {
4789      if (jng_color_type >= 12)
4790        {
4791          if (jng_alpha_compression_method == 0)
4792            {
4793              png_byte
4794                data[5];
4795              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4796              PNGType(data,mng_IEND);
4797              LogPNGChunk(logging,mng_IEND,0L);
4798              (void) WriteBlob(alpha_image,4,data);
4799              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4800            }
4801
4802          (void) CloseBlob(alpha_image);
4803
4804          if (logging != MagickFalse)
4805            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4806              "    Reading alpha from alpha_blob.");
4807
4808          (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4809            "%s",alpha_image->filename);
4810
4811          jng_image=ReadImage(alpha_image_info,exception);
4812
4813          if (jng_image != (Image *) NULL)
4814            for (y=0; y < (ssize_t) image->rows; y++)
4815            {
4816              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4817                exception);
4818              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4819
4820              if (image->alpha_trait == BlendPixelTrait)
4821                for (x=(ssize_t) image->columns; x != 0; x--)
4822                {
4823                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4824                   q+=GetPixelChannels(image);
4825                   s+=GetPixelChannels(jng_image);
4826                }
4827
4828              else
4829                for (x=(ssize_t) image->columns; x != 0; x--)
4830                {
4831                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4832                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
4833                     image->alpha_trait=BlendPixelTrait;
4834                   q+=GetPixelChannels(image);
4835                   s+=GetPixelChannels(jng_image);
4836                }
4837
4838              if (SyncAuthenticPixels(image,exception) == MagickFalse)
4839                break;
4840            }
4841          (void) RelinquishUniqueFileResource(alpha_image->filename);
4842          alpha_image=DestroyImage(alpha_image);
4843          alpha_image_info=DestroyImageInfo(alpha_image_info);
4844          if (jng_image != (Image *) NULL)
4845            jng_image=DestroyImage(jng_image);
4846        }
4847     }
4848
4849   /* Read the JNG image.  */
4850
4851   if (mng_info->mng_type == 0)
4852     {
4853       mng_info->mng_width=jng_width;
4854       mng_info->mng_height=jng_height;
4855     }
4856
4857   if (image->page.width == 0 && image->page.height == 0)
4858     {
4859       image->page.width=jng_width;
4860       image->page.height=jng_height;
4861     }
4862
4863   if (image->page.x == 0 && image->page.y == 0)
4864     {
4865       image->page.x=mng_info->x_off[mng_info->object_id];
4866       image->page.y=mng_info->y_off[mng_info->object_id];
4867     }
4868
4869   else
4870     {
4871       image->page.y=mng_info->y_off[mng_info->object_id];
4872     }
4873
4874   mng_info->image_found++;
4875   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4876     2*GetBlobSize(image));
4877
4878   if (logging != MagickFalse)
4879     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4880       "  exit ReadOneJNGImage()");
4881
4882   return(image);
4883 }
4884
4885 /*
4886 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4887 %                                                                             %
4888 %                                                                             %
4889 %                                                                             %
4890 %   R e a d J N G I m a g e                                                   %
4891 %                                                                             %
4892 %                                                                             %
4893 %                                                                             %
4894 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4895 %
4896 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4897 %  (including the 8-byte signature)  and returns it.  It allocates the memory
4898 %  necessary for the new Image structure and returns a pointer to the new
4899 %  image.
4900 %
4901 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4902 %
4903 %  The format of the ReadJNGImage method is:
4904 %
4905 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4906 %         *exception)
4907 %
4908 %  A description of each parameter follows:
4909 %
4910 %    o image_info: the image info.
4911 %
4912 %    o exception: return any errors or warnings in this structure.
4913 %
4914 */
4915
4916 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4917 {
4918   Image
4919     *image,
4920     *previous;
4921
4922   MagickBooleanType
4923     have_mng_structure,
4924     logging,
4925     status;
4926
4927   MngInfo
4928     *mng_info;
4929
4930   char
4931     magic_number[MaxTextExtent];
4932
4933   size_t
4934     count;
4935
4936   /*
4937     Open image file.
4938   */
4939   assert(image_info != (const ImageInfo *) NULL);
4940   assert(image_info->signature == MagickSignature);
4941   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4942   assert(exception != (ExceptionInfo *) NULL);
4943   assert(exception->signature == MagickSignature);
4944   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4945   image=AcquireImage(image_info,exception);
4946   mng_info=(MngInfo *) NULL;
4947   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4948
4949   if (status == MagickFalse)
4950     return((Image *) NULL);
4951
4952   if (LocaleCompare(image_info->magick,"JNG") != 0)
4953     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4954
4955   /* Verify JNG signature.  */
4956
4957   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4958
4959   if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4960     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4961
4962   /* Allocate a MngInfo structure.  */
4963
4964   have_mng_structure=MagickFalse;
4965   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4966
4967   if (mng_info == (MngInfo *) NULL)
4968     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4969
4970   /* Initialize members of the MngInfo structure.  */
4971
4972   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4973   have_mng_structure=MagickTrue;
4974
4975   mng_info->image=image;
4976   previous=image;
4977   image=ReadOneJNGImage(mng_info,image_info,exception);
4978   MngInfoFreeStruct(mng_info,&have_mng_structure);
4979
4980   if (image == (Image *) NULL)
4981     {
4982       if (IsImageObject(previous) != MagickFalse)
4983         {
4984           (void) CloseBlob(previous);
4985           (void) DestroyImageList(previous);
4986         }
4987
4988       if (logging != MagickFalse)
4989         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4990           "exit ReadJNGImage() with error");
4991
4992       return((Image *) NULL);
4993     }
4994   (void) CloseBlob(image);
4995
4996   if (image->columns == 0 || image->rows == 0)
4997     {
4998       if (logging != MagickFalse)
4999         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5000           "exit ReadJNGImage() with error");
5001
5002       ThrowReaderException(CorruptImageError,"CorruptImage");
5003     }
5004
5005   if (logging != MagickFalse)
5006     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5007
5008   return(image);
5009 }
5010 #endif
5011
5012 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5013 {
5014   char
5015     page_geometry[MaxTextExtent];
5016
5017   Image
5018     *image,
5019     *previous;
5020
5021   MagickBooleanType
5022     logging,
5023     have_mng_structure;
5024
5025   volatile int
5026     first_mng_object,
5027     object_id,
5028     term_chunk_found,
5029     skip_to_iend;
5030
5031   volatile ssize_t
5032     image_count=0;
5033
5034   MagickBooleanType
5035     status;
5036
5037   MagickOffsetType
5038     offset;
5039
5040   MngInfo
5041     *mng_info;
5042
5043   MngBox
5044     default_fb,
5045     fb,
5046     previous_fb;
5047
5048 #if defined(MNG_INSERT_LAYERS)
5049   PixelInfo
5050     mng_background_color;
5051 #endif
5052
5053   register unsigned char
5054     *p;
5055
5056   register ssize_t
5057     i;
5058
5059   size_t
5060     count;
5061
5062   ssize_t
5063     loop_level;
5064
5065   volatile short
5066     skipping_loop;
5067
5068 #if defined(MNG_INSERT_LAYERS)
5069   unsigned int
5070     mandatory_back=0;
5071 #endif
5072
5073   volatile unsigned int
5074 #ifdef MNG_OBJECT_BUFFERS
5075     mng_background_object=0,
5076 #endif
5077     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5078
5079   size_t
5080     default_frame_timeout,
5081     frame_timeout,
5082 #if defined(MNG_INSERT_LAYERS)
5083     image_height,
5084     image_width,
5085 #endif
5086     length;
5087
5088   /* These delays are all measured in image ticks_per_second,
5089    * not in MNG ticks_per_second
5090    */
5091   volatile size_t
5092     default_frame_delay,
5093     final_delay,
5094     final_image_delay,
5095     frame_delay,
5096 #if defined(MNG_INSERT_LAYERS)
5097     insert_layers,
5098 #endif
5099     mng_iterations=1,
5100     simplicity=0,
5101     subframe_height=0,
5102     subframe_width=0;
5103
5104   previous_fb.top=0;
5105   previous_fb.bottom=0;
5106   previous_fb.left=0;
5107   previous_fb.right=0;
5108   default_fb.top=0;
5109   default_fb.bottom=0;
5110   default_fb.left=0;
5111   default_fb.right=0;
5112
5113   /* Open image file.  */
5114
5115   assert(image_info != (const ImageInfo *) NULL);
5116   assert(image_info->signature == MagickSignature);
5117   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
5118   assert(exception != (ExceptionInfo *) NULL);
5119   assert(exception->signature == MagickSignature);
5120   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
5121   image=AcquireImage(image_info,exception);
5122   mng_info=(MngInfo *) NULL;
5123   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5124
5125   if (status == MagickFalse)
5126     return((Image *) NULL);
5127
5128   first_mng_object=MagickFalse;
5129   skipping_loop=(-1);
5130   have_mng_structure=MagickFalse;
5131
5132   /* Allocate a MngInfo structure.  */
5133
5134   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
5135
5136   if (mng_info == (MngInfo *) NULL)
5137     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5138
5139   /* Initialize members of the MngInfo structure.  */
5140
5141   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5142   mng_info->image=image;
5143   have_mng_structure=MagickTrue;
5144
5145   if (LocaleCompare(image_info->magick,"MNG") == 0)
5146     {
5147       char
5148         magic_number[MaxTextExtent];
5149
5150       /* Verify MNG signature.  */
5151       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5152       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5153         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5154
5155       /* Initialize some nonzero members of the MngInfo structure.  */
5156       for (i=0; i < MNG_MAX_OBJECTS; i++)
5157       {
5158         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5159         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5160       }
5161       mng_info->exists[0]=MagickTrue;
5162     }
5163
5164   first_mng_object=MagickTrue;
5165   mng_type=0;
5166 #if defined(MNG_INSERT_LAYERS)
5167   insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5168 #endif
5169   default_frame_delay=0;
5170   default_frame_timeout=0;
5171   frame_delay=0;
5172   final_delay=1;
5173   mng_info->ticks_per_second=1UL*image->ticks_per_second;
5174   object_id=0;
5175   skip_to_iend=MagickFalse;
5176   term_chunk_found=MagickFalse;
5177   mng_info->framing_mode=1;
5178 #if defined(MNG_INSERT_LAYERS)
5179   mandatory_back=MagickFalse;
5180 #endif
5181 #if defined(MNG_INSERT_LAYERS)
5182   mng_background_color=image->background_color;
5183 #endif
5184   default_fb=mng_info->frame;
5185   previous_fb=mng_info->frame;
5186   do
5187   {
5188     char
5189       type[MaxTextExtent];
5190
5191     if (LocaleCompare(image_info->magick,"MNG") == 0)
5192       {
5193         unsigned char
5194           *chunk;
5195
5196         /*
5197           Read a new chunk.
5198         */
5199         type[0]='\0';
5200         (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
5201         length=ReadBlobMSBLong(image);
5202         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5203
5204         if (logging != MagickFalse)
5205           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5206            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
5207            type[0],type[1],type[2],type[3],(double) length);
5208
5209         if (length > PNG_UINT_31_MAX)
5210           status=MagickFalse;
5211
5212         if (count == 0)
5213           ThrowReaderException(CorruptImageError,"CorruptImage");
5214
5215         p=NULL;
5216         chunk=(unsigned char *) NULL;
5217
5218         if (length)
5219           {
5220             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5221
5222             if (chunk == (unsigned char *) NULL)
5223               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5224
5225             for (i=0; i < (ssize_t) length; i++)
5226               chunk[i]=(unsigned char) ReadBlobByte(image);
5227
5228             p=chunk;
5229           }
5230
5231         (void) ReadBlobMSBLong(image);  /* read crc word */
5232
5233 #if !defined(JNG_SUPPORTED)
5234         if (memcmp(type,mng_JHDR,4) == 0)
5235           {
5236             skip_to_iend=MagickTrue;
5237
5238             if (mng_info->jhdr_warning == 0)
5239               (void) ThrowMagickException(exception,GetMagickModule(),
5240                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5241
5242             mng_info->jhdr_warning++;
5243           }
5244 #endif
5245         if (memcmp(type,mng_DHDR,4) == 0)
5246           {
5247             skip_to_iend=MagickTrue;
5248
5249             if (mng_info->dhdr_warning == 0)
5250               (void) ThrowMagickException(exception,GetMagickModule(),
5251                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5252
5253             mng_info->dhdr_warning++;
5254           }
5255         if (memcmp(type,mng_MEND,4) == 0)
5256           break;
5257
5258         if (skip_to_iend)
5259           {
5260             if (memcmp(type,mng_IEND,4) == 0)
5261               skip_to_iend=MagickFalse;
5262
5263             if (length)
5264               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5265
5266             if (logging != MagickFalse)
5267               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5268                 "  Skip to IEND.");
5269
5270             continue;
5271           }
5272
5273         if (memcmp(type,mng_MHDR,4) == 0)
5274           {
5275             mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5276                 (p[2] << 8) | p[3]);
5277
5278             mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5279                 (p[6] << 8) | p[7]);
5280
5281             if (logging != MagickFalse)
5282               {
5283                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5284                   "  MNG width: %.20g",(double) mng_info->mng_width);
5285                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5286                   "  MNG height: %.20g",(double) mng_info->mng_height);
5287               }
5288
5289             p+=8;
5290             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5291
5292             if (mng_info->ticks_per_second == 0)
5293               default_frame_delay=0;
5294
5295             else
5296               default_frame_delay=1UL*image->ticks_per_second/
5297                 mng_info->ticks_per_second;
5298
5299             frame_delay=default_frame_delay;
5300             simplicity=0;
5301
5302             if (length > 16)
5303               {
5304                 p+=16;
5305                 simplicity=(size_t) mng_get_long(p);
5306               }
5307
5308             mng_type=1;    /* Full MNG */
5309
5310             if ((simplicity != 0) && ((simplicity | 11) == 11))
5311               mng_type=2; /* LC */
5312
5313             if ((simplicity != 0) && ((simplicity | 9) == 9))
5314               mng_type=3; /* VLC */
5315
5316 #if defined(MNG_INSERT_LAYERS)
5317             if (mng_type != 3)
5318               insert_layers=MagickTrue;
5319 #endif
5320             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5321               {
5322                 /* Allocate next image structure.  */
5323                 AcquireNextImage(image_info,image,exception);
5324
5325                 if (GetNextImageInList(image) == (Image *) NULL)
5326                   return((Image *) NULL);
5327
5328                 image=SyncNextImageInList(image);
5329                 mng_info->image=image;
5330               }
5331
5332             if ((mng_info->mng_width > 65535L) ||
5333                 (mng_info->mng_height > 65535L))
5334               ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5335
5336             (void) FormatLocaleString(page_geometry,MaxTextExtent,
5337               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5338               mng_info->mng_height);
5339
5340             mng_info->frame.left=0;
5341             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5342             mng_info->frame.top=0;
5343             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5344             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5345
5346             for (i=0; i < MNG_MAX_OBJECTS; i++)
5347               mng_info->object_clip[i]=mng_info->frame;
5348
5349             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5350             continue;
5351           }
5352
5353         if (memcmp(type,mng_TERM,4) == 0)
5354           {
5355             int
5356               repeat=0;
5357
5358
5359             if (length)
5360               repeat=p[0];
5361
5362             if (repeat == 3)
5363               {
5364                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5365                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5366
5367                 if (mng_iterations == PNG_UINT_31_MAX)
5368                   mng_iterations=0;
5369
5370                 image->iterations=mng_iterations;
5371                 term_chunk_found=MagickTrue;
5372               }
5373
5374             if (logging != MagickFalse)
5375               {
5376                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5377                   "    repeat=%d",repeat);
5378
5379                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5380                   "    final_delay=%.20g",(double) final_delay);
5381
5382                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5383                   "    image->iterations=%.20g",(double) image->iterations);
5384               }
5385
5386             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5387             continue;
5388           }
5389         if (memcmp(type,mng_DEFI,4) == 0)
5390           {
5391             if (mng_type == 3)
5392               (void) ThrowMagickException(exception,GetMagickModule(),
5393                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5394                 image->filename);
5395
5396             object_id=(p[0] << 8) | p[1];
5397
5398             if (mng_type == 2 && object_id != 0)
5399               (void) ThrowMagickException(exception,GetMagickModule(),
5400                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5401                 image->filename);
5402
5403             if (object_id > MNG_MAX_OBJECTS)
5404               {
5405                 /*
5406                   Instead of using a warning we should allocate a larger
5407                   MngInfo structure and continue.
5408                 */
5409                 (void) ThrowMagickException(exception,GetMagickModule(),
5410                   CoderError,"object id too large","`%s'",image->filename);
5411                 object_id=MNG_MAX_OBJECTS;
5412               }
5413
5414             if (mng_info->exists[object_id])
5415               if (mng_info->frozen[object_id])
5416                 {
5417                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5418                   (void) ThrowMagickException(exception,
5419                     GetMagickModule(),CoderError,
5420                     "DEFI cannot redefine a frozen MNG object","`%s'",
5421                     image->filename);
5422                   continue;
5423                 }
5424
5425             mng_info->exists[object_id]=MagickTrue;
5426
5427             if (length > 2)
5428               mng_info->invisible[object_id]=p[2];
5429
5430             /*
5431               Extract object offset info.
5432             */
5433             if (length > 11)
5434               {
5435                 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5436                     (p[5] << 16) | (p[6] << 8) | p[7]);
5437
5438                 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5439                     (p[9] << 16) | (p[10] << 8) | p[11]);
5440
5441                 if (logging != MagickFalse)
5442                   {
5443                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5444                       "  x_off[%d]: %.20g",object_id,(double)
5445                       mng_info->x_off[object_id]);
5446
5447                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5448                       "  y_off[%d]: %.20g",object_id,(double)
5449                       mng_info->y_off[object_id]);
5450                   }
5451               }
5452
5453             /*
5454               Extract object clipping info.
5455             */
5456             if (length > 27)
5457               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5458                 &p[12]);
5459
5460             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5461             continue;
5462           }
5463         if (memcmp(type,mng_bKGD,4) == 0)
5464           {
5465             mng_info->have_global_bkgd=MagickFalse;
5466
5467             if (length > 5)
5468               {
5469                 mng_info->mng_global_bkgd.red=
5470                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5471
5472                 mng_info->mng_global_bkgd.green=
5473                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5474
5475                 mng_info->mng_global_bkgd.blue=
5476                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5477
5478                 mng_info->have_global_bkgd=MagickTrue;
5479               }
5480
5481             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5482             continue;
5483           }
5484         if (memcmp(type,mng_BACK,4) == 0)
5485           {
5486 #if defined(MNG_INSERT_LAYERS)
5487             if (length > 6)
5488               mandatory_back=p[6];
5489
5490             else
5491               mandatory_back=0;
5492
5493             if (mandatory_back && length > 5)
5494               {
5495                 mng_background_color.red=
5496                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5497
5498                 mng_background_color.green=
5499                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5500
5501                 mng_background_color.blue=
5502                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5503
5504                 mng_background_color.alpha=OpaqueAlpha;
5505               }
5506
5507 #ifdef MNG_OBJECT_BUFFERS
5508             if (length > 8)
5509               mng_background_object=(p[7] << 8) | p[8];
5510 #endif
5511 #endif
5512             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5513             continue;
5514           }
5515
5516         if (memcmp(type,mng_PLTE,4) == 0)
5517           {
5518             /* Read global PLTE.  */
5519
5520             if (length && (length < 769))
5521               {
5522                 if (mng_info->global_plte == (png_colorp) NULL)
5523                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5524                     sizeof(*mng_info->global_plte));
5525
5526                 for (i=0; i < (ssize_t) (length/3); i++)
5527                 {
5528                   mng_info->global_plte[i].red=p[3*i];
5529                   mng_info->global_plte[i].green=p[3*i+1];
5530                   mng_info->global_plte[i].blue=p[3*i+2];
5531                 }
5532
5533                 mng_info->global_plte_length=(unsigned int) (length/3);
5534               }
5535 #ifdef MNG_LOOSE
5536             for ( ; i < 256; i++)
5537             {
5538               mng_info->global_plte[i].red=i;
5539               mng_info->global_plte[i].green=i;
5540               mng_info->global_plte[i].blue=i;
5541             }
5542
5543             if (length)
5544               mng_info->global_plte_length=256;
5545 #endif
5546             else
5547               mng_info->global_plte_length=0;
5548
5549             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5550             continue;
5551           }
5552
5553         if (memcmp(type,mng_tRNS,4) == 0)
5554           {
5555             /* read global tRNS */
5556
5557             if (length < 257)
5558               for (i=0; i < (ssize_t) length; i++)
5559                 mng_info->global_trns[i]=p[i];
5560
5561 #ifdef MNG_LOOSE
5562             for ( ; i < 256; i++)
5563               mng_info->global_trns[i]=255;
5564 #endif
5565             mng_info->global_trns_length=(unsigned int) length;
5566             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5567             continue;
5568           }
5569         if (memcmp(type,mng_gAMA,4) == 0)
5570           {
5571             if (length == 4)
5572               {
5573                 ssize_t
5574                   igamma;
5575
5576                 igamma=mng_get_long(p);
5577                 mng_info->global_gamma=((float) igamma)*0.00001;
5578                 mng_info->have_global_gama=MagickTrue;
5579               }
5580
5581             else
5582               mng_info->have_global_gama=MagickFalse;
5583
5584             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5585             continue;
5586           }
5587
5588         if (memcmp(type,mng_cHRM,4) == 0)
5589           {
5590             /* Read global cHRM */
5591
5592             if (length == 32)
5593               {
5594                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5595                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5596                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5597                 mng_info->global_chrm.red_primary.y=0.00001*
5598                   mng_get_long(&p[12]);
5599                 mng_info->global_chrm.green_primary.x=0.00001*
5600                   mng_get_long(&p[16]);
5601                 mng_info->global_chrm.green_primary.y=0.00001*
5602                   mng_get_long(&p[20]);
5603                 mng_info->global_chrm.blue_primary.x=0.00001*
5604                   mng_get_long(&p[24]);
5605                 mng_info->global_chrm.blue_primary.y=0.00001*
5606                   mng_get_long(&p[28]);
5607                 mng_info->have_global_chrm=MagickTrue;
5608               }
5609             else
5610               mng_info->have_global_chrm=MagickFalse;
5611
5612             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5613             continue;
5614           }
5615
5616         if (memcmp(type,mng_sRGB,4) == 0)
5617           {
5618             /*
5619               Read global sRGB.
5620             */
5621             if (length)
5622               {
5623                 mng_info->global_srgb_intent=
5624                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5625                 mng_info->have_global_srgb=MagickTrue;
5626               }
5627             else
5628               mng_info->have_global_srgb=MagickFalse;
5629
5630             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5631             continue;
5632           }
5633
5634         if (memcmp(type,mng_iCCP,4) == 0)
5635           {
5636             /* To do: */
5637
5638             /*
5639               Read global iCCP.
5640             */
5641             if (length)
5642               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5643
5644             continue;
5645           }
5646
5647         if (memcmp(type,mng_FRAM,4) == 0)
5648           {
5649             if (mng_type == 3)
5650               (void) ThrowMagickException(exception,GetMagickModule(),
5651                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5652                 image->filename);
5653
5654             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5655               image->delay=frame_delay;
5656
5657             frame_delay=default_frame_delay;
5658             frame_timeout=default_frame_timeout;
5659             fb=default_fb;
5660
5661             if (length)
5662               if (p[0])
5663                 mng_info->framing_mode=p[0];
5664
5665             if (logging != MagickFalse)
5666               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5667                 "    Framing_mode=%d",mng_info->framing_mode);
5668
5669             if (length > 6)
5670               {
5671                 /* Note the delay and frame clipping boundaries.  */
5672
5673                 p++; /* framing mode */
5674
5675                 while (*p && ((p-chunk) < (ssize_t) length))
5676                   p++;  /* frame name */
5677
5678                 p++;  /* frame name terminator */
5679
5680                 if ((p-chunk) < (ssize_t) (length-4))
5681                   {
5682                     int
5683                       change_delay,
5684                       change_timeout,
5685                       change_clipping;
5686
5687                     change_delay=(*p++);
5688                     change_timeout=(*p++);
5689                     change_clipping=(*p++);
5690                     p++; /* change_sync */
5691
5692                     if (change_delay)
5693                       {
5694                         frame_delay=1UL*image->ticks_per_second*
5695                           mng_get_long(p);
5696
5697                         if (mng_info->ticks_per_second != 0)
5698                           frame_delay/=mng_info->ticks_per_second;
5699
5700                         else
5701                           frame_delay=PNG_UINT_31_MAX;
5702
5703                         if (change_delay == 2)
5704                           default_frame_delay=frame_delay;
5705
5706                         p+=4;
5707
5708                         if (logging != MagickFalse)
5709                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5710                             "    Framing_delay=%.20g",(double) frame_delay);
5711                       }
5712
5713                     if (change_timeout)
5714                       {
5715                         frame_timeout=1UL*image->ticks_per_second*
5716                           mng_get_long(p);
5717
5718                         if (mng_info->ticks_per_second != 0)
5719                           frame_timeout/=mng_info->ticks_per_second;
5720
5721                         else
5722                           frame_timeout=PNG_UINT_31_MAX;
5723
5724                         if (change_delay == 2)
5725                           default_frame_timeout=frame_timeout;
5726
5727                         p+=4;
5728
5729                         if (logging != MagickFalse)
5730                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5731                             "    Framing_timeout=%.20g",(double) frame_timeout);
5732                       }
5733
5734                     if (change_clipping)
5735                       {
5736                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5737                         p+=17;
5738                         previous_fb=fb;
5739
5740                         if (logging != MagickFalse)
5741                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5742                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5743                             (double) fb.left,(double) fb.right,(double) fb.top,
5744                             (double) fb.bottom);
5745
5746                         if (change_clipping == 2)
5747                           default_fb=fb;
5748                       }
5749                   }
5750               }
5751             mng_info->clip=fb;
5752             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5753
5754             subframe_width=(size_t) (mng_info->clip.right
5755                -mng_info->clip.left);
5756
5757             subframe_height=(size_t) (mng_info->clip.bottom
5758                -mng_info->clip.top);
5759             /*
5760               Insert a background layer behind the frame if framing_mode is 4.
5761             */
5762 #if defined(MNG_INSERT_LAYERS)
5763             if (logging != MagickFalse)
5764               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5765                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
5766                 subframe_width,(double) subframe_height);
5767
5768             if (insert_layers && (mng_info->framing_mode == 4) &&
5769                 (subframe_width) && (subframe_height))
5770               {
5771                 /* Allocate next image structure.  */
5772                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5773                   {
5774                     AcquireNextImage(image_info,image,exception);
5775
5776                     if (GetNextImageInList(image) == (Image *) NULL)
5777                       {
5778                         image=DestroyImageList(image);
5779                         MngInfoFreeStruct(mng_info,&have_mng_structure);
5780                         return((Image *) NULL);
5781                       }
5782
5783                     image=SyncNextImageInList(image);
5784                   }
5785
5786                 mng_info->image=image;
5787
5788                 if (term_chunk_found)
5789                   {
5790                     image->start_loop=MagickTrue;
5791                     image->iterations=mng_iterations;
5792                     term_chunk_found=MagickFalse;
5793                   }
5794
5795                 else
5796                     image->start_loop=MagickFalse;
5797
5798                 image->columns=subframe_width;
5799                 image->rows=subframe_height;
5800                 image->page.width=subframe_width;
5801                 image->page.height=subframe_height;
5802                 image->page.x=mng_info->clip.left;
5803                 image->page.y=mng_info->clip.top;
5804                 image->background_color=mng_background_color;
5805                 image->alpha_trait=UndefinedPixelTrait;
5806                 image->delay=0;
5807                 (void) SetImageBackgroundColor(image,exception);
5808
5809                 if (logging != MagickFalse)
5810                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5811                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5812                     (double) mng_info->clip.left,(double) mng_info->clip.right,
5813                     (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5814               }
5815 #endif
5816             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5817             continue;
5818           }
5819         if (memcmp(type,mng_CLIP,4) == 0)
5820           {
5821             unsigned int
5822               first_object,
5823               last_object;
5824
5825             /*
5826               Read CLIP.
5827             */
5828             first_object=(p[0] << 8) | p[1];
5829             last_object=(p[2] << 8) | p[3];
5830
5831             for (i=(int) first_object; i <= (int) last_object; i++)
5832             {
5833               if (mng_info->exists[i] && !mng_info->frozen[i])
5834                 {
5835                   MngBox
5836                     box;
5837
5838                   box=mng_info->object_clip[i];
5839                   mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5840                 }
5841             }
5842
5843             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5844             continue;
5845           }
5846         if (memcmp(type,mng_SAVE,4) == 0)
5847           {
5848             for (i=1; i < MNG_MAX_OBJECTS; i++)
5849               if (mng_info->exists[i])
5850                 {
5851                  mng_info->frozen[i]=MagickTrue;
5852 #ifdef MNG_OBJECT_BUFFERS
5853                  if (mng_info->ob[i] != (MngBuffer *) NULL)
5854                     mng_info->ob[i]->frozen=MagickTrue;
5855 #endif
5856                 }
5857
5858             if (length)
5859               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5860
5861             continue;
5862           }
5863
5864         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5865           {
5866             /* Read DISC or SEEK.  */
5867
5868             if ((length == 0) || !memcmp(type,mng_SEEK,4))
5869               {
5870                 for (i=1; i < MNG_MAX_OBJECTS; i++)
5871                   MngInfoDiscardObject(mng_info,i);
5872               }
5873
5874             else
5875               {
5876                 register ssize_t
5877                   j;
5878
5879                 for (j=0; j < (ssize_t) length; j+=2)
5880                 {
5881                   i=p[j] << 8 | p[j+1];
5882                   MngInfoDiscardObject(mng_info,i);
5883                 }
5884               }
5885
5886             if (length)
5887               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5888
5889             continue;
5890           }
5891
5892         if (memcmp(type,mng_MOVE,4) == 0)
5893           {
5894             size_t
5895               first_object,
5896               last_object;
5897
5898             /* read MOVE */
5899
5900             first_object=(p[0] << 8) | p[1];
5901             last_object=(p[2] << 8) | p[3];
5902             for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5903             {
5904               if (mng_info->exists[i] && !mng_info->frozen[i])
5905                 {
5906                   MngPair
5907                     new_pair;
5908
5909                   MngPair
5910                     old_pair;
5911
5912                   old_pair.a=mng_info->x_off[i];
5913                   old_pair.b=mng_info->y_off[i];
5914                   new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5915                   mng_info->x_off[i]=new_pair.a;
5916                   mng_info->y_off[i]=new_pair.b;
5917                 }
5918             }
5919
5920             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5921             continue;
5922           }
5923
5924         if (memcmp(type,mng_LOOP,4) == 0)
5925           {
5926             ssize_t loop_iters=1;
5927             loop_level=chunk[0];
5928             mng_info->loop_active[loop_level]=1;  /* mark loop active */
5929
5930             /* Record starting point.  */
5931             loop_iters=mng_get_long(&chunk[1]);
5932
5933             if (logging != MagickFalse)
5934               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5935                 "  LOOP level %.20g has %.20g iterations ",(double) loop_level,
5936                 (double) loop_iters);
5937
5938             if (loop_iters == 0)
5939               skipping_loop=loop_level;
5940
5941             else
5942               {
5943                 mng_info->loop_jump[loop_level]=TellBlob(image);
5944                 mng_info->loop_count[loop_level]=loop_iters;
5945               }
5946
5947             mng_info->loop_iteration[loop_level]=0;
5948             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5949             continue;
5950           }
5951
5952         if (memcmp(type,mng_ENDL,4) == 0)
5953           {
5954             loop_level=chunk[0];
5955
5956             if (skipping_loop > 0)
5957               {
5958                 if (skipping_loop == loop_level)
5959                   {
5960                     /*
5961                       Found end of zero-iteration loop.
5962                     */
5963                     skipping_loop=(-1);
5964                     mng_info->loop_active[loop_level]=0;
5965                   }
5966               }
5967
5968             else
5969               {
5970                 if (mng_info->loop_active[loop_level] == 1)
5971                   {
5972                     mng_info->loop_count[loop_level]--;
5973                     mng_info->loop_iteration[loop_level]++;
5974
5975                     if (logging != MagickFalse)
5976                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5977                         "  ENDL: LOOP level %.20g has %.20g remaining iters ",
5978                         (double) loop_level,(double)
5979                         mng_info->loop_count[loop_level]);
5980
5981                     if (mng_info->loop_count[loop_level] != 0)
5982                       {
5983                         offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5984                           SEEK_SET);
5985
5986                         if (offset < 0)
5987                           ThrowReaderException(CorruptImageError,
5988                             "ImproperImageHeader");
5989                       }
5990
5991                     else
5992                       {
5993                         short
5994                           last_level;
5995
5996                         /*
5997                           Finished loop.
5998                         */
5999                         mng_info->loop_active[loop_level]=0;
6000                         last_level=(-1);
6001                         for (i=0; i < loop_level; i++)
6002                           if (mng_info->loop_active[i] == 1)
6003                             last_level=(short) i;
6004                         loop_level=last_level;
6005                       }
6006                   }
6007               }
6008
6009             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6010             continue;
6011           }
6012
6013         if (memcmp(type,mng_CLON,4) == 0)
6014           {
6015             if (mng_info->clon_warning == 0)
6016               (void) ThrowMagickException(exception,GetMagickModule(),
6017                 CoderError,"CLON is not implemented yet","`%s'",
6018                 image->filename);
6019
6020             mng_info->clon_warning++;
6021           }
6022
6023         if (memcmp(type,mng_MAGN,4) == 0)
6024           {
6025             png_uint_16
6026               magn_first,
6027               magn_last,
6028               magn_mb,
6029               magn_ml,
6030               magn_mr,
6031               magn_mt,
6032               magn_mx,
6033               magn_my,
6034               magn_methx,
6035               magn_methy;
6036
6037             if (length > 1)
6038               magn_first=(p[0] << 8) | p[1];
6039
6040             else
6041               magn_first=0;
6042
6043             if (length > 3)
6044               magn_last=(p[2] << 8) | p[3];
6045
6046             else
6047               magn_last=magn_first;
6048 #ifndef MNG_OBJECT_BUFFERS
6049             if (magn_first || magn_last)
6050               if (mng_info->magn_warning == 0)
6051                 {
6052                   (void) ThrowMagickException(exception,
6053                      GetMagickModule(),CoderError,
6054                      "MAGN is not implemented yet for nonzero objects",
6055                      "`%s'",image->filename);
6056
6057                    mng_info->magn_warning++;
6058                 }
6059 #endif
6060             if (length > 4)
6061               magn_methx=p[4];
6062
6063             else
6064               magn_methx=0;
6065
6066             if (length > 6)
6067               magn_mx=(p[5] << 8) | p[6];
6068
6069             else
6070               magn_mx=1;
6071
6072             if (magn_mx == 0)
6073               magn_mx=1;
6074
6075             if (length > 8)
6076               magn_my=(p[7] << 8) | p[8];
6077
6078             else
6079               magn_my=magn_mx;
6080
6081             if (magn_my == 0)
6082               magn_my=1;
6083
6084             if (length > 10)
6085               magn_ml=(p[9] << 8) | p[10];
6086
6087             else
6088               magn_ml=magn_mx;
6089
6090             if (magn_ml == 0)
6091               magn_ml=1;
6092
6093             if (length > 12)
6094               magn_mr=(p[11] << 8) | p[12];
6095
6096             else
6097               magn_mr=magn_mx;
6098
6099             if (magn_mr == 0)
6100               magn_mr=1;
6101
6102             if (length > 14)
6103               magn_mt=(p[13] << 8) | p[14];
6104
6105             else
6106               magn_mt=magn_my;
6107
6108             if (magn_mt == 0)
6109               magn_mt=1;
6110
6111             if (length > 16)
6112               magn_mb=(p[15] << 8) | p[16];
6113
6114             else
6115               magn_mb=magn_my;
6116
6117             if (magn_mb == 0)
6118               magn_mb=1;
6119
6120             if (length > 17)
6121               magn_methy=p[17];
6122
6123             else
6124               magn_methy=magn_methx;
6125
6126
6127             if (magn_methx > 5 || magn_methy > 5)
6128               if (mng_info->magn_warning == 0)
6129                 {
6130                   (void) ThrowMagickException(exception,
6131                      GetMagickModule(),CoderError,
6132                      "Unknown MAGN method in MNG datastream","`%s'",
6133                      image->filename);
6134
6135                    mng_info->magn_warning++;
6136                 }
6137 #ifdef MNG_OBJECT_BUFFERS
6138           /* Magnify existing objects in the range magn_first to magn_last */
6139 #endif
6140             if (magn_first == 0 || magn_last == 0)
6141               {
6142                 /* Save the magnification factors for object 0 */
6143                 mng_info->magn_mb=magn_mb;
6144                 mng_info->magn_ml=magn_ml;
6145                 mng_info->magn_mr=magn_mr;
6146                 mng_info->magn_mt=magn_mt;
6147                 mng_info->magn_mx=magn_mx;
6148                 mng_info->magn_my=magn_my;
6149                 mng_info->magn_methx=magn_methx;
6150                 mng_info->magn_methy=magn_methy;
6151               }
6152           }
6153
6154         if (memcmp(type,mng_PAST,4) == 0)
6155           {
6156             if (mng_info->past_warning == 0)
6157               (void) ThrowMagickException(exception,GetMagickModule(),
6158                 CoderError,"PAST is not implemented yet","`%s'",
6159                 image->filename);
6160
6161             mng_info->past_warning++;
6162           }
6163
6164         if (memcmp(type,mng_SHOW,4) == 0)
6165           {
6166             if (mng_info->show_warning == 0)
6167               (void) ThrowMagickException(exception,GetMagickModule(),
6168                 CoderError,"SHOW is not implemented yet","`%s'",
6169                 image->filename);
6170
6171             mng_info->show_warning++;
6172           }
6173
6174         if (memcmp(type,mng_sBIT,4) == 0)
6175           {
6176             if (length < 4)
6177               mng_info->have_global_sbit=MagickFalse;
6178
6179             else
6180               {
6181                 mng_info->global_sbit.gray=p[0];
6182                 mng_info->global_sbit.red=p[0];
6183                 mng_info->global_sbit.green=p[1];
6184                 mng_info->global_sbit.blue=p[2];
6185                 mng_info->global_sbit.alpha=p[3];
6186                 mng_info->have_global_sbit=MagickTrue;
6187              }
6188           }
6189         if (memcmp(type,mng_pHYs,4) == 0)
6190           {
6191             if (length > 8)
6192               {
6193                 mng_info->global_x_pixels_per_unit=
6194                     (size_t) mng_get_long(p);
6195                 mng_info->global_y_pixels_per_unit=
6196                     (size_t) mng_get_long(&p[4]);
6197                 mng_info->global_phys_unit_type=p[8];
6198                 mng_info->have_global_phys=MagickTrue;
6199               }
6200
6201             else
6202               mng_info->have_global_phys=MagickFalse;
6203           }
6204         if (memcmp(type,mng_pHYg,4) == 0)
6205           {
6206             if (mng_info->phyg_warning == 0)
6207               (void) ThrowMagickException(exception,GetMagickModule(),
6208                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6209
6210             mng_info->phyg_warning++;
6211           }
6212         if (memcmp(type,mng_BASI,4) == 0)
6213           {
6214             skip_to_iend=MagickTrue;
6215
6216             if (mng_info->basi_warning == 0)
6217               (void) ThrowMagickException(exception,GetMagickModule(),
6218                 CoderError,"BASI is not implemented yet","`%s'",
6219                 image->filename);
6220
6221             mng_info->basi_warning++;
6222 #ifdef MNG_BASI_SUPPORTED
6223             basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6224                (p[2] << 8) | p[3]);
6225             basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6226                (p[6] << 8) | p[7]);
6227             basi_color_type=p[8];
6228             basi_compression_method=p[9];
6229             basi_filter_type=p[10];
6230             basi_interlace_method=p[11];
6231             if (length > 11)
6232               basi_red=(p[12] << 8) & p[13];
6233
6234             else
6235               basi_red=0;
6236
6237             if (length > 13)
6238               basi_green=(p[14] << 8) & p[15];
6239
6240             else
6241               basi_green=0;
6242
6243             if (length > 15)
6244               basi_blue=(p[16] << 8) & p[17];
6245
6246             else
6247               basi_blue=0;
6248
6249             if (length > 17)
6250               basi_alpha=(p[18] << 8) & p[19];
6251
6252             else
6253               {
6254                 if (basi_sample_depth == 16)
6255                   basi_alpha=65535L;
6256                 else
6257                   basi_alpha=255;
6258               }
6259
6260             if (length > 19)
6261               basi_viewable=p[20];
6262
6263             else
6264               basi_viewable=0;
6265
6266 #endif
6267             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6268             continue;
6269           }
6270
6271         if (memcmp(type,mng_IHDR,4)
6272 #if defined(JNG_SUPPORTED)
6273             && memcmp(type,mng_JHDR,4)
6274 #endif
6275             )
6276           {
6277             /* Not an IHDR or JHDR chunk */
6278             if (length)
6279               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6280
6281             continue;
6282           }
6283 /* Process IHDR */
6284         if (logging != MagickFalse)
6285           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6286             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6287
6288         mng_info->exists[object_id]=MagickTrue;
6289         mng_info->viewable[object_id]=MagickTrue;
6290
6291         if (mng_info->invisible[object_id])
6292           {
6293             if (logging != MagickFalse)
6294               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6295                 "  Skipping invisible object");
6296
6297             skip_to_iend=MagickTrue;
6298             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6299             continue;
6300           }
6301 #if defined(MNG_INSERT_LAYERS)
6302         if (length < 8)
6303           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6304
6305         image_width=(size_t) mng_get_long(p);
6306         image_height=(size_t) mng_get_long(&p[4]);
6307 #endif
6308         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6309
6310         /*
6311           Insert a transparent background layer behind the entire animation
6312           if it is not full screen.
6313         */
6314 #if defined(MNG_INSERT_LAYERS)
6315         if (insert_layers && mng_type && first_mng_object)
6316           {
6317             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6318                 (image_width < mng_info->mng_width) ||
6319                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6320                 (image_height < mng_info->mng_height) ||
6321                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6322               {
6323                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6324                   {
6325                     /*
6326                       Allocate next image structure.
6327                     */
6328                     AcquireNextImage(image_info,image,exception);
6329
6330                     if (GetNextImageInList(image) == (Image *) NULL)
6331                       {
6332                         image=DestroyImageList(image);
6333                         MngInfoFreeStruct(mng_info,&have_mng_structure);
6334                         return((Image *) NULL);
6335                       }
6336
6337                     image=SyncNextImageInList(image);
6338                   }
6339                 mng_info->image=image;
6340
6341                 if (term_chunk_found)
6342                   {
6343                     image->start_loop=MagickTrue;
6344                     image->iterations=mng_iterations;
6345                     term_chunk_found=MagickFalse;
6346                   }
6347
6348                 else
6349                     image->start_loop=MagickFalse;
6350
6351                 /* Make a background rectangle.  */
6352
6353                 image->delay=0;
6354                 image->columns=mng_info->mng_width;
6355                 image->rows=mng_info->mng_height;
6356                 image->page.width=mng_info->mng_width;
6357                 image->page.height=mng_info->mng_height;
6358                 image->page.x=0;
6359                 image->page.y=0;
6360                 image->background_color=mng_background_color;
6361                 (void) SetImageBackgroundColor(image,exception);
6362                 if (logging != MagickFalse)
6363                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6364                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6365                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6366               }
6367           }
6368         /*
6369           Insert a background layer behind the upcoming image if
6370           framing_mode is 3, and we haven't already inserted one.
6371         */
6372         if (insert_layers && (mng_info->framing_mode == 3) &&
6373                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6374                 (simplicity & 0x08)))
6375           {
6376             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6377             {
6378               /*
6379                 Allocate next image structure.
6380               */
6381               AcquireNextImage(image_info,image,exception);
6382
6383               if (GetNextImageInList(image) == (Image *) NULL)
6384                 {
6385                   image=DestroyImageList(image);
6386                   MngInfoFreeStruct(mng_info,&have_mng_structure);
6387                   return((Image *) NULL);
6388                 }
6389
6390               image=SyncNextImageInList(image);
6391             }
6392
6393             mng_info->image=image;
6394
6395             if (term_chunk_found)
6396               {
6397                 image->start_loop=MagickTrue;
6398                 image->iterations=mng_iterations;
6399                 term_chunk_found=MagickFalse;
6400               }
6401
6402             else
6403                 image->start_loop=MagickFalse;
6404
6405             image->delay=0;
6406             image->columns=subframe_width;
6407             image->rows=subframe_height;
6408             image->page.width=subframe_width;
6409             image->page.height=subframe_height;
6410             image->page.x=mng_info->clip.left;
6411             image->page.y=mng_info->clip.top;
6412             image->background_color=mng_background_color;
6413             image->alpha_trait=UndefinedPixelTrait;
6414             (void) SetImageBackgroundColor(image,exception);
6415
6416             if (logging != MagickFalse)
6417               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6418                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6419                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6420                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6421           }
6422 #endif /* MNG_INSERT_LAYERS */
6423         first_mng_object=MagickFalse;
6424
6425         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6426           {
6427             /*
6428               Allocate next image structure.
6429             */
6430             AcquireNextImage(image_info,image,exception);
6431
6432             if (GetNextImageInList(image) == (Image *) NULL)
6433               {
6434                 image=DestroyImageList(image);
6435                 MngInfoFreeStruct(mng_info,&have_mng_structure);
6436                 return((Image *) NULL);
6437               }
6438
6439             image=SyncNextImageInList(image);
6440           }
6441         mng_info->image=image;
6442         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6443           GetBlobSize(image));
6444
6445         if (status == MagickFalse)
6446           break;
6447
6448         if (term_chunk_found)
6449           {
6450             image->start_loop=MagickTrue;
6451             term_chunk_found=MagickFalse;
6452           }
6453
6454         else
6455             image->start_loop=MagickFalse;
6456
6457         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6458           {
6459             image->delay=frame_delay;
6460             frame_delay=default_frame_delay;
6461           }
6462
6463         else
6464           image->delay=0;
6465
6466         image->page.width=mng_info->mng_width;
6467         image->page.height=mng_info->mng_height;
6468         image->page.x=mng_info->x_off[object_id];
6469         image->page.y=mng_info->y_off[object_id];
6470         image->iterations=mng_iterations;
6471
6472         /*
6473           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6474         */
6475
6476         if (logging != MagickFalse)
6477           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6478             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6479             type[2],type[3]);
6480
6481         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6482
6483         if (offset < 0)
6484           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6485       }
6486
6487     previous=image;
6488     mng_info->image=image;
6489     mng_info->mng_type=mng_type;
6490     mng_info->object_id=object_id;
6491
6492     if (memcmp(type,mng_IHDR,4) == 0)
6493       image=ReadOnePNGImage(mng_info,image_info,exception);
6494
6495 #if defined(JNG_SUPPORTED)
6496     else
6497       image=ReadOneJNGImage(mng_info,image_info,exception);
6498 #endif
6499
6500     if (image == (Image *) NULL)
6501       {
6502         if (IsImageObject(previous) != MagickFalse)
6503           {
6504             (void) DestroyImageList(previous);
6505             (void) CloseBlob(previous);
6506           }
6507
6508         MngInfoFreeStruct(mng_info,&have_mng_structure);
6509         return((Image *) NULL);
6510       }
6511
6512     if (image->columns == 0 || image->rows == 0)
6513       {
6514         (void) CloseBlob(image);
6515         image=DestroyImageList(image);
6516         MngInfoFreeStruct(mng_info,&have_mng_structure);
6517         return((Image *) NULL);
6518       }
6519
6520     mng_info->image=image;
6521
6522     if (mng_type)
6523       {
6524         MngBox
6525           crop_box;
6526
6527         if (mng_info->magn_methx || mng_info->magn_methy)
6528           {
6529             png_uint_32
6530                magnified_height,
6531                magnified_width;
6532
6533             if (logging != MagickFalse)
6534               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6535                 "  Processing MNG MAGN chunk");
6536
6537             if (mng_info->magn_methx == 1)
6538               {
6539                 magnified_width=mng_info->magn_ml;
6540
6541                 if (image->columns > 1)
6542                    magnified_width += mng_info->magn_mr;
6543
6544                 if (image->columns > 2)
6545                    magnified_width += (png_uint_32)
6546                       ((image->columns-2)*(mng_info->magn_mx));
6547               }
6548
6549             else
6550               {
6551                 magnified_width=(png_uint_32) image->columns;
6552
6553                 if (image->columns > 1)
6554                    magnified_width += mng_info->magn_ml-1;
6555
6556                 if (image->columns > 2)
6557                    magnified_width += mng_info->magn_mr-1;
6558
6559                 if (image->columns > 3)
6560                    magnified_width += (png_uint_32)
6561                       ((image->columns-3)*(mng_info->magn_mx-1));
6562               }
6563
6564             if (mng_info->magn_methy == 1)
6565               {
6566                 magnified_height=mng_info->magn_mt;
6567
6568                 if (image->rows > 1)
6569                    magnified_height += mng_info->magn_mb;
6570
6571                 if (image->rows > 2)
6572                    magnified_height += (png_uint_32)
6573                       ((image->rows-2)*(mng_info->magn_my));
6574               }
6575
6576             else
6577               {
6578                 magnified_height=(png_uint_32) image->rows;
6579
6580                 if (image->rows > 1)
6581                    magnified_height += mng_info->magn_mt-1;
6582
6583                 if (image->rows > 2)
6584                    magnified_height += mng_info->magn_mb-1;
6585
6586                 if (image->rows > 3)
6587                    magnified_height += (png_uint_32)
6588                       ((image->rows-3)*(mng_info->magn_my-1));
6589               }
6590
6591             if (magnified_height > image->rows ||
6592                 magnified_width > image->columns)
6593               {
6594                 Image
6595                   *large_image;
6596
6597                 int
6598                   yy;
6599
6600                 Quantum
6601                   *next,
6602                   *prev;
6603
6604                 png_uint_16
6605                   magn_methx,
6606                   magn_methy;
6607
6608                 ssize_t
6609                   m,
6610                   y;
6611
6612                 register Quantum
6613                   *n,
6614                   *q;
6615
6616                 register ssize_t
6617                   x;
6618
6619                 /* Allocate next image structure.  */
6620
6621                 if (logging != MagickFalse)
6622                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6623                     "    Allocate magnified image");
6624
6625                 AcquireNextImage(image_info,image,exception);
6626
6627                 if (GetNextImageInList(image) == (Image *) NULL)
6628                   {
6629                     image=DestroyImageList(image);
6630                     MngInfoFreeStruct(mng_info,&have_mng_structure);
6631                     return((Image *) NULL);
6632                   }
6633
6634                 large_image=SyncNextImageInList(image);
6635
6636                 large_image->columns=magnified_width;
6637                 large_image->rows=magnified_height;
6638
6639                 magn_methx=mng_info->magn_methx;
6640                 magn_methy=mng_info->magn_methy;
6641
6642 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6643 #define QM unsigned short
6644                 if (magn_methx != 1 || magn_methy != 1)
6645                   {
6646                   /*
6647                      Scale pixels to unsigned shorts to prevent
6648                      overflow of intermediate values of interpolations
6649                   */
6650                      for (y=0; y < (ssize_t) image->rows; y++)
6651                      {
6652                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6653                           exception);
6654
6655                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6656                        {
6657                           SetPixelRed(image,ScaleQuantumToShort(
6658                             GetPixelRed(image,q)),q);
6659                           SetPixelGreen(image,ScaleQuantumToShort(
6660                             GetPixelGreen(image,q)),q);
6661                           SetPixelBlue(image,ScaleQuantumToShort(
6662                             GetPixelBlue(image,q)),q);
6663                           SetPixelAlpha(image,ScaleQuantumToShort(
6664                             GetPixelAlpha(image,q)),q);
6665                           q+=GetPixelChannels(image);
6666                        }
6667
6668                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6669                          break;
6670                      }
6671                   }
6672 #else
6673 #define QM Quantum
6674 #endif
6675
6676                 if (image->alpha_trait == BlendPixelTrait)
6677                    (void) SetImageBackgroundColor(large_image,exception);
6678
6679                 else
6680                   {
6681                     large_image->background_color.alpha=OpaqueAlpha;
6682                     (void) SetImageBackgroundColor(large_image,exception);
6683
6684                     if (magn_methx == 4)
6685                       magn_methx=2;
6686
6687                     if (magn_methx == 5)
6688                       magn_methx=3;
6689
6690                     if (magn_methy == 4)
6691                       magn_methy=2;
6692
6693                     if (magn_methy == 5)
6694                       magn_methy=3;
6695                   }
6696
6697                 /* magnify the rows into the right side of the large image */
6698
6699                 if (logging != MagickFalse)
6700                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6701                     "    Magnify the rows to %.20g",(double) large_image->rows);
6702                 m=(ssize_t) mng_info->magn_mt;
6703                 yy=0;
6704                 length=(size_t) image->columns*GetPixelChannels(image);
6705                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6706                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6707
6708                 if ((prev == (Quantum *) NULL) ||
6709                     (next == (Quantum *) NULL))
6710                   {
6711                      image=DestroyImageList(image);
6712                      MngInfoFreeStruct(mng_info,&have_mng_structure);
6713                      ThrowReaderException(ResourceLimitError,
6714                        "MemoryAllocationFailed");
6715                   }
6716
6717                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6718                 (void) CopyMagickMemory(next,n,length);
6719
6720                 for (y=0; y < (ssize_t) image->rows; y++)
6721                 {
6722                   if (y == 0)
6723                     m=(ssize_t) mng_info->magn_mt;
6724
6725                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6726                     m=(ssize_t) mng_info->magn_mb;
6727
6728                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6729                     m=(ssize_t) mng_info->magn_mb;
6730
6731                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6732                     m=1;
6733
6734                   else
6735                     m=(ssize_t) mng_info->magn_my;
6736
6737                   n=prev;
6738                   prev=next;
6739                   next=n;
6740
6741                   if (y < (ssize_t) image->rows-1)
6742                     {
6743                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6744                           exception);
6745                       (void) CopyMagickMemory(next,n,length);
6746                     }
6747
6748                   for (i=0; i < m; i++, yy++)
6749                   {
6750                     register Quantum
6751                       *pixels;
6752
6753                     assert(yy < (ssize_t) large_image->rows);
6754                     pixels=prev;
6755                     n=next;
6756                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6757                       1,exception);
6758                     q+=(large_image->columns-image->columns)*
6759                       GetPixelChannels(large_image);
6760
6761                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
6762                     {
6763                       /* To do: get color as function of indexes[x] */
6764                       /*
6765                       if (image->storage_class == PseudoClass)
6766                         {
6767                         }
6768                       */
6769
6770                       if (magn_methy <= 1)
6771                         {
6772                           /* replicate previous */
6773                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6774                           SetPixelGreen(large_image,GetPixelGreen(image,
6775                              pixels),q);
6776                           SetPixelBlue(large_image,GetPixelBlue(image,
6777                              pixels),q);
6778                           SetPixelAlpha(large_image,GetPixelAlpha(image,
6779                              pixels),q);
6780                         }
6781
6782                       else if (magn_methy == 2 || magn_methy == 4)
6783                         {
6784                           if (i == 0)
6785                             {
6786                               SetPixelRed(large_image,GetPixelRed(image,
6787                                  pixels),q);
6788                               SetPixelGreen(large_image,GetPixelGreen(image,
6789                                  pixels),q);
6790                               SetPixelBlue(large_image,GetPixelBlue(image,
6791                                  pixels),q);
6792                               SetPixelAlpha(large_image,GetPixelAlpha(image,
6793                                  pixels),q);
6794                             }
6795
6796                           else
6797                             {
6798                               /* Interpolate */
6799                               SetPixelRed(large_image,((QM) (((ssize_t)
6800                                  (2*i*(GetPixelRed(image,n)
6801                                  -GetPixelRed(image,pixels)+m))/
6802                                  ((ssize_t) (m*2))
6803                                  +GetPixelRed(image,pixels)))),q);
6804                               SetPixelGreen(large_image,((QM) (((ssize_t)
6805                                  (2*i*(GetPixelGreen(image,n)
6806                                  -GetPixelGreen(image,pixels)+m))/
6807                                  ((ssize_t) (m*2))
6808                                  +GetPixelGreen(image,pixels)))),q);
6809                               SetPixelBlue(large_image,((QM) (((ssize_t)
6810                                  (2*i*(GetPixelBlue(image,n)
6811                                  -GetPixelBlue(image,pixels)+m))/
6812                                  ((ssize_t) (m*2))
6813                                  +GetPixelBlue(image,pixels)))),q);
6814
6815                               if (image->alpha_trait == BlendPixelTrait)
6816                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
6817                                     (2*i*(GetPixelAlpha(image,n)
6818                                     -GetPixelAlpha(image,pixels)+m))
6819                                     /((ssize_t) (m*2))+
6820                                    GetPixelAlpha(image,pixels)))),q);
6821                             }
6822
6823                           if (magn_methy == 4)
6824                             {
6825                               /* Replicate nearest */
6826                               if (i <= ((m+1) << 1))
6827                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6828                                     pixels),q);
6829                               else
6830                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6831                                     n),q);
6832                             }
6833                         }
6834
6835                       else /* if (magn_methy == 3 || magn_methy == 5) */
6836                         {
6837                           /* Replicate nearest */
6838                           if (i <= ((m+1) << 1))
6839                           {
6840                              SetPixelRed(large_image,GetPixelRed(image,
6841                                     pixels),q);
6842                              SetPixelGreen(large_image,GetPixelGreen(image,
6843                                     pixels),q);
6844                              SetPixelBlue(large_image,GetPixelBlue(image,
6845                                     pixels),q);
6846                              SetPixelAlpha(large_image,GetPixelAlpha(image,
6847                                     pixels),q);
6848                           }
6849
6850                           else
6851                           {
6852                              SetPixelRed(large_image,GetPixelRed(image,n),q);
6853                              SetPixelGreen(large_image,GetPixelGreen(image,n),
6854                                     q);
6855                              SetPixelBlue(large_image,GetPixelBlue(image,n),
6856                                     q);
6857                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6858                                     q);
6859                           }
6860
6861                           if (magn_methy == 5)
6862                             {
6863                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6864                                  (GetPixelAlpha(image,n)
6865                                  -GetPixelAlpha(image,pixels))
6866                                  +m))/((ssize_t) (m*2))
6867                                  +GetPixelAlpha(image,pixels)),q);
6868                             }
6869                         }
6870                       n+=GetPixelChannels(image);
6871                       q+=GetPixelChannels(large_image);
6872                       pixels+=GetPixelChannels(image);
6873                     } /* x */
6874
6875                     if (SyncAuthenticPixels(large_image,exception) == 0)
6876                       break;
6877
6878                   } /* i */
6879                 } /* y */
6880
6881                 prev=(Quantum *) RelinquishMagickMemory(prev);
6882                 next=(Quantum *) RelinquishMagickMemory(next);
6883
6884                 length=image->columns;
6885
6886                 if (logging != MagickFalse)
6887                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6888                     "    Delete original image");
6889
6890                 DeleteImageFromList(&image);
6891
6892                 image=large_image;
6893
6894                 mng_info->image=image;
6895
6896                 /* magnify the columns */
6897                 if (logging != MagickFalse)
6898                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6899                     "    Magnify the columns to %.20g",(double) image->columns);
6900
6901                 for (y=0; y < (ssize_t) image->rows; y++)
6902                 {
6903                   register Quantum
6904                     *pixels;
6905
6906                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6907                   pixels=q+(image->columns-length)*GetPixelChannels(image);
6908                   n=pixels+GetPixelChannels(image);
6909
6910                   for (x=(ssize_t) (image->columns-length);
6911                     x < (ssize_t) image->columns; x++)
6912                   {
6913                     /* To do: Rewrite using Get/Set***PixelChannel() */
6914
6915                     if (x == (ssize_t) (image->columns-length))
6916                       m=(ssize_t) mng_info->magn_ml;
6917
6918                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6919                       m=(ssize_t) mng_info->magn_mr;
6920
6921                     else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6922                       m=(ssize_t) mng_info->magn_mr;
6923
6924                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6925                       m=1;
6926
6927                     else
6928                       m=(ssize_t) mng_info->magn_mx;
6929
6930                     for (i=0; i < m; i++)
6931                     {
6932                       if (magn_methx <= 1)
6933                         {
6934                           /* replicate previous */
6935                           SetPixelRed(image,GetPixelRed(image,pixels),q);
6936                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6937                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6938                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6939                         }
6940
6941                       else if (magn_methx == 2 || magn_methx == 4)
6942                         {
6943                           if (i == 0)
6944                           {
6945                             SetPixelRed(image,GetPixelRed(image,pixels),q);
6946                             SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6947                             SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6948                             SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6949                           }
6950
6951                           /* To do: Rewrite using Get/Set***PixelChannel() */
6952                           else
6953                             {
6954                               /* Interpolate */
6955                               SetPixelRed(image,(QM) ((2*i*(
6956                                  GetPixelRed(image,n)
6957                                  -GetPixelRed(image,pixels))+m)
6958                                  /((ssize_t) (m*2))+
6959                                  GetPixelRed(image,pixels)),q);
6960
6961                               SetPixelGreen(image,(QM) ((2*i*(
6962                                  GetPixelGreen(image,n)
6963                                  -GetPixelGreen(image,pixels))+m)
6964                                  /((ssize_t) (m*2))+
6965                                  GetPixelGreen(image,pixels)),q);
6966
6967                               SetPixelBlue(image,(QM) ((2*i*(
6968                                  GetPixelBlue(image,n)
6969                                  -GetPixelBlue(image,pixels))+m)
6970                                  /((ssize_t) (m*2))+
6971                                  GetPixelBlue(image,pixels)),q);
6972                               if (image->alpha_trait == BlendPixelTrait)
6973                                  SetPixelAlpha(image,(QM) ((2*i*(
6974                                    GetPixelAlpha(image,n)
6975                                    -GetPixelAlpha(image,pixels))+m)
6976                                    /((ssize_t) (m*2))+
6977                                    GetPixelAlpha(image,pixels)),q);
6978                             }
6979
6980                           if (magn_methx == 4)
6981                             {
6982                               /* Replicate nearest */
6983                               if (i <= ((m+1) << 1))
6984                               {
6985                                  SetPixelAlpha(image,
6986                                    GetPixelAlpha(image,pixels)+0,q);
6987                               }
6988                               else
6989                               {
6990                                  SetPixelAlpha(image,
6991                                    GetPixelAlpha(image,n)+0,q);
6992                               }
6993                             }
6994                         }
6995
6996                       else /* if (magn_methx == 3 || magn_methx == 5) */
6997                         {
6998                           /* Replicate nearest */
6999                           if (i <= ((m+1) << 1))
7000                           {
7001                              SetPixelRed(image,GetPixelRed(image,pixels),q);
7002                              SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7003                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7004                              SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7005                           }
7006
7007                           else
7008                           {
7009                              SetPixelRed(image,GetPixelRed(image,n),q);
7010                              SetPixelGreen(image,GetPixelGreen(image,n),q);
7011                              SetPixelBlue(image,GetPixelBlue(image,n),q);
7012                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7013                           }
7014
7015                           if (magn_methx == 5)
7016                             {
7017                               /* Interpolate */
7018                               SetPixelAlpha(image,
7019                                  (QM) ((2*i*( GetPixelAlpha(image,n)
7020                                  -GetPixelAlpha(image,pixels))+m)/
7021                                  ((ssize_t) (m*2))
7022                                  +GetPixelAlpha(image,pixels)),q);
7023                             }
7024                         }
7025                       q+=GetPixelChannels(image);
7026                     }
7027                     n+=GetPixelChannels(image);
7028                   }
7029
7030                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
7031                     break;
7032                 }
7033 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7034               if (magn_methx != 1 || magn_methy != 1)
7035                 {
7036                 /*
7037                    Rescale pixels to Quantum
7038                 */
7039                    for (y=0; y < (ssize_t) image->rows; y++)
7040                    {
7041                      q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7042
7043                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
7044                      {
7045                         SetPixelRed(image,ScaleShortToQuantum(
7046                           GetPixelRed(image,q)),q);
7047                         SetPixelGreen(image,ScaleShortToQuantum(
7048                           GetPixelGreen(image,q)),q);
7049                         SetPixelBlue(image,ScaleShortToQuantum(
7050                           GetPixelBlue(image,q)),q);
7051                         SetPixelAlpha(image,ScaleShortToQuantum(
7052                           GetPixelAlpha(image,q)),q);
7053                         q+=GetPixelChannels(image);
7054                      }
7055
7056                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
7057                        break;
7058                    }
7059                 }
7060 #endif
7061                 if (logging != MagickFalse)
7062                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7063                     "  Finished MAGN processing");
7064               }
7065           }
7066
7067         /*
7068           Crop_box is with respect to the upper left corner of the MNG.
7069         */
7070         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7071         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7072         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7073         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7074         crop_box=mng_minimum_box(crop_box,mng_info->clip);
7075         crop_box=mng_minimum_box(crop_box,mng_info->frame);
7076         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7077         if ((crop_box.left != (mng_info->image_box.left
7078             +mng_info->x_off[object_id])) ||
7079             (crop_box.right != (mng_info->image_box.right
7080             +mng_info->x_off[object_id])) ||
7081             (crop_box.top != (mng_info->image_box.top
7082             +mng_info->y_off[object_id])) ||
7083             (crop_box.bottom != (mng_info->image_box.bottom
7084             +mng_info->y_off[object_id])))
7085           {
7086             if (logging != MagickFalse)
7087               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7088                 "  Crop the PNG image");
7089
7090             if ((crop_box.left < crop_box.right) &&
7091                 (crop_box.top < crop_box.bottom))
7092               {
7093                 Image
7094                   *im;
7095
7096                 RectangleInfo
7097                   crop_info;
7098
7099                 /*
7100                   Crop_info is with respect to the upper left corner of
7101                   the image.
7102                 */
7103                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7104                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7105                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7106                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7107                 image->page.width=image->columns;
7108                 image->page.height=image->rows;
7109                 image->page.x=0;
7110                 image->page.y=0;
7111                 im=CropImage(image,&crop_info,exception);
7112
7113                 if (im != (Image *) NULL)
7114                   {
7115                     image->columns=im->columns;
7116                     image->rows=im->rows;
7117                     im=DestroyImage(im);
7118                     image->page.width=image->columns;
7119                     image->page.height=image->rows;
7120                     image->page.x=crop_box.left;
7121                     image->page.y=crop_box.top;
7122                   }
7123               }
7124
7125             else
7126               {
7127                 /*
7128                   No pixels in crop area.  The MNG spec still requires
7129                   a layer, though, so make a single transparent pixel in
7130                   the top left corner.
7131                 */
7132                 image->columns=1;
7133                 image->rows=1;
7134                 image->colors=2;
7135                 (void) SetImageBackgroundColor(image,exception);
7136                 image->page.width=1;
7137                 image->page.height=1;
7138                 image->page.x=0;
7139                 image->page.y=0;
7140               }
7141           }
7142 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7143         image=mng_info->image;
7144 #endif
7145       }
7146
7147 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7148       /* PNG does not handle depths greater than 16 so reduce it even
7149        * if lossy.
7150        */
7151       if (image->depth > 16)
7152          image->depth=16;
7153 #endif
7154
7155 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7156       if (image->depth > 8)
7157         {
7158           /* To do: fill low byte properly */
7159           image->depth=16;
7160         }
7161
7162       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7163          image->depth = 8;
7164 #endif
7165
7166       if (image_info->number_scenes != 0)
7167         {
7168           if (mng_info->scenes_found >
7169              (ssize_t) (image_info->first_scene+image_info->number_scenes))
7170             break;
7171         }
7172
7173       if (logging != MagickFalse)
7174         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7175           "  Finished reading image datastream.");
7176
7177   } while (LocaleCompare(image_info->magick,"MNG") == 0);
7178
7179   (void) CloseBlob(image);
7180
7181   if (logging != MagickFalse)
7182     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7183       "  Finished reading all image datastreams.");
7184
7185 #if defined(MNG_INSERT_LAYERS)
7186   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7187        (mng_info->mng_height))
7188     {
7189       /*
7190         Insert a background layer if nothing else was found.
7191       */
7192       if (logging != MagickFalse)
7193         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7194           "  No images found.  Inserting a background layer.");
7195
7196       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7197         {
7198           /*
7199             Allocate next image structure.
7200           */
7201           AcquireNextImage(image_info,image,exception);
7202           if (GetNextImageInList(image) == (Image *) NULL)
7203             {
7204               image=DestroyImageList(image);
7205               MngInfoFreeStruct(mng_info,&have_mng_structure);
7206
7207               if (logging != MagickFalse)
7208                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7209                   "  Allocation failed, returning NULL.");
7210
7211               return((Image *) NULL);
7212             }
7213           image=SyncNextImageInList(image);
7214         }
7215       image->columns=mng_info->mng_width;
7216       image->rows=mng_info->mng_height;
7217       image->page.width=mng_info->mng_width;
7218       image->page.height=mng_info->mng_height;
7219       image->page.x=0;
7220       image->page.y=0;
7221       image->background_color=mng_background_color;
7222       image->alpha_trait=UndefinedPixelTrait;
7223
7224       if (image_info->ping == MagickFalse)
7225         (void) SetImageBackgroundColor(image,exception);
7226
7227       mng_info->image_found++;
7228     }
7229 #endif
7230   image->iterations=mng_iterations;
7231
7232   if (mng_iterations == 1)
7233     image->start_loop=MagickTrue;
7234
7235   while (GetPreviousImageInList(image) != (Image *) NULL)
7236   {
7237     image_count++;
7238     if (image_count > 10*mng_info->image_found)
7239       {
7240         if (logging != MagickFalse)
7241           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7242
7243         (void) ThrowMagickException(exception,GetMagickModule(),
7244           CoderError,"Linked list is corrupted, beginning of list not found",
7245           "`%s'",image_info->filename);
7246
7247         return((Image *) NULL);
7248       }
7249
7250     image=GetPreviousImageInList(image);
7251
7252     if (GetNextImageInList(image) == (Image *) NULL)
7253       {
7254         if (logging != MagickFalse)
7255           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7256
7257         (void) ThrowMagickException(exception,GetMagickModule(),
7258           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7259           image_info->filename);
7260       }
7261   }
7262
7263   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7264              GetNextImageInList(image) ==
7265      (Image *) NULL)
7266     {
7267       if (logging != MagickFalse)
7268         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7269             "  First image null");
7270
7271       (void) ThrowMagickException(exception,GetMagickModule(),
7272         CoderError,"image->next for first image is NULL but shouldn't be.",
7273         "`%s'",image_info->filename);
7274     }
7275
7276   if (mng_info->image_found == 0)
7277     {
7278       if (logging != MagickFalse)
7279         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7280           "  No visible images found.");
7281
7282       (void) ThrowMagickException(exception,GetMagickModule(),
7283         CoderError,"No visible images in file","`%s'",image_info->filename);
7284
7285       if (image != (Image *) NULL)
7286         image=DestroyImageList(image);
7287
7288       MngInfoFreeStruct(mng_info,&have_mng_structure);
7289       return((Image *) NULL);
7290     }
7291
7292   if (mng_info->ticks_per_second)
7293     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7294             final_delay/mng_info->ticks_per_second;
7295
7296   else
7297     image->start_loop=MagickTrue;
7298
7299   /* Find final nonzero image delay */
7300   final_image_delay=0;
7301
7302   while (GetNextImageInList(image) != (Image *) NULL)
7303     {
7304       if (image->delay)
7305         final_image_delay=image->delay;
7306
7307       image=GetNextImageInList(image);
7308     }
7309
7310   if (final_delay < final_image_delay)
7311     final_delay=final_image_delay;
7312
7313   image->delay=final_delay;
7314
7315   if (logging != MagickFalse)
7316       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7317         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7318         (double) final_delay);
7319
7320   if (logging != MagickFalse)
7321     {
7322       int
7323         scene;
7324
7325       scene=0;
7326       image=GetFirstImageInList(image);
7327
7328       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7329         "  Before coalesce:");
7330
7331       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7332         "    scene 0 delay=%.20g",(double) image->delay);
7333
7334       while (GetNextImageInList(image) != (Image *) NULL)
7335       {
7336         image=GetNextImageInList(image);
7337         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7338           "    scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7339       }
7340     }
7341
7342   image=GetFirstImageInList(image);
7343 #ifdef MNG_COALESCE_LAYERS
7344   if (insert_layers)
7345     {
7346       Image
7347         *next_image,
7348         *next;
7349
7350       size_t
7351         scene;
7352
7353       if (logging != MagickFalse)
7354         (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Coalesce Images");
7355
7356       scene=image->scene;
7357       next_image=CoalesceImages(image,exception);
7358
7359       if (next_image == (Image *) NULL)
7360         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7361
7362       image=DestroyImageList(image);
7363       image=next_image;
7364
7365       for (next=image; next != (Image *) NULL; next=next_image)
7366       {
7367          next->page.width=mng_info->mng_width;
7368          next->page.height=mng_info->mng_height;
7369          next->page.x=0;
7370          next->page.y=0;
7371          next->scene=scene++;
7372          next_image=GetNextImageInList(next);
7373
7374          if (next_image == (Image *) NULL)
7375            break;
7376
7377          if (next->delay == 0)
7378            {
7379              scene--;
7380              next_image->previous=GetPreviousImageInList(next);
7381              if (GetPreviousImageInList(next) == (Image *) NULL)
7382                image=next_image;
7383              else
7384                next->previous->next=next_image;
7385              next=DestroyImage(next);
7386            }
7387       }
7388     }
7389 #endif
7390
7391   while (GetNextImageInList(image) != (Image *) NULL)
7392       image=GetNextImageInList(image);
7393
7394   image->dispose=BackgroundDispose;
7395
7396   if (logging != MagickFalse)
7397     {
7398       int
7399         scene;
7400
7401       scene=0;
7402       image=GetFirstImageInList(image);
7403
7404       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7405         "  After coalesce:");
7406
7407       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7408         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7409         (double) image->dispose);
7410
7411       while (GetNextImageInList(image) != (Image *) NULL)
7412       {
7413         image=GetNextImageInList(image);
7414
7415         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7416           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7417           (double) image->delay,(double) image->dispose);
7418       }
7419    }
7420
7421   image=GetFirstImageInList(image);
7422   MngInfoFreeStruct(mng_info,&have_mng_structure);
7423   have_mng_structure=MagickFalse;
7424
7425   if (logging != MagickFalse)
7426     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7427
7428   return(GetFirstImageInList(image));
7429 }
7430 #else /* PNG_LIBPNG_VER > 10011 */
7431 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7432 {
7433   printf("Your PNG library is too old: You have libpng-%s\n",
7434      PNG_LIBPNG_VER_STRING);
7435
7436   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7437     "PNG library is too old","`%s'",image_info->filename);
7438
7439   return(Image *) NULL;
7440 }
7441
7442 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7443 {
7444   return(ReadPNGImage(image_info,exception));
7445 }
7446 #endif /* PNG_LIBPNG_VER > 10011 */
7447 #endif
7448 \f
7449 /*
7450 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7451 %                                                                             %
7452 %                                                                             %
7453 %                                                                             %
7454 %   R e g i s t e r P N G I m a g e                                           %
7455 %                                                                             %
7456 %                                                                             %
7457 %                                                                             %
7458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7459 %
7460 %  RegisterPNGImage() adds properties for the PNG image format to
7461 %  the list of supported formats.  The properties include the image format
7462 %  tag, a method to read and/or write the format, whether the format
7463 %  supports the saving of more than one frame to the same file or blob,
7464 %  whether the format supports native in-memory I/O, and a brief
7465 %  description of the format.
7466 %
7467 %  The format of the RegisterPNGImage method is:
7468 %
7469 %      size_t RegisterPNGImage(void)
7470 %
7471 */
7472 ModuleExport size_t RegisterPNGImage(void)
7473 {
7474   char
7475     version[MaxTextExtent];
7476
7477   MagickInfo
7478     *entry;
7479
7480   static const char
7481     *PNGNote=
7482     {
7483       "See http://www.libpng.org/ for details about the PNG format."
7484     },
7485
7486     *JNGNote=
7487     {
7488       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7489       "format."
7490     },
7491
7492     *MNGNote=
7493     {
7494       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7495       "format."
7496     };
7497
7498   *version='\0';
7499
7500 #if defined(PNG_LIBPNG_VER_STRING)
7501   (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7502   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7503
7504   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7505     {
7506       (void) ConcatenateMagickString(version,",",MaxTextExtent);
7507       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7508             MaxTextExtent);
7509     }
7510 #endif
7511
7512   entry=SetMagickInfo("MNG");
7513   entry->seekable_stream=MagickTrue;  /* To do: eliminate this. */
7514
7515 #if defined(MAGICKCORE_PNG_DELEGATE)
7516   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7517   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7518 #endif
7519
7520   entry->magick=(IsImageFormatHandler *) IsMNG;
7521   entry->description=ConstantString("Multiple-image Network Graphics");
7522
7523   if (*version != '\0')
7524     entry->version=ConstantString(version);
7525
7526   entry->mime_type=ConstantString("video/x-mng");
7527   entry->module=ConstantString("PNG");
7528   entry->note=ConstantString(MNGNote);
7529   (void) RegisterMagickInfo(entry);
7530
7531   entry=SetMagickInfo("PNG");
7532
7533 #if defined(MAGICKCORE_PNG_DELEGATE)
7534   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7535   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7536 #endif
7537
7538   entry->magick=(IsImageFormatHandler *) IsPNG;
7539   entry->adjoin=MagickFalse;
7540   entry->description=ConstantString("Portable Network Graphics");
7541   entry->mime_type=ConstantString("image/png");
7542   entry->module=ConstantString("PNG");
7543
7544   if (*version != '\0')
7545     entry->version=ConstantString(version);
7546
7547   entry->note=ConstantString(PNGNote);
7548   (void) RegisterMagickInfo(entry);
7549
7550   entry=SetMagickInfo("PNG8");
7551
7552 #if defined(MAGICKCORE_PNG_DELEGATE)
7553   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7554   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7555 #endif
7556
7557   entry->magick=(IsImageFormatHandler *) IsPNG;
7558   entry->adjoin=MagickFalse;
7559   entry->description=ConstantString(
7560             "8-bit indexed with optional binary transparency");
7561   entry->mime_type=ConstantString("image/png");
7562   entry->module=ConstantString("PNG");
7563   (void) RegisterMagickInfo(entry);
7564
7565   entry=SetMagickInfo("PNG24");
7566   *version='\0';
7567
7568 #if defined(ZLIB_VERSION)
7569   (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7570   (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7571
7572   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7573     {
7574       (void) ConcatenateMagickString(version,",",MaxTextExtent);
7575       (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7576     }
7577 #endif
7578
7579   if (*version != '\0')
7580     entry->version=ConstantString(version);
7581
7582 #if defined(MAGICKCORE_PNG_DELEGATE)
7583   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7584   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7585 #endif
7586
7587   entry->magick=(IsImageFormatHandler *) IsPNG;
7588   entry->adjoin=MagickFalse;
7589   entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
7590   entry->mime_type=ConstantString("image/png");
7591   entry->module=ConstantString("PNG");
7592   (void) RegisterMagickInfo(entry);
7593
7594   entry=SetMagickInfo("PNG32");
7595
7596 #if defined(MAGICKCORE_PNG_DELEGATE)
7597   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7598   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7599 #endif
7600
7601   entry->magick=(IsImageFormatHandler *) IsPNG;
7602   entry->adjoin=MagickFalse;
7603   entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7604   entry->mime_type=ConstantString("image/png");
7605   entry->module=ConstantString("PNG");
7606   (void) RegisterMagickInfo(entry);
7607
7608   entry=SetMagickInfo("PNG48");
7609
7610 #if defined(MAGICKCORE_PNG_DELEGATE)
7611   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7612   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7613 #endif
7614
7615   entry->magick=(IsImageFormatHandler *) IsPNG;
7616   entry->adjoin=MagickFalse;
7617   entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7618   entry->mime_type=ConstantString("image/png");
7619   entry->module=ConstantString("PNG");
7620   (void) RegisterMagickInfo(entry);
7621
7622   entry=SetMagickInfo("PNG64");
7623
7624 #if defined(MAGICKCORE_PNG_DELEGATE)
7625   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7626   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7627 #endif
7628
7629   entry->magick=(IsImageFormatHandler *) IsPNG;
7630   entry->adjoin=MagickFalse;
7631   entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7632   entry->mime_type=ConstantString("image/png");
7633   entry->module=ConstantString("PNG");
7634   (void) RegisterMagickInfo(entry);
7635
7636   entry=SetMagickInfo("PNG00");
7637
7638 #if defined(MAGICKCORE_PNG_DELEGATE)
7639   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7640   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7641 #endif
7642
7643   entry->magick=(IsImageFormatHandler *) IsPNG;
7644   entry->adjoin=MagickFalse;
7645   entry->description=ConstantString(
7646     "PNG inheriting bit-depth and color-type from original");
7647   entry->mime_type=ConstantString("image/png");
7648   entry->module=ConstantString("PNG");
7649   (void) RegisterMagickInfo(entry);
7650
7651   entry=SetMagickInfo("JNG");
7652
7653 #if defined(JNG_SUPPORTED)
7654 #if defined(MAGICKCORE_PNG_DELEGATE)
7655   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7656   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7657 #endif
7658 #endif
7659
7660   entry->magick=(IsImageFormatHandler *) IsJNG;
7661   entry->adjoin=MagickFalse;
7662   entry->description=ConstantString("JPEG Network Graphics");
7663   entry->mime_type=ConstantString("image/x-jng");
7664   entry->module=ConstantString("PNG");
7665   entry->note=ConstantString(JNGNote);
7666   (void) RegisterMagickInfo(entry);
7667
7668 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7669   ping_semaphore=AcquireSemaphoreInfo();
7670 #endif
7671
7672   return(MagickImageCoderSignature);
7673 }
7674 \f
7675 /*
7676 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7677 %                                                                             %
7678 %                                                                             %
7679 %                                                                             %
7680 %   U n r e g i s t e r P N G I m a g e                                       %
7681 %                                                                             %
7682 %                                                                             %
7683 %                                                                             %
7684 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7685 %
7686 %  UnregisterPNGImage() removes format registrations made by the
7687 %  PNG module from the list of supported formats.
7688 %
7689 %  The format of the UnregisterPNGImage method is:
7690 %
7691 %      UnregisterPNGImage(void)
7692 %
7693 */
7694 ModuleExport void UnregisterPNGImage(void)
7695 {
7696   (void) UnregisterMagickInfo("MNG");
7697   (void) UnregisterMagickInfo("PNG");
7698   (void) UnregisterMagickInfo("PNG8");
7699   (void) UnregisterMagickInfo("PNG24");
7700   (void) UnregisterMagickInfo("PNG32");
7701   (void) UnregisterMagickInfo("PNG48");
7702   (void) UnregisterMagickInfo("PNG64");
7703   (void) UnregisterMagickInfo("PNG00");
7704   (void) UnregisterMagickInfo("JNG");
7705
7706 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7707   if (ping_semaphore != (SemaphoreInfo *) NULL)
7708     RelinquishSemaphoreInfo(&ping_semaphore);
7709 #endif
7710 }
7711 \f
7712 #if defined(MAGICKCORE_PNG_DELEGATE)
7713 #if PNG_LIBPNG_VER > 10011
7714 /*
7715 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7716 %                                                                             %
7717 %                                                                             %
7718 %                                                                             %
7719 %   W r i t e M N G I m a g e                                                 %
7720 %                                                                             %
7721 %                                                                             %
7722 %                                                                             %
7723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7724 %
7725 %  WriteMNGImage() writes an image in the Portable Network Graphics
7726 %  Group's "Multiple-image Network Graphics" encoded image format.
7727 %
7728 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
7729 %
7730 %  The format of the WriteMNGImage method is:
7731 %
7732 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7733 %        Image *image,ExceptionInfo *exception)
7734 %
7735 %  A description of each parameter follows.
7736 %
7737 %    o image_info: the image info.
7738 %
7739 %    o image:  The image.
7740 %
7741 %    o exception: return any errors or warnings in this structure.
7742 %
7743 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7744 %    "To do" under ReadPNGImage):
7745 %
7746 %    Preserve all unknown and not-yet-handled known chunks found in input
7747 %    PNG file and copy them  into output PNG files according to the PNG
7748 %    copying rules.
7749 %
7750 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
7751 %
7752 %    Improve selection of color type (use indexed-colour or indexed-colour
7753 %    with tRNS when 256 or fewer unique RGBA values are present).
7754 %
7755 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7756 %    This will be complicated if we limit ourselves to generating MNG-LC
7757 %    files.  For now we ignore disposal method 3 and simply overlay the next
7758 %    image on it.
7759 %
7760 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
7761 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
7762 %    [mostly done 15 June 1999 but still need to take care of tRNS]
7763 %
7764 %    Check for identical sRGB and replace with a global sRGB (and remove
7765 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7766 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7767 %    local gAMA/cHRM with local sRGB if appropriate).
7768 %
7769 %    Check for identical sBIT chunks and write global ones.
7770 %
7771 %    Provide option to skip writing the signature tEXt chunks.
7772 %
7773 %    Use signatures to detect identical objects and reuse the first
7774 %    instance of such objects instead of writing duplicate objects.
7775 %
7776 %    Use a smaller-than-32k value of compression window size when
7777 %    appropriate.
7778 %
7779 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
7780 %    ancillary text chunks and save profiles.
7781 %
7782 %    Provide an option to force LC files (to ensure exact framing rate)
7783 %    instead of VLC.
7784 %
7785 %    Provide an option to force VLC files instead of LC, even when offsets
7786 %    are present.  This will involve expanding the embedded images with a
7787 %    transparent region at the top and/or left.
7788 */
7789
7790 static void
7791 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7792    png_info *ping_info, unsigned char *profile_type, unsigned char
7793    *profile_description, unsigned char *profile_data, png_uint_32 length)
7794 {
7795    png_textp
7796      text;
7797
7798    register ssize_t
7799      i;
7800
7801    unsigned char
7802      *sp;
7803
7804    png_charp
7805      dp;
7806
7807    png_uint_32
7808      allocated_length,
7809      description_length;
7810
7811    unsigned char
7812      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7813
7814    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7815       return;
7816
7817    if (image_info->verbose)
7818      {
7819        (void) printf("writing raw profile: type=%s, length=%.20g\n",
7820          (char *) profile_type, (double) length);
7821      }
7822
7823 #if PNG_LIBPNG_VER >= 10400
7824    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7825 #else
7826    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7827 #endif
7828    description_length=(png_uint_32) strlen((const char *) profile_description);
7829    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7830       + description_length);
7831 #if PNG_LIBPNG_VER >= 10400
7832    text[0].text=(png_charp) png_malloc(ping,
7833       (png_alloc_size_t) allocated_length);
7834    text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7835 #else
7836    text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7837    text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7838 #endif
7839    text[0].key[0]='\0';
7840    (void) ConcatenateMagickString(text[0].key,
7841       "Raw profile type ",MaxTextExtent);
7842    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7843    sp=profile_data;
7844    dp=text[0].text;
7845    *dp++='\n';
7846    (void) CopyMagickString(dp,(const char *) profile_description,
7847      allocated_length);
7848    dp+=description_length;
7849    *dp++='\n';
7850    (void) FormatLocaleString(dp,allocated_length-
7851      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7852    dp+=8;
7853
7854    for (i=0; i < (ssize_t) length; i++)
7855    {
7856      if (i%36 == 0)
7857        *dp++='\n';
7858      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7859      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7860    }
7861
7862    *dp++='\n';
7863    *dp='\0';
7864    text[0].text_length=(png_size_t) (dp-text[0].text);
7865    text[0].compression=image_info->compression == NoCompression ||
7866      (image_info->compression == UndefinedCompression &&
7867      text[0].text_length < 128) ? -1 : 0;
7868
7869    if (text[0].text_length <= allocated_length)
7870      png_set_text(ping,ping_info,text,1);
7871
7872    png_free(ping,text[0].text);
7873    png_free(ping,text[0].key);
7874    png_free(ping,text);
7875 }
7876
7877 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7878   const char *string, MagickBooleanType logging)
7879 {
7880   char
7881     *name;
7882
7883   const StringInfo
7884     *profile;
7885
7886   unsigned char
7887     *data;
7888
7889   png_uint_32 length;
7890
7891   ResetImageProfileIterator(image);
7892
7893   for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7894   {
7895     profile=GetImageProfile(image,name);
7896
7897     if (profile != (const StringInfo *) NULL)
7898       {
7899         StringInfo
7900           *ping_profile;
7901
7902         if (LocaleNCompare(name,string,11) == 0)
7903           {
7904             if (logging != MagickFalse)
7905                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7906                    "  Found %s profile",name);
7907
7908             ping_profile=CloneStringInfo(profile);
7909             data=GetStringInfoDatum(ping_profile),
7910             length=(png_uint_32) GetStringInfoLength(ping_profile);
7911             data[4]=data[3];
7912             data[3]=data[2];
7913             data[2]=data[1];
7914             data[1]=data[0];
7915             (void) WriteBlobMSBULong(image,length-5);  /* data length */
7916             (void) WriteBlob(image,length-1,data+1);
7917             (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7918             ping_profile=DestroyStringInfo(ping_profile);
7919           }
7920       }
7921
7922       name=GetNextImageProfile(image);
7923    }
7924
7925    return(MagickTrue);
7926 }
7927
7928
7929 /* Write one PNG image */
7930 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7931   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7932 {
7933   char
7934     im_vers[32],
7935     libpng_runv[32],
7936     libpng_vers[32],
7937     zlib_runv[32],
7938     zlib_vers[32];
7939
7940   Image
7941     *image;
7942
7943   ImageInfo
7944     *image_info;
7945
7946   char
7947     s[2];
7948
7949   const char
7950     *name,
7951     *property,
7952     *value;
7953
7954   const StringInfo
7955     *profile;
7956
7957   int
7958     num_passes,
7959     pass;
7960
7961   png_byte
7962      ping_trans_alpha[256];
7963
7964   png_color
7965      palette[257];
7966
7967   png_color_16
7968     ping_background,
7969     ping_trans_color;
7970
7971   png_info
7972     *ping_info;
7973
7974   png_struct
7975     *ping;
7976
7977   png_uint_32
7978     ping_height,
7979     ping_width;
7980
7981   ssize_t
7982     y;
7983
7984   MagickBooleanType
7985     image_matte,
7986     logging,
7987     matte,
7988
7989     ping_have_blob,
7990     ping_have_cheap_transparency,
7991     ping_have_color,
7992     ping_have_non_bw,
7993     ping_have_PLTE,
7994     ping_have_bKGD,
7995     ping_have_iCCP,
7996     ping_have_pHYs,
7997     ping_have_sRGB,
7998     ping_have_tRNS,
7999
8000     ping_exclude_bKGD,
8001     ping_exclude_cHRM,
8002     ping_exclude_date,
8003     /* ping_exclude_EXIF, */
8004     ping_exclude_gAMA,
8005     ping_exclude_iCCP,
8006     /* ping_exclude_iTXt, */
8007     ping_exclude_oFFs,
8008     ping_exclude_pHYs,
8009     ping_exclude_sRGB,
8010     ping_exclude_tEXt,
8011     /* ping_exclude_tRNS, */
8012     ping_exclude_vpAg,
8013     ping_exclude_zCCP, /* hex-encoded iCCP */
8014     ping_exclude_zTXt,
8015
8016     ping_preserve_colormap,
8017     ping_preserve_iCCP,
8018     ping_need_colortype_warning,
8019
8020     status,
8021     tried_332,
8022     tried_333,
8023     tried_444;
8024
8025   MemoryInfo
8026     *volatile pixel_info;
8027
8028   QuantumInfo
8029     *quantum_info;
8030
8031   PNGErrorInfo
8032     error_info;
8033
8034   register ssize_t
8035     i,
8036     x;
8037
8038   unsigned char
8039     *ping_pixels;
8040
8041   volatile int
8042     image_colors,
8043     ping_bit_depth,
8044     ping_color_type,
8045     ping_interlace_method,
8046     ping_compression_method,
8047     ping_filter_method,
8048     ping_num_trans;
8049
8050   volatile size_t
8051     image_depth,
8052     old_bit_depth;
8053
8054   size_t
8055     quality,
8056     rowbytes,
8057     save_image_depth;
8058
8059   int
8060     j,
8061     number_colors,
8062     number_opaque,
8063     number_semitransparent,
8064     number_transparent,
8065     ping_pHYs_unit_type;
8066
8067   png_uint_32
8068     ping_pHYs_x_resolution,
8069     ping_pHYs_y_resolution;
8070
8071   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8072     "  Enter WriteOnePNGImage()");
8073
8074   image = CloneImage(IMimage,0,0,MagickFalse,exception);
8075   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8076   if (image_info == (ImageInfo *) NULL)
8077      ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8078
8079   /* Define these outside of the following "if logging()" block so they will
8080    * show in debuggers.
8081    */
8082   *im_vers='\0';
8083   (void) ConcatenateMagickString(im_vers,
8084          MagickLibVersionText,MaxTextExtent);
8085   (void) ConcatenateMagickString(im_vers,
8086          MagickLibAddendum,MaxTextExtent);
8087
8088   *libpng_vers='\0';
8089   (void) ConcatenateMagickString(libpng_vers,
8090          PNG_LIBPNG_VER_STRING,32);
8091   *libpng_runv='\0';
8092   (void) ConcatenateMagickString(libpng_runv,
8093          png_get_libpng_ver(NULL),32);
8094
8095   *zlib_vers='\0';
8096   (void) ConcatenateMagickString(zlib_vers,
8097          ZLIB_VERSION,32);
8098   *zlib_runv='\0';
8099   (void) ConcatenateMagickString(zlib_runv,
8100          zlib_version,32);
8101
8102   if (logging)
8103     {
8104        LogMagickEvent(CoderEvent,GetMagickModule(),"    IM version     = %s",
8105            im_vers);
8106        LogMagickEvent(CoderEvent,GetMagickModule(),"    Libpng version = %s",
8107            libpng_vers);
8108        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8109        {
8110        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8111            libpng_runv);
8112        }
8113        LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
8114            zlib_vers);
8115        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8116        {
8117        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8118            zlib_runv);
8119        }
8120     }
8121
8122   /* Initialize some stuff */
8123   ping_bit_depth=0,
8124   ping_color_type=0,
8125   ping_interlace_method=0,
8126   ping_compression_method=0,
8127   ping_filter_method=0,
8128   ping_num_trans = 0;
8129
8130   ping_background.red = 0;
8131   ping_background.green = 0;
8132   ping_background.blue = 0;
8133   ping_background.gray = 0;
8134   ping_background.index = 0;
8135
8136   ping_trans_color.red=0;
8137   ping_trans_color.green=0;
8138   ping_trans_color.blue=0;
8139   ping_trans_color.gray=0;
8140
8141   ping_pHYs_unit_type = 0;
8142   ping_pHYs_x_resolution = 0;
8143   ping_pHYs_y_resolution = 0;
8144
8145   ping_have_blob=MagickFalse;
8146   ping_have_cheap_transparency=MagickFalse;
8147   ping_have_color=MagickTrue;
8148   ping_have_non_bw=MagickTrue;
8149   ping_have_PLTE=MagickFalse;
8150   ping_have_bKGD=MagickFalse;
8151   ping_have_iCCP=MagickFalse;
8152   ping_have_pHYs=MagickFalse;
8153   ping_have_sRGB=MagickFalse;
8154   ping_have_tRNS=MagickFalse;
8155
8156   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8157   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8158   ping_exclude_date=mng_info->ping_exclude_date;
8159   /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8160   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8161   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8162   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8163   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8164   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8165   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8166   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8167   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8168   ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8169   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8170   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8171
8172   ping_preserve_colormap = mng_info->ping_preserve_colormap;
8173   ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8174   ping_need_colortype_warning = MagickFalse;
8175
8176   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8177    * i.e., eliminate the ICC profile and set image->rendering_intent.
8178    * Note that this will not involve any changes to the actual pixels
8179    * but merely passes information to applications that read the resulting
8180    * PNG image.
8181    *
8182    * To do: recognize other variants of the sRGB profile, using the CRC to
8183    * verify all recognized variants including the 7 already known.
8184    *
8185    * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8186    *
8187    * Use something other than image->rendering_intent to record the fact
8188    * that the sRGB profile was found.
8189    *
8190    * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8191    * profile.  Record the Blackpoint Compensation, if any.
8192    */
8193    if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8194    {
8195       char
8196         *name;
8197
8198       const StringInfo
8199         *profile;
8200
8201       ResetImageProfileIterator(image);
8202       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8203       {
8204         profile=GetImageProfile(image,name);
8205
8206         if (profile != (StringInfo *) NULL)
8207           {
8208             if ((LocaleCompare(name,"ICC") == 0) ||
8209                 (LocaleCompare(name,"ICM") == 0))
8210
8211              {
8212                  int
8213                    icheck,
8214                    got_crc=0;
8215
8216
8217                  png_uint_32
8218                    length,
8219                    profile_crc=0;
8220
8221                  unsigned char
8222                    *data;
8223
8224                  length=(png_uint_32) GetStringInfoLength(profile);
8225
8226                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8227                  {
8228                    if (length == sRGB_info[icheck].len)
8229                    {
8230                      if (got_crc == 0)
8231                      {
8232                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8233                          "    Got a %lu-byte ICC profile (potentially sRGB)",
8234                          (unsigned long) length);
8235
8236                        data=GetStringInfoDatum(profile);
8237                        profile_crc=crc32(0,data,length);
8238
8239                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8240                            "      with crc=%8x",(unsigned int) profile_crc);
8241                        got_crc++;
8242                      }
8243
8244                      if (profile_crc == sRGB_info[icheck].crc)
8245                      {
8246                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8247                             "      It is sRGB with rendering intent = %s",
8248                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
8249                              sRGB_info[icheck].intent));
8250                         if (image->rendering_intent==UndefinedIntent)
8251                         {
8252                           image->rendering_intent=
8253                           Magick_RenderingIntent_from_PNG_RenderingIntent(
8254                              sRGB_info[icheck].intent);
8255                         }
8256                         ping_exclude_iCCP = MagickTrue;
8257                         ping_exclude_zCCP = MagickTrue;
8258                         ping_have_sRGB = MagickTrue;
8259                         break;
8260                      }
8261                    }
8262                  }
8263                  if (sRGB_info[icheck].len == 0)
8264                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8265                         "    Got a %lu-byte ICC profile not recognized as sRGB",
8266                         (unsigned long) length);
8267               }
8268           }
8269         name=GetNextImageProfile(image);
8270       }
8271   }
8272
8273   number_opaque = 0;
8274   number_semitransparent = 0;
8275   number_transparent = 0;
8276
8277   if (logging != MagickFalse)
8278     {
8279       if (image->storage_class == UndefinedClass)
8280           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8281           "    storage_class=UndefinedClass");
8282       if (image->storage_class == DirectClass)
8283           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8284           "    storage_class=DirectClass");
8285       if (image->storage_class == PseudoClass)
8286           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8287           "    storage_class=PseudoClass");
8288     }
8289
8290   if (image->storage_class == PseudoClass &&
8291      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8292      mng_info->write_png48 || mng_info->write_png64 ||
8293      (mng_info->write_png_colortype != 1 &&
8294      mng_info->write_png_colortype != 5)))
8295     {
8296       (void) SyncImage(image,exception);
8297       image->storage_class = DirectClass;
8298     }
8299
8300   if (ping_preserve_colormap == MagickFalse)
8301     {
8302       if (image->storage_class != PseudoClass && image->colormap != NULL)
8303         {
8304           /* Free the bogus colormap; it can cause trouble later */
8305            if (logging != MagickFalse)
8306               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8307               "    Freeing bogus colormap");
8308            (void) RelinquishMagickMemory(image->colormap);
8309            image->colormap=NULL;
8310         }
8311     }
8312
8313   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8314     (void) TransformImageColorspace(image,sRGBColorspace,exception);
8315
8316   /*
8317     Sometimes we get PseudoClass images whose RGB values don't match
8318     the colors in the colormap.  This code syncs the RGB values.
8319   */
8320   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8321      (void) SyncImage(image,exception);
8322
8323 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8324   if (image->depth > 8)
8325     {
8326       if (logging != MagickFalse)
8327         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8328           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8329
8330       image->depth=8;
8331     }
8332 #endif
8333
8334   /* Respect the -depth option */
8335   if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
8336     {
8337        register Quantum
8338          *r;
8339
8340        if (image->depth > 8)
8341          {
8342 #if MAGICKCORE_QUANTUM_DEPTH > 16
8343            /* Scale to 16-bit */
8344            LBR16PacketRGBO(image->background_color);
8345
8346            for (y=0; y < (ssize_t) image->rows; y++)
8347            {
8348              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8349
8350              if (r == (Quantum *) NULL)
8351                break;
8352
8353              for (x=0; x < (ssize_t) image->columns; x++)
8354              {
8355                 LBR16PixelRGBA(r);
8356                 r+=GetPixelChannels(image);
8357              }
8358
8359              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8360                 break;
8361            }
8362
8363            if (image->storage_class == PseudoClass && image->colormap != NULL)
8364            {
8365              for (i=0; i < (ssize_t) image->colors; i++)
8366              {
8367                LBR16PacketRGBO(image->colormap[i]);
8368              }
8369            }
8370 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
8371          }
8372
8373        else if (image->depth > 4)
8374          {
8375 #if MAGICKCORE_QUANTUM_DEPTH > 8
8376            /* Scale to 8-bit */
8377            LBR08PacketRGBO(image->background_color);
8378
8379            for (y=0; y < (ssize_t) image->rows; y++)
8380            {
8381              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8382
8383              if (r == (Quantum *) NULL)
8384                break;
8385
8386              for (x=0; x < (ssize_t) image->columns; x++)
8387              {
8388                 LBR08PixelRGBA(r);
8389                 r+=GetPixelChannels(image);
8390              }
8391
8392              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8393                 break;
8394            }
8395
8396            if (image->storage_class == PseudoClass && image->colormap != NULL)
8397            {
8398              for (i=0; i < (ssize_t) image->colors; i++)
8399              {
8400                LBR08PacketRGBO(image->colormap[i]);
8401              }
8402            }
8403 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
8404          }
8405        else
8406          if (image->depth > 2)
8407          {
8408            /* Scale to 4-bit */
8409            LBR04PacketRGBO(image->background_color);
8410
8411            for (y=0; y < (ssize_t) image->rows; y++)
8412            {
8413              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8414
8415              if (r == (Quantum *) NULL)
8416                break;
8417
8418              for (x=0; x < (ssize_t) image->columns; x++)
8419              {
8420                 LBR04PixelRGBA(r);
8421                 r+=GetPixelChannels(image);
8422              }
8423
8424              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8425                 break;
8426            }
8427
8428            if (image->storage_class == PseudoClass && image->colormap != NULL)
8429            {
8430              for (i=0; i < (ssize_t) image->colors; i++)
8431              {
8432                LBR04PacketRGBO(image->colormap[i]);
8433              }
8434            }
8435          }
8436
8437        else if (image->depth > 1)
8438          {
8439            /* Scale to 2-bit */
8440            LBR02PacketRGBO(image->background_color);
8441
8442            for (y=0; y < (ssize_t) image->rows; y++)
8443            {
8444              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8445
8446              if (r == (Quantum *) NULL)
8447                break;
8448
8449              for (x=0; x < (ssize_t) image->columns; x++)
8450              {
8451                 LBR02PixelRGBA(r);
8452                 r+=GetPixelChannels(image);
8453              }
8454
8455              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8456                 break;
8457            }
8458
8459            if (image->storage_class == PseudoClass && image->colormap != NULL)
8460            {
8461              for (i=0; i < (ssize_t) image->colors; i++)
8462              {
8463                LBR02PacketRGBO(image->colormap[i]);
8464              }
8465            }
8466          }
8467        else
8468          {
8469            /* Scale to 1-bit */
8470            LBR01PacketRGBO(image->background_color);
8471
8472            for (y=0; y < (ssize_t) image->rows; y++)
8473            {
8474              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8475
8476              if (r == (Quantum *) NULL)
8477                break;
8478
8479              for (x=0; x < (ssize_t) image->columns; x++)
8480              {
8481                 LBR01PixelRGBA(r);
8482                 r+=GetPixelChannels(image);
8483              }
8484
8485              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8486                 break;
8487            }
8488
8489            if (image->storage_class == PseudoClass && image->colormap != NULL)
8490            {
8491              for (i=0; i < (ssize_t) image->colors; i++)
8492              {
8493                LBR01PacketRGBO(image->colormap[i]);
8494              }
8495            }
8496          }
8497     }
8498
8499   /* To do: set to next higher multiple of 8 */
8500   if (image->depth < 8)
8501      image->depth=8;
8502
8503 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8504   /* PNG does not handle depths greater than 16 so reduce it even
8505    * if lossy
8506    */
8507   if (image->depth > 8)
8508       image->depth=16;
8509 #endif
8510
8511 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8512   if (image->depth > 8)
8513     {
8514       /* To do: fill low byte properly */
8515       image->depth=16;
8516     }
8517
8518   if (image->depth == 16 && mng_info->write_png_depth != 16)
8519     if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8520       image->depth = 8;
8521 #endif
8522
8523   image_colors = (int) image->colors;
8524   number_opaque = (int) image->colors;
8525   number_transparent = 0;
8526   number_semitransparent = 0;
8527
8528   if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
8529      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8530      mng_info->write_png_colortype < 4 &&
8531      image->alpha_trait != BlendPixelTrait)))
8532   {
8533      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8534       * are not going to need the result.
8535       */
8536      if (mng_info->write_png_colortype == 1 ||
8537         mng_info->write_png_colortype == 5)
8538        ping_have_color=MagickFalse;
8539
8540      if (image->alpha_trait == BlendPixelTrait)
8541        {
8542          number_transparent = 2;
8543          number_semitransparent = 1;
8544        }
8545   }
8546
8547   else
8548   {
8549   /* BUILD_PALETTE
8550    *
8551    * Normally we run this just once, but in the case of writing PNG8
8552    * we reduce the transparency to binary and run again, then if there
8553    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8554    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8555    * palette.  Then (To do) we take care of a final reduction that is only
8556    * needed if there are still 256 colors present and one of them has both
8557    * transparent and opaque instances.
8558    */
8559
8560   tried_332 = MagickFalse;
8561   tried_333 = MagickFalse;
8562   tried_444 = MagickFalse;
8563
8564   for (j=0; j<6; j++)
8565   {
8566     /*
8567      * Sometimes we get DirectClass images that have 256 colors or fewer.
8568      * This code will build a colormap.
8569      *
8570      * Also, sometimes we get PseudoClass images with an out-of-date
8571      * colormap.  This code will replace the colormap with a new one.
8572      * Sometimes we get PseudoClass images that have more than 256 colors.
8573      * This code will delete the colormap and change the image to
8574      * DirectClass.
8575      *
8576      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8577      * even though it sometimes contains left-over non-opaque values.
8578      *
8579      * Also we gather some information (number of opaque, transparent,
8580      * and semitransparent pixels, and whether the image has any non-gray
8581      * pixels or only black-and-white pixels) that we might need later.
8582      *
8583      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8584      * we need to check for bogus non-opaque values, at least.
8585      */
8586
8587    int
8588      n;
8589
8590    PixelInfo
8591      opaque[260],
8592      semitransparent[260],
8593      transparent[260];
8594
8595    register const Quantum
8596      *s;
8597
8598    register Quantum
8599      *q,
8600      *r;
8601
8602    if (logging != MagickFalse)
8603      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8604          "    Enter BUILD_PALETTE:");
8605
8606    if (logging != MagickFalse)
8607      {
8608        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8609              "      image->columns=%.20g",(double) image->columns);
8610        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8611              "      image->rows=%.20g",(double) image->rows);
8612        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8613              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
8614        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8615              "      image->depth=%.20g",(double) image->depth);
8616
8617        if (image->storage_class == PseudoClass && image->colormap != NULL)
8618        {
8619          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8620              "      Original colormap:");
8621          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8622              "        i    (red,green,blue,alpha)");
8623
8624          for (i=0; i < 256; i++)
8625          {
8626                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8627                    "        %d    (%d,%d,%d,%d)",
8628                     (int) i,
8629                     (int) image->colormap[i].red,
8630                     (int) image->colormap[i].green,
8631                     (int) image->colormap[i].blue,
8632                     (int) image->colormap[i].alpha);
8633          }
8634
8635          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8636          {
8637            if (i > 255)
8638              {
8639                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8640                    "        %d    (%d,%d,%d,%d)",
8641                     (int) i,
8642                     (int) image->colormap[i].red,
8643                     (int) image->colormap[i].green,
8644                     (int) image->colormap[i].blue,
8645                     (int) image->colormap[i].alpha);
8646              }
8647          }
8648        }
8649
8650        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8651            "      image->colors=%d",(int) image->colors);
8652
8653        if (image->colors == 0)
8654          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8655              "        (zero means unknown)");
8656
8657        if (ping_preserve_colormap == MagickFalse)
8658          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8659               "      Regenerate the colormap");
8660      }
8661
8662      image_colors=0;
8663      number_opaque = 0;
8664      number_semitransparent = 0;
8665      number_transparent = 0;
8666
8667      for (y=0; y < (ssize_t) image->rows; y++)
8668      {
8669        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8670
8671        if (q == (Quantum *) NULL)
8672          break;
8673
8674        for (x=0; x < (ssize_t) image->columns; x++)
8675        {
8676            if (image->alpha_trait != BlendPixelTrait ||
8677               GetPixelAlpha(image,q) == OpaqueAlpha)
8678              {
8679                if (number_opaque < 259)
8680                  {
8681                    if (number_opaque == 0)
8682                      {
8683                        GetPixelInfoPixel(image, q, opaque);
8684                        opaque[0].alpha=OpaqueAlpha;
8685                        number_opaque=1;
8686                      }
8687
8688                    for (i=0; i< (ssize_t) number_opaque; i++)
8689                      {
8690                        if (IsPixelEquivalent(image,q, opaque+i))
8691                          break;
8692                      }
8693
8694                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
8695                      {
8696                        number_opaque++;
8697                        GetPixelInfoPixel(image, q, opaque+i);
8698                        opaque[i].alpha=OpaqueAlpha;
8699                      }
8700                  }
8701              }
8702            else if (GetPixelAlpha(image,q) == TransparentAlpha)
8703              {
8704                if (number_transparent < 259)
8705                  {
8706                    if (number_transparent == 0)
8707                      {
8708                        GetPixelInfoPixel(image, q, transparent);
8709                        ping_trans_color.red=(unsigned short)
8710                          GetPixelRed(image,q);
8711                        ping_trans_color.green=(unsigned short)
8712                          GetPixelGreen(image,q);
8713                        ping_trans_color.blue=(unsigned short)
8714                          GetPixelBlue(image,q);
8715                        ping_trans_color.gray=(unsigned short)
8716                          GetPixelGray(image,q);
8717                        number_transparent = 1;
8718                      }
8719
8720                    for (i=0; i< (ssize_t) number_transparent; i++)
8721                      {
8722                        if (IsPixelEquivalent(image,q, transparent+i))
8723                          break;
8724                      }
8725
8726                    if (i ==  (ssize_t) number_transparent &&
8727                        number_transparent < 259)
8728                      {
8729                        number_transparent++;
8730                        GetPixelInfoPixel(image,q,transparent+i);
8731                      }
8732                  }
8733              }
8734            else
8735              {
8736                if (number_semitransparent < 259)
8737                  {
8738                    if (number_semitransparent == 0)
8739                      {
8740                        GetPixelInfoPixel(image,q,semitransparent);
8741                        number_semitransparent = 1;
8742                      }
8743
8744                    for (i=0; i< (ssize_t) number_semitransparent; i++)
8745                      {
8746                        if (IsPixelEquivalent(image,q, semitransparent+i)
8747                            && GetPixelAlpha(image,q) ==
8748                            semitransparent[i].alpha)
8749                          break;
8750                      }
8751
8752                    if (i ==  (ssize_t) number_semitransparent &&
8753                        number_semitransparent < 259)
8754                      {
8755                        number_semitransparent++;
8756                        GetPixelInfoPixel(image, q, semitransparent+i);
8757                      }
8758                  }
8759              }
8760            q+=GetPixelChannels(image);
8761         }
8762      }
8763
8764      if (mng_info->write_png8 == MagickFalse &&
8765          ping_exclude_bKGD == MagickFalse)
8766        {
8767          /* Add the background color to the palette, if it
8768           * isn't already there.
8769           */
8770           if (logging != MagickFalse)
8771             {
8772               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8773                   "      Check colormap for background (%d,%d,%d)",
8774                   (int) image->background_color.red,
8775                   (int) image->background_color.green,
8776                   (int) image->background_color.blue);
8777             }
8778           for (i=0; i<number_opaque; i++)
8779           {
8780              if (opaque[i].red == image->background_color.red &&
8781                  opaque[i].green == image->background_color.green &&
8782                  opaque[i].blue == image->background_color.blue)
8783                break;
8784           }
8785           if (number_opaque < 259 && i == number_opaque)
8786             {
8787                opaque[i] = image->background_color;
8788                ping_background.index = i;
8789                number_opaque++;
8790                if (logging != MagickFalse)
8791                  {
8792                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8793                        "      background_color index is %d",(int) i);
8794                  }
8795
8796             }
8797           else if (logging != MagickFalse)
8798               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8799                   "      No room in the colormap to add background color");
8800        }
8801
8802      image_colors=number_opaque+number_transparent+number_semitransparent;
8803
8804      if (logging != MagickFalse)
8805        {
8806          if (image_colors > 256)
8807             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8808                   "      image has more than 256 colors");
8809
8810          else
8811             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8812                   "      image has %d colors",image_colors);
8813        }
8814
8815      if (ping_preserve_colormap != MagickFalse)
8816        break;
8817
8818      if (mng_info->write_png_colortype != 7) /* We won't need this info */
8819        {
8820          ping_have_color=MagickFalse;
8821          ping_have_non_bw=MagickFalse;
8822
8823          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8824          {
8825            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8826               "incompatible colorspace");
8827            ping_have_color=MagickTrue;
8828            ping_have_non_bw=MagickTrue;
8829          }
8830
8831          if(image_colors > 256)
8832            {
8833              for (y=0; y < (ssize_t) image->rows; y++)
8834              {
8835                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8836
8837                if (q == (Quantum *) NULL)
8838                  break;
8839
8840                s=q;
8841                for (x=0; x < (ssize_t) image->columns; x++)
8842                {
8843                  if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8844                      GetPixelRed(image,s) != GetPixelBlue(image,s))
8845                    {
8846                       ping_have_color=MagickTrue;
8847                       ping_have_non_bw=MagickTrue;
8848                       break;
8849                    }
8850                  s+=GetPixelChannels(image);
8851                }
8852
8853                if (ping_have_color != MagickFalse)
8854                  break;
8855
8856                /* Worst case is black-and-white; we are looking at every
8857                 * pixel twice.
8858                 */
8859
8860                if (ping_have_non_bw == MagickFalse)
8861                  {
8862                    s=q;
8863                    for (x=0; x < (ssize_t) image->columns; x++)
8864                    {
8865                      if (GetPixelRed(image,s) != 0 &&
8866                          GetPixelRed(image,s) != QuantumRange)
8867                        {
8868                          ping_have_non_bw=MagickTrue;
8869                          break;
8870                        }
8871                      s+=GetPixelChannels(image);
8872                    }
8873                }
8874              }
8875            }
8876        }
8877
8878      if (image_colors < 257)
8879        {
8880          PixelInfo
8881            colormap[260];
8882
8883          /*
8884           * Initialize image colormap.
8885           */
8886
8887          if (logging != MagickFalse)
8888             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8889                   "      Sort the new colormap");
8890
8891         /* Sort palette, transparent first */;
8892
8893          n = 0;
8894
8895          for (i=0; i<number_transparent; i++)
8896             colormap[n++] = transparent[i];
8897
8898          for (i=0; i<number_semitransparent; i++)
8899             colormap[n++] = semitransparent[i];
8900
8901          for (i=0; i<number_opaque; i++)
8902             colormap[n++] = opaque[i];
8903
8904          ping_background.index +=
8905            (number_transparent + number_semitransparent);
8906
8907          /* image_colors < 257; search the colormap instead of the pixels
8908           * to get ping_have_color and ping_have_non_bw
8909           */
8910          for (i=0; i<n; i++)
8911          {
8912            if (ping_have_color == MagickFalse)
8913              {
8914                 if (colormap[i].red != colormap[i].green ||
8915                     colormap[i].red != colormap[i].blue)
8916                   {
8917                      ping_have_color=MagickTrue;
8918                      ping_have_non_bw=MagickTrue;
8919                      break;
8920                   }
8921               }
8922
8923            if (ping_have_non_bw == MagickFalse)
8924              {
8925                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8926                    ping_have_non_bw=MagickTrue;
8927              }
8928           }
8929
8930         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8931             (number_transparent == 0 && number_semitransparent == 0)) &&
8932             (((mng_info->write_png_colortype-1) ==
8933             PNG_COLOR_TYPE_PALETTE) ||
8934             (mng_info->write_png_colortype == 0)))
8935           {
8936             if (logging != MagickFalse)
8937               {
8938                 if (n !=  (ssize_t) image_colors)
8939                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8940                    "   image_colors (%d) and n (%d)  don't match",
8941                    image_colors, n);
8942
8943                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8944                    "      AcquireImageColormap");
8945               }
8946
8947             image->colors = image_colors;
8948
8949             if (AcquireImageColormap(image,image_colors,exception) ==
8950                 MagickFalse)
8951                ThrowWriterException(ResourceLimitError,
8952                    "MemoryAllocationFailed");
8953
8954             for (i=0; i< (ssize_t) image_colors; i++)
8955                image->colormap[i] = colormap[i];
8956
8957             if (logging != MagickFalse)
8958               {
8959                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8960                       "      image->colors=%d (%d)",
8961                       (int) image->colors, image_colors);
8962
8963                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8964                       "      Update the pixel indexes");
8965               }
8966
8967             /* Sync the pixel indices with the new colormap */
8968
8969             for (y=0; y < (ssize_t) image->rows; y++)
8970             {
8971               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8972
8973               if (q == (Quantum *) NULL)
8974                 break;
8975
8976               for (x=0; x < (ssize_t) image->columns; x++)
8977               {
8978                 for (i=0; i< (ssize_t) image_colors; i++)
8979                 {
8980                   if ((image->alpha_trait != BlendPixelTrait ||
8981                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8982                       image->colormap[i].red == GetPixelRed(image,q) &&
8983                       image->colormap[i].green == GetPixelGreen(image,q) &&
8984                       image->colormap[i].blue == GetPixelBlue(image,q))
8985                   {
8986                     SetPixelIndex(image,i,q);
8987                     break;
8988                   }
8989                 }
8990                 q+=GetPixelChannels(image);
8991               }
8992
8993               if (SyncAuthenticPixels(image,exception) == MagickFalse)
8994                  break;
8995             }
8996           }
8997        }
8998
8999      if (logging != MagickFalse)
9000        {
9001          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9002             "      image->colors=%d", (int) image->colors);
9003
9004          if (image->colormap != NULL)
9005            {
9006              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9007                  "       i     (red,green,blue,alpha)");
9008
9009              for (i=0; i < (ssize_t) image->colors; i++)
9010              {
9011                if (i < 300 || i >= (ssize_t) image->colors - 10)
9012                  {
9013                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9014                        "       %d     (%d,%d,%d,%d)",
9015                         (int) i,
9016                         (int) image->colormap[i].red,
9017                         (int) image->colormap[i].green,
9018                         (int) image->colormap[i].blue,
9019                         (int) image->colormap[i].alpha);
9020                  }
9021              }
9022            }
9023
9024            if (number_transparent < 257)
9025              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9026                    "      number_transparent     = %d",
9027                    number_transparent);
9028            else
9029
9030              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9031                    "      number_transparent     > 256");
9032
9033            if (number_opaque < 257)
9034              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9035                    "      number_opaque          = %d",
9036                    number_opaque);
9037
9038            else
9039              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9040                    "      number_opaque          > 256");
9041
9042            if (number_semitransparent < 257)
9043              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9044                    "      number_semitransparent = %d",
9045                    number_semitransparent);
9046
9047            else
9048              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9049                    "      number_semitransparent > 256");
9050
9051            if (ping_have_non_bw == MagickFalse)
9052               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9053                     "      All pixels and the background are black or white");
9054
9055            else if (ping_have_color == MagickFalse)
9056               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9057                     "      All pixels and the background are gray");
9058
9059            else
9060               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9061                     "      At least one pixel or the background is non-gray");
9062
9063            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9064                "    Exit BUILD_PALETTE:");
9065        }
9066
9067    if (mng_info->write_png8 == MagickFalse)
9068       break;
9069
9070    /* Make any reductions necessary for the PNG8 format */
9071     if (image_colors <= 256 &&
9072         image_colors != 0 && image->colormap != NULL &&
9073         number_semitransparent == 0 &&
9074         number_transparent <= 1)
9075       break;
9076
9077     /* PNG8 can't have semitransparent colors so we threshold the
9078      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9079      * transparent color so if more than one is transparent we merge
9080      * them into image->background_color.
9081      */
9082     if (number_semitransparent != 0 || number_transparent > 1)
9083       {
9084         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9085             "    Thresholding the alpha channel to binary");
9086
9087         for (y=0; y < (ssize_t) image->rows; y++)
9088         {
9089           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9090
9091           if (r == (Quantum *) NULL)
9092             break;
9093
9094           for (x=0; x < (ssize_t) image->columns; x++)
9095           {
9096               if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9097                 {
9098                   SetPixelInfoPixel(image,&image->background_color,r);
9099                   SetPixelAlpha(image,TransparentAlpha,r);
9100                 }
9101               else
9102                   SetPixelAlpha(image,OpaqueAlpha,r);
9103               r+=GetPixelChannels(image);
9104           }
9105
9106           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9107              break;
9108
9109           if (image_colors != 0 && image_colors <= 256 &&
9110              image->colormap != NULL)
9111             for (i=0; i<image_colors; i++)
9112                 image->colormap[i].alpha =
9113                     (image->colormap[i].alpha > TransparentAlpha/2 ?
9114                     TransparentAlpha : OpaqueAlpha);
9115         }
9116       continue;
9117     }
9118
9119     /* PNG8 can't have more than 256 colors so we quantize the pixels and
9120      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
9121      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9122      * colors or less.
9123      */
9124     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9125       {
9126         if (logging != MagickFalse)
9127            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9128                "    Quantizing the background color to 4-4-4");
9129
9130         tried_444 = MagickTrue;
9131
9132         LBR04PacketRGB(image->background_color);
9133
9134         if (logging != MagickFalse)
9135           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9136               "    Quantizing the pixel colors to 4-4-4");
9137
9138         if (image->colormap == NULL)
9139         {
9140           for (y=0; y < (ssize_t) image->rows; y++)
9141           {
9142             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9143
9144             if (r == (Quantum *) NULL)
9145               break;
9146
9147             for (x=0; x < (ssize_t) image->columns; x++)
9148             {
9149               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9150                   LBR04PixelRGB(r);
9151               r+=GetPixelChannels(image);
9152             }
9153
9154             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9155                break;
9156           }
9157         }
9158
9159         else /* Should not reach this; colormap already exists and
9160                 must be <= 256 */
9161         {
9162           if (logging != MagickFalse)
9163               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9164               "    Quantizing the colormap to 4-4-4");
9165
9166           for (i=0; i<image_colors; i++)
9167           {
9168             LBR04PacketRGB(image->colormap[i]);
9169           }
9170         }
9171         continue;
9172       }
9173
9174     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9175       {
9176         if (logging != MagickFalse)
9177            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9178                "    Quantizing the background color to 3-3-3");
9179
9180         tried_333 = MagickTrue;
9181
9182         LBR03PacketRGB(image->background_color);
9183
9184         if (logging != MagickFalse)
9185           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9186               "    Quantizing the pixel colors to 3-3-3-1");
9187
9188         if (image->colormap == NULL)
9189         {
9190           for (y=0; y < (ssize_t) image->rows; y++)
9191           {
9192             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9193
9194             if (r == (Quantum *) NULL)
9195               break;
9196
9197             for (x=0; x < (ssize_t) image->columns; x++)
9198             {
9199               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9200                   LBR03RGB(r);
9201               r+=GetPixelChannels(image);
9202             }
9203
9204             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9205                break;
9206           }
9207         }
9208
9209         else /* Should not reach this; colormap already exists and
9210                 must be <= 256 */
9211         {
9212           if (logging != MagickFalse)
9213               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9214               "    Quantizing the colormap to 3-3-3-1");
9215           for (i=0; i<image_colors; i++)
9216           {
9217               LBR03PacketRGB(image->colormap[i]);
9218           }
9219         }
9220         continue;
9221       }
9222
9223     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9224       {
9225         if (logging != MagickFalse)
9226            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9227                "    Quantizing the background color to 3-3-2");
9228
9229         tried_332 = MagickTrue;
9230
9231         /* Red and green were already done so we only quantize the blue
9232          * channel
9233          */
9234
9235         LBR02PacketBlue(image->background_color);
9236
9237         if (logging != MagickFalse)
9238           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9239               "    Quantizing the pixel colors to 3-3-2-1");
9240
9241         if (image->colormap == NULL)
9242         {
9243           for (y=0; y < (ssize_t) image->rows; y++)
9244           {
9245             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9246
9247             if (r == (Quantum *) NULL)
9248               break;
9249
9250             for (x=0; x < (ssize_t) image->columns; x++)
9251             {
9252               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9253                   LBR02PixelBlue(r);
9254               r+=GetPixelChannels(image);
9255             }
9256
9257             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9258                break;
9259           }
9260         }
9261
9262         else /* Should not reach this; colormap already exists and
9263                 must be <= 256 */
9264         {
9265           if (logging != MagickFalse)
9266               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9267               "    Quantizing the colormap to 3-3-2-1");
9268           for (i=0; i<image_colors; i++)
9269           {
9270               LBR02PacketBlue(image->colormap[i]);
9271           }
9272       }
9273       continue;
9274     }
9275
9276     if (image_colors == 0 || image_colors > 256)
9277     {
9278       /* Take care of special case with 256 opaque colors + 1 transparent
9279        * color.  We don't need to quantize to 2-3-2-1; we only need to
9280        * eliminate one color, so we'll merge the two darkest red
9281        * colors (0x49, 0, 0) -> (0x24, 0, 0).
9282        */
9283       if (logging != MagickFalse)
9284         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9285             "    Merging two dark red background colors to 3-3-2-1");
9286
9287       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9288           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9289           ScaleQuantumToChar(image->background_color.blue) == 0x00)
9290       {
9291          image->background_color.red=ScaleCharToQuantum(0x24);
9292       }
9293
9294       if (logging != MagickFalse)
9295         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9296             "    Merging two dark red pixel colors to 3-3-2-1");
9297
9298       if (image->colormap == NULL)
9299       {
9300         for (y=0; y < (ssize_t) image->rows; y++)
9301         {
9302           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9303
9304           if (r == (Quantum *) NULL)
9305             break;
9306
9307           for (x=0; x < (ssize_t) image->columns; x++)
9308           {
9309             if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9310                 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9311                 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9312                 GetPixelAlpha(image,r) == OpaqueAlpha)
9313               {
9314                 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9315               }
9316             r+=GetPixelChannels(image);
9317           }
9318
9319           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9320              break;
9321
9322         }
9323       }
9324
9325       else
9326       {
9327          for (i=0; i<image_colors; i++)
9328          {
9329             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9330                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9331                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9332             {
9333                image->colormap[i].red=ScaleCharToQuantum(0x24);
9334             }
9335          }
9336       }
9337     }
9338   }
9339   }
9340   /* END OF BUILD_PALETTE */
9341
9342   /* If we are excluding the tRNS chunk and there is transparency,
9343    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9344    * PNG.
9345    */
9346   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9347      (number_transparent != 0 || number_semitransparent != 0))
9348     {
9349       unsigned int colortype=mng_info->write_png_colortype;
9350
9351       if (ping_have_color == MagickFalse)
9352         mng_info->write_png_colortype = 5;
9353
9354       else
9355         mng_info->write_png_colortype = 7;
9356
9357       if (colortype != 0 &&
9358          mng_info->write_png_colortype != colortype)
9359         ping_need_colortype_warning=MagickTrue;
9360
9361     }
9362
9363   /* See if cheap transparency is possible.  It is only possible
9364    * when there is a single transparent color, no semitransparent
9365    * color, and no opaque color that has the same RGB components
9366    * as the transparent color.  We only need this information if
9367    * we are writing a PNG with colortype 0 or 2, and we have not
9368    * excluded the tRNS chunk.
9369    */
9370   if (number_transparent == 1 &&
9371       mng_info->write_png_colortype < 4)
9372     {
9373        ping_have_cheap_transparency = MagickTrue;
9374
9375        if (number_semitransparent != 0)
9376          ping_have_cheap_transparency = MagickFalse;
9377
9378        else if (image_colors == 0 || image_colors > 256 ||
9379            image->colormap == NULL)
9380          {
9381            register const Quantum
9382              *q;
9383
9384            for (y=0; y < (ssize_t) image->rows; y++)
9385            {
9386              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9387
9388              if (q == (Quantum *) NULL)
9389                break;
9390
9391              for (x=0; x < (ssize_t) image->columns; x++)
9392              {
9393                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9394                      (unsigned short) GetPixelRed(image,q) ==
9395                                      ping_trans_color.red &&
9396                      (unsigned short) GetPixelGreen(image,q) ==
9397                                      ping_trans_color.green &&
9398                      (unsigned short) GetPixelBlue(image,q) ==
9399                                      ping_trans_color.blue)
9400                    {
9401                      ping_have_cheap_transparency = MagickFalse;
9402                      break;
9403                    }
9404
9405                  q+=GetPixelChannels(image);
9406              }
9407
9408              if (ping_have_cheap_transparency == MagickFalse)
9409                 break;
9410            }
9411          }
9412        else
9413          {
9414             /* Assuming that image->colormap[0] is the one transparent color
9415              * and that all others are opaque.
9416              */
9417             if (image_colors > 1)
9418               for (i=1; i<image_colors; i++)
9419                 if (image->colormap[i].red == image->colormap[0].red &&
9420                     image->colormap[i].green == image->colormap[0].green &&
9421                     image->colormap[i].blue == image->colormap[0].blue)
9422                   {
9423                      ping_have_cheap_transparency = MagickFalse;
9424                      break;
9425                   }
9426          }
9427
9428        if (logging != MagickFalse)
9429          {
9430            if (ping_have_cheap_transparency == MagickFalse)
9431              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9432                  "   Cheap transparency is not possible.");
9433
9434            else
9435              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9436                  "   Cheap transparency is possible.");
9437          }
9438      }
9439   else
9440     ping_have_cheap_transparency = MagickFalse;
9441
9442   image_depth=image->depth;
9443
9444   quantum_info = (QuantumInfo *) NULL;
9445   number_colors=0;
9446   image_colors=(int) image->colors;
9447   image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
9448
9449   mng_info->IsPalette=image->storage_class == PseudoClass &&
9450     image_colors <= 256 && image->colormap != NULL;
9451
9452   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9453      (image->colors == 0 || image->colormap == NULL))
9454     {
9455       image_info=DestroyImageInfo(image_info);
9456       image=DestroyImage(image);
9457       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9458           "Cannot write PNG8 or color-type 3; colormap is NULL",
9459           "`%s'",IMimage->filename);
9460       return(MagickFalse);
9461     }
9462
9463   /*
9464     Allocate the PNG structures
9465   */
9466 #ifdef PNG_USER_MEM_SUPPORTED
9467  error_info.image=image;
9468  error_info.exception=exception;
9469   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9470     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9471     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9472
9473 #else
9474   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9475     MagickPNGErrorHandler,MagickPNGWarningHandler);
9476
9477 #endif
9478   if (ping == (png_struct *) NULL)
9479     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9480
9481   ping_info=png_create_info_struct(ping);
9482
9483   if (ping_info == (png_info *) NULL)
9484     {
9485       png_destroy_write_struct(&ping,(png_info **) NULL);
9486       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9487     }
9488
9489   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9490   pixel_info=(MemoryInfo *) NULL;
9491
9492   if (setjmp(png_jmpbuf(ping)))
9493     {
9494       /*
9495         PNG write failed.
9496       */
9497 #ifdef PNG_DEBUG
9498      if (image_info->verbose)
9499         (void) printf("PNG write has failed.\n");
9500 #endif
9501       png_destroy_write_struct(&ping,&ping_info);
9502 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9503       UnlockSemaphoreInfo(ping_semaphore);
9504 #endif
9505
9506       if (pixel_info != (MemoryInfo *) NULL)
9507         pixel_info=RelinquishVirtualMemory(pixel_info);
9508
9509       if (quantum_info != (QuantumInfo *) NULL)
9510         quantum_info=DestroyQuantumInfo(quantum_info);
9511
9512       if (ping_have_blob != MagickFalse)
9513           (void) CloseBlob(image);
9514       image_info=DestroyImageInfo(image_info);
9515       image=DestroyImage(image);
9516       return(MagickFalse);
9517     }
9518
9519   /* {  For navigation to end of SETJMP-protected block.  Within this
9520    *    block, use png_error() instead of Throwing an Exception, to ensure
9521    *    that libpng is able to clean up, and that the semaphore is unlocked.
9522    */
9523
9524 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9525   LockSemaphoreInfo(ping_semaphore);
9526 #endif
9527
9528 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9529   /* Allow benign errors */
9530   png_set_benign_errors(ping, 1);
9531 #endif
9532
9533   /*
9534     Prepare PNG for writing.
9535   */
9536
9537 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9538   if (mng_info->write_mng)
9539   {
9540      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9541 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9542      /* Disable new libpng-1.5.10 feature when writing a MNG because
9543       * zero-length PLTE is OK
9544       */
9545      png_set_check_for_invalid_index (ping, 0);
9546 # endif
9547   }
9548
9549 #else
9550 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9551   if (mng_info->write_mng)
9552      png_permit_empty_plte(ping,MagickTrue);
9553
9554 # endif
9555 #endif
9556
9557   x=0;
9558
9559   ping_width=(png_uint_32) image->columns;
9560   ping_height=(png_uint_32) image->rows;
9561
9562   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9563      image_depth=8;
9564
9565   if (mng_info->write_png48 || mng_info->write_png64)
9566      image_depth=16;
9567
9568   if (mng_info->write_png_depth != 0)
9569      image_depth=mng_info->write_png_depth;
9570
9571   /* Adjust requested depth to next higher valid depth if necessary */
9572   if (image_depth > 8)
9573      image_depth=16;
9574
9575   if ((image_depth > 4) && (image_depth < 8))
9576      image_depth=8;
9577
9578   if (image_depth == 3)
9579      image_depth=4;
9580
9581   if (logging != MagickFalse)
9582     {
9583      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9584         "    width=%.20g",(double) ping_width);
9585      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9586         "    height=%.20g",(double) ping_height);
9587      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9588         "    image_matte=%.20g",(double) image->alpha_trait);
9589      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9590         "    image->depth=%.20g",(double) image->depth);
9591      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9592         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
9593     }
9594
9595   save_image_depth=image_depth;
9596   ping_bit_depth=(png_byte) save_image_depth;
9597
9598
9599 #if defined(PNG_pHYs_SUPPORTED)
9600   if (ping_exclude_pHYs == MagickFalse)
9601   {
9602   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9603       (!mng_info->write_mng || !mng_info->equal_physs))
9604     {
9605       if (logging != MagickFalse)
9606         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9607             "    Setting up pHYs chunk");
9608
9609       if (image->units == PixelsPerInchResolution)
9610         {
9611           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9612           ping_pHYs_x_resolution=
9613              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9614           ping_pHYs_y_resolution=
9615              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9616         }
9617
9618       else if (image->units == PixelsPerCentimeterResolution)
9619         {
9620           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9621           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9622           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9623         }
9624
9625       else
9626         {
9627           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9628           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9629           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9630         }
9631
9632       if (logging != MagickFalse)
9633         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9634           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9635           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9636           (int) ping_pHYs_unit_type);
9637        ping_have_pHYs = MagickTrue;
9638     }
9639   }
9640 #endif
9641
9642   if (ping_exclude_bKGD == MagickFalse)
9643   {
9644   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9645     {
9646        unsigned int
9647          mask;
9648
9649        mask=0xffff;
9650        if (ping_bit_depth == 8)
9651           mask=0x00ff;
9652
9653        if (ping_bit_depth == 4)
9654           mask=0x000f;
9655
9656        if (ping_bit_depth == 2)
9657           mask=0x0003;
9658
9659        if (ping_bit_depth == 1)
9660           mask=0x0001;
9661
9662        ping_background.red=(png_uint_16)
9663          (ScaleQuantumToShort(image->background_color.red) & mask);
9664
9665        ping_background.green=(png_uint_16)
9666          (ScaleQuantumToShort(image->background_color.green) & mask);
9667
9668        ping_background.blue=(png_uint_16)
9669          (ScaleQuantumToShort(image->background_color.blue) & mask);
9670
9671        ping_background.gray=(png_uint_16) ping_background.green;
9672     }
9673
9674   if (logging != MagickFalse)
9675     {
9676       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9677           "    Setting up bKGD chunk (1)");
9678       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9679           "      background_color index is %d",
9680           (int) ping_background.index);
9681
9682       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9683           "    ping_bit_depth=%d",ping_bit_depth);
9684     }
9685
9686   ping_have_bKGD = MagickTrue;
9687   }
9688
9689   /*
9690     Select the color type.
9691   */
9692   matte=image_matte;
9693   old_bit_depth=0;
9694
9695   if (mng_info->IsPalette && mng_info->write_png8)
9696     {
9697       /* To do: make this a function cause it's used twice, except
9698          for reducing the sample depth from 8. */
9699
9700       number_colors=image_colors;
9701
9702       ping_have_tRNS=MagickFalse;
9703
9704       /*
9705         Set image palette.
9706       */
9707       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9708
9709       if (logging != MagickFalse)
9710         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9711             "  Setting up PLTE chunk with %d colors (%d)",
9712             number_colors, image_colors);
9713
9714       for (i=0; i < (ssize_t) number_colors; i++)
9715       {
9716         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9717         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9718         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9719         if (logging != MagickFalse)
9720           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9721 #if MAGICKCORE_QUANTUM_DEPTH == 8
9722             "    %3ld (%3d,%3d,%3d)",
9723 #else
9724             "    %5ld (%5d,%5d,%5d)",
9725 #endif
9726             (long) i,palette[i].red,palette[i].green,palette[i].blue);
9727
9728       }
9729
9730       ping_have_PLTE=MagickTrue;
9731       image_depth=ping_bit_depth;
9732       ping_num_trans=0;
9733
9734       if (matte != MagickFalse)
9735       {
9736           /*
9737             Identify which colormap entry is transparent.
9738           */
9739           assert(number_colors <= 256);
9740           assert(image->colormap != NULL);
9741
9742           for (i=0; i < (ssize_t) number_transparent; i++)
9743              ping_trans_alpha[i]=0;
9744
9745
9746           ping_num_trans=(unsigned short) (number_transparent +
9747              number_semitransparent);
9748
9749           if (ping_num_trans == 0)
9750              ping_have_tRNS=MagickFalse;
9751
9752           else
9753              ping_have_tRNS=MagickTrue;
9754       }
9755
9756       if (ping_exclude_bKGD == MagickFalse)
9757       {
9758        /*
9759         * Identify which colormap entry is the background color.
9760         */
9761
9762         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9763           if (IsPNGColorEqual(ping_background,image->colormap[i]))
9764             break;
9765
9766         ping_background.index=(png_byte) i;
9767
9768         if (logging != MagickFalse)
9769           {
9770             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9771                  "      background_color index is %d",
9772                  (int) ping_background.index);
9773           }
9774       }
9775     } /* end of write_png8 */
9776
9777   else if (mng_info->write_png_colortype == 1)
9778     {
9779       image_matte=MagickFalse;
9780       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9781     }
9782
9783   else if (mng_info->write_png24 || mng_info->write_png48 ||
9784       mng_info->write_png_colortype == 3)
9785     {
9786       image_matte=MagickFalse;
9787       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9788     }
9789
9790   else if (mng_info->write_png32 || mng_info->write_png64 ||
9791       mng_info->write_png_colortype == 7)
9792     {
9793       image_matte=MagickTrue;
9794       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9795     }
9796
9797   else /* mng_info->write_pngNN not specified */
9798     {
9799       image_depth=ping_bit_depth;
9800
9801       if (mng_info->write_png_colortype != 0)
9802         {
9803           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9804
9805           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9806               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9807             image_matte=MagickTrue;
9808
9809           else
9810             image_matte=MagickFalse;
9811
9812           if (logging != MagickFalse)
9813              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9814              "   PNG colortype %d was specified:",(int) ping_color_type);
9815         }
9816
9817       else /* write_png_colortype not specified */
9818         {
9819           if (logging != MagickFalse)
9820              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9821              "  Selecting PNG colortype:");
9822
9823           ping_color_type=(png_byte) ((matte != MagickFalse)?
9824             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9825
9826           if (image_info->type == TrueColorType)
9827             {
9828               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9829               image_matte=MagickFalse;
9830             }
9831
9832           if (image_info->type == TrueColorMatteType)
9833             {
9834               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9835               image_matte=MagickTrue;
9836             }
9837
9838           if (image_info->type == PaletteType ||
9839               image_info->type == PaletteMatteType)
9840             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9841
9842           if (mng_info->write_png_colortype == 0 &&
9843              (image_info->type == UndefinedType ||
9844              image_info->type == OptimizeType))
9845             {
9846               if (ping_have_color == MagickFalse)
9847                 {
9848                   if (image_matte == MagickFalse)
9849                     {
9850                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9851                       image_matte=MagickFalse;
9852                     }
9853
9854                   else
9855                     {
9856                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9857                       image_matte=MagickTrue;
9858                     }
9859                 }
9860               else
9861                 {
9862                   if (image_matte == MagickFalse)
9863                     {
9864                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9865                       image_matte=MagickFalse;
9866                     }
9867
9868                   else
9869                     {
9870                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9871                       image_matte=MagickTrue;
9872                     }
9873                  }
9874             }
9875
9876         }
9877
9878       if (logging != MagickFalse)
9879          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9880          "    Selected PNG colortype=%d",ping_color_type);
9881
9882       if (ping_bit_depth < 8)
9883         {
9884           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9885               ping_color_type == PNG_COLOR_TYPE_RGB ||
9886               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9887             ping_bit_depth=8;
9888         }
9889
9890       old_bit_depth=ping_bit_depth;
9891
9892       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9893         {
9894           if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
9895              ping_bit_depth=1;
9896         }
9897
9898       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9899         {
9900            size_t one = 1;
9901            ping_bit_depth=1;
9902
9903            if (image->colors == 0)
9904            {
9905               /* DO SOMETHING */
9906                 png_error(ping,"image has 0 colors");
9907            }
9908
9909            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9910              ping_bit_depth <<= 1;
9911         }
9912
9913       if (logging != MagickFalse)
9914          {
9915            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9916             "    Number of colors: %.20g",(double) image_colors);
9917
9918            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9919             "    Tentative PNG bit depth: %d",ping_bit_depth);
9920          }
9921
9922       if (ping_bit_depth < (int) mng_info->write_png_depth)
9923          ping_bit_depth = mng_info->write_png_depth;
9924     }
9925
9926   image_depth=ping_bit_depth;
9927
9928   if (logging != MagickFalse)
9929     {
9930       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9931         "    Tentative PNG color type: %s (%.20g)",
9932         PngColorTypeToString(ping_color_type),
9933         (double) ping_color_type);
9934
9935       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9936         "    image_info->type: %.20g",(double) image_info->type);
9937
9938       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9939         "    image_depth: %.20g",(double) image_depth);
9940
9941       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9942
9943         "    image->depth: %.20g",(double) image->depth);
9944
9945       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9946         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
9947     }
9948
9949   if (matte != MagickFalse)
9950     {
9951       if (mng_info->IsPalette)
9952         {
9953           if (mng_info->write_png_colortype == 0)
9954             {
9955               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9956
9957               if (ping_have_color != MagickFalse)
9958                  ping_color_type=PNG_COLOR_TYPE_RGBA;
9959             }
9960
9961           /*
9962            * Determine if there is any transparent color.
9963           */
9964           if (number_transparent + number_semitransparent == 0)
9965             {
9966               /*
9967                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
9968               */
9969
9970               image_matte=MagickFalse;
9971
9972               if (mng_info->write_png_colortype == 0)
9973                 ping_color_type&=0x03;
9974             }
9975
9976           else
9977             {
9978               unsigned int
9979                 mask;
9980
9981               mask=0xffff;
9982
9983               if (ping_bit_depth == 8)
9984                  mask=0x00ff;
9985
9986               if (ping_bit_depth == 4)
9987                  mask=0x000f;
9988
9989               if (ping_bit_depth == 2)
9990                  mask=0x0003;
9991
9992               if (ping_bit_depth == 1)
9993                  mask=0x0001;
9994
9995               ping_trans_color.red=(png_uint_16)
9996                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9997
9998               ping_trans_color.green=(png_uint_16)
9999                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10000
10001               ping_trans_color.blue=(png_uint_16)
10002                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10003
10004               ping_trans_color.gray=(png_uint_16)
10005                 (ScaleQuantumToShort(GetPixelInfoIntensity(
10006                    image->colormap)) & mask);
10007
10008               ping_trans_color.index=(png_byte) 0;
10009
10010               ping_have_tRNS=MagickTrue;
10011             }
10012
10013           if (ping_have_tRNS != MagickFalse)
10014             {
10015               /*
10016                * Determine if there is one and only one transparent color
10017                * and if so if it is fully transparent.
10018                */
10019               if (ping_have_cheap_transparency == MagickFalse)
10020                 ping_have_tRNS=MagickFalse;
10021             }
10022
10023           if (ping_have_tRNS != MagickFalse)
10024             {
10025               if (mng_info->write_png_colortype == 0)
10026                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
10027
10028               if (image_depth == 8)
10029                 {
10030                   ping_trans_color.red&=0xff;
10031                   ping_trans_color.green&=0xff;
10032                   ping_trans_color.blue&=0xff;
10033                   ping_trans_color.gray&=0xff;
10034                 }
10035             }
10036         }
10037       else
10038         {
10039           if (image_depth == 8)
10040             {
10041               ping_trans_color.red&=0xff;
10042               ping_trans_color.green&=0xff;
10043               ping_trans_color.blue&=0xff;
10044               ping_trans_color.gray&=0xff;
10045             }
10046         }
10047     }
10048
10049     matte=image_matte;
10050
10051     if (ping_have_tRNS != MagickFalse)
10052       image_matte=MagickFalse;
10053
10054     if ((mng_info->IsPalette) &&
10055         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10056         ping_have_color == MagickFalse &&
10057         (image_matte == MagickFalse || image_depth >= 8))
10058       {
10059         size_t one=1;
10060
10061         if (image_matte != MagickFalse)
10062           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10063
10064         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10065           {
10066             ping_color_type=PNG_COLOR_TYPE_GRAY;
10067
10068             if (save_image_depth == 16 && image_depth == 8)
10069               {
10070                 if (logging != MagickFalse)
10071                   {
10072                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10073                         "  Scaling ping_trans_color (0)");
10074                   }
10075                     ping_trans_color.gray*=0x0101;
10076               }
10077           }
10078
10079         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10080           image_depth=MAGICKCORE_QUANTUM_DEPTH;
10081
10082         if ((image_colors == 0) ||
10083              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10084           image_colors=(int) (one << image_depth);
10085
10086         if (image_depth > 8)
10087           ping_bit_depth=16;
10088
10089         else
10090           {
10091             ping_bit_depth=8;
10092             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10093               {
10094                 if(!mng_info->write_png_depth)
10095                   {
10096                     ping_bit_depth=1;
10097
10098                     while ((int) (one << ping_bit_depth)
10099                         < (ssize_t) image_colors)
10100                       ping_bit_depth <<= 1;
10101                   }
10102               }
10103
10104             else if (ping_color_type ==
10105                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10106                 mng_info->IsPalette)
10107               {
10108               /* Check if grayscale is reducible */
10109
10110                 int
10111                   depth_4_ok=MagickTrue,
10112                   depth_2_ok=MagickTrue,
10113                   depth_1_ok=MagickTrue;
10114
10115                 for (i=0; i < (ssize_t) image_colors; i++)
10116                 {
10117                    unsigned char
10118                      intensity;
10119
10120                    intensity=ScaleQuantumToChar(image->colormap[i].red);
10121
10122                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10123                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10124                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10125                      depth_2_ok=depth_1_ok=MagickFalse;
10126                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10127                      depth_1_ok=MagickFalse;
10128                 }
10129
10130                 if (depth_1_ok && mng_info->write_png_depth <= 1)
10131                   ping_bit_depth=1;
10132
10133                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10134                   ping_bit_depth=2;
10135
10136                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10137                   ping_bit_depth=4;
10138               }
10139           }
10140
10141           image_depth=ping_bit_depth;
10142       }
10143
10144     else
10145
10146       if (mng_info->IsPalette)
10147       {
10148         number_colors=image_colors;
10149
10150         if (image_depth <= 8)
10151           {
10152             /*
10153               Set image palette.
10154             */
10155             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10156
10157             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10158               {
10159                 for (i=0; i < (ssize_t) number_colors; i++)
10160                 {
10161                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10162                   palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10163                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10164                 }
10165
10166                 if (logging != MagickFalse)
10167                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10168                     "  Setting up PLTE chunk with %d colors",
10169                     number_colors);
10170
10171                 ping_have_PLTE=MagickTrue;
10172               }
10173
10174             /* color_type is PNG_COLOR_TYPE_PALETTE */
10175             if (mng_info->write_png_depth == 0)
10176               {
10177                 size_t
10178                   one;
10179
10180                 ping_bit_depth=1;
10181                 one=1;
10182
10183                 while ((one << ping_bit_depth) < (size_t) number_colors)
10184                   ping_bit_depth <<= 1;
10185               }
10186
10187             ping_num_trans=0;
10188
10189             if (matte != MagickFalse)
10190               {
10191                 /*
10192                  * Set up trans_colors array.
10193                  */
10194                 assert(number_colors <= 256);
10195
10196                 ping_num_trans=(unsigned short) (number_transparent +
10197                   number_semitransparent);
10198
10199                 if (ping_num_trans == 0)
10200                   ping_have_tRNS=MagickFalse;
10201
10202                 else
10203                   {
10204                     if (logging != MagickFalse)
10205                       {
10206                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10207                           "  Scaling ping_trans_color (1)");
10208                       }
10209                     ping_have_tRNS=MagickTrue;
10210
10211                     for (i=0; i < ping_num_trans; i++)
10212                     {
10213                        ping_trans_alpha[i]= (png_byte)
10214                          ScaleQuantumToChar(image->colormap[i].alpha);
10215                     }
10216                   }
10217               }
10218           }
10219       }
10220
10221     else
10222       {
10223
10224         if (image_depth < 8)
10225           image_depth=8;
10226
10227         if ((save_image_depth == 16) && (image_depth == 8))
10228           {
10229             if (logging != MagickFalse)
10230               {
10231                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10232                   "    Scaling ping_trans_color from (%d,%d,%d)",
10233                   (int) ping_trans_color.red,
10234                   (int) ping_trans_color.green,
10235                   (int) ping_trans_color.blue);
10236               }
10237
10238             ping_trans_color.red*=0x0101;
10239             ping_trans_color.green*=0x0101;
10240             ping_trans_color.blue*=0x0101;
10241             ping_trans_color.gray*=0x0101;
10242
10243             if (logging != MagickFalse)
10244               {
10245                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10246                   "    to (%d,%d,%d)",
10247                   (int) ping_trans_color.red,
10248                   (int) ping_trans_color.green,
10249                   (int) ping_trans_color.blue);
10250               }
10251           }
10252       }
10253
10254     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
10255          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
10256
10257     /*
10258       Adjust background and transparency samples in sub-8-bit grayscale files.
10259     */
10260     if (ping_bit_depth < 8 && ping_color_type ==
10261         PNG_COLOR_TYPE_GRAY)
10262       {
10263          png_uint_16
10264            maxval;
10265
10266          size_t
10267            one=1;
10268
10269          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10270
10271          if (ping_exclude_bKGD == MagickFalse)
10272          {
10273
10274          ping_background.gray=(png_uint_16) ((maxval/65535.)*
10275            (ScaleQuantumToShort(((GetPixelInfoIntensity(
10276            &image->background_color))) +.5)));
10277
10278          if (logging != MagickFalse)
10279            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10280              "  Setting up bKGD chunk (2)");
10281          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10282              "      background_color index is %d",
10283              (int) ping_background.index);
10284
10285          ping_have_bKGD = MagickTrue;
10286          }
10287
10288          if (logging != MagickFalse)
10289            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10290              "  Scaling ping_trans_color.gray from %d",
10291              (int)ping_trans_color.gray);
10292
10293          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10294            ping_trans_color.gray)+.5);
10295
10296          if (logging != MagickFalse)
10297            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10298              "      to %d", (int)ping_trans_color.gray);
10299       }
10300
10301   if (ping_exclude_bKGD == MagickFalse)
10302   {
10303     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10304       {
10305         /*
10306            Identify which colormap entry is the background color.
10307         */
10308
10309         number_colors=image_colors;
10310
10311         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10312           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10313             break;
10314
10315         ping_background.index=(png_byte) i;
10316
10317         if (logging != MagickFalse)
10318           {
10319             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10320               "  Setting up bKGD chunk with index=%d",(int) i);
10321           }
10322
10323         if (i < (ssize_t) number_colors)
10324           {
10325             ping_have_bKGD = MagickTrue;
10326
10327             if (logging != MagickFalse)
10328               {
10329                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10330                   "     background   =(%d,%d,%d)",
10331                         (int) ping_background.red,
10332                         (int) ping_background.green,
10333                         (int) ping_background.blue);
10334               }
10335           }
10336
10337         else  /* Can't happen */
10338           {
10339             if (logging != MagickFalse)
10340               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10341                   "      No room in PLTE to add bKGD color");
10342             ping_have_bKGD = MagickFalse;
10343           }
10344       }
10345   }
10346
10347   if (logging != MagickFalse)
10348     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10349       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10350       ping_color_type);
10351   /*
10352     Initialize compression level and filtering.
10353   */
10354   if (logging != MagickFalse)
10355     {
10356       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10357         "  Setting up deflate compression");
10358
10359       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10360         "    Compression buffer size: 32768");
10361     }
10362
10363   png_set_compression_buffer_size(ping,32768L);
10364
10365   if (logging != MagickFalse)
10366     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10367       "    Compression mem level: 9");
10368
10369   png_set_compression_mem_level(ping, 9);
10370
10371   /* Untangle the "-quality" setting:
10372
10373      Undefined is 0; the default is used.
10374      Default is 75
10375
10376      10's digit:
10377
10378         0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10379            zlib default compression level
10380
10381         1-9: the zlib compression level
10382
10383      1's digit:
10384
10385         0-4: the PNG filter method
10386
10387         5:   libpng adaptive filtering if compression level > 5
10388              libpng filter type "none" if compression level <= 5
10389                 or if image is grayscale or palette
10390
10391         6:   libpng adaptive filtering
10392
10393         7:   "LOCO" filtering (intrapixel differing) if writing
10394              a MNG, otherwise "none".  Did not work in IM-6.7.0-9
10395              and earlier because of a missing "else".
10396
10397         8:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10398              filtering. Unused prior to IM-6.7.0-10, was same as 6
10399
10400         9:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10401              Unused prior to IM-6.7.0-10, was same as 6
10402
10403     Note that using the -quality option, not all combinations of
10404     PNG filter type, zlib compression level, and zlib compression
10405     strategy are possible.  This will be addressed soon in a
10406     release that accomodates "-define png:compression-strategy", etc.
10407
10408    */
10409
10410   quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10411      image_info->quality;
10412
10413   if (quality <= 9)
10414     {
10415       if (mng_info->write_png_compression_strategy == 0)
10416         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10417     }
10418
10419   else if (mng_info->write_png_compression_level == 0)
10420     {
10421       int
10422         level;
10423
10424       level=(int) MagickMin((ssize_t) quality/10,9);
10425
10426       mng_info->write_png_compression_level = level+1;
10427     }
10428
10429   if (mng_info->write_png_compression_strategy == 0)
10430     {
10431         if ((quality %10) == 8 || (quality %10) == 9)
10432 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10433           mng_info->write_png_compression_strategy=Z_RLE+1;
10434 #else
10435           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10436 #endif
10437     }
10438
10439   if (mng_info->write_png_compression_filter == 0)
10440         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10441
10442   if (logging != MagickFalse)
10443     {
10444      if (mng_info->write_png_compression_level)
10445         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10446           "    Compression level:    %d",
10447             (int) mng_info->write_png_compression_level-1);
10448
10449      if (mng_info->write_png_compression_strategy)
10450         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10451           "    Compression strategy: %d",
10452             (int) mng_info->write_png_compression_strategy-1);
10453
10454         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10455           "  Setting up filtering");
10456
10457         if (mng_info->write_png_compression_filter == 6)
10458           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10459             "    Base filter method: ADAPTIVE");
10460         else if (mng_info->write_png_compression_filter == 0 ||
10461                  mng_info->write_png_compression_filter == 1)
10462           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10463             "    Base filter method: NONE");
10464         else
10465           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10466             "    Base filter method: %d",
10467             (int) mng_info->write_png_compression_filter-1);
10468     }
10469
10470   if (mng_info->write_png_compression_level != 0)
10471     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10472
10473   if (mng_info->write_png_compression_filter == 6)
10474     {
10475       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10476          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10477          (quality < 50))
10478         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10479       else
10480         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10481      }
10482   else if (mng_info->write_png_compression_filter == 7 ||
10483       mng_info->write_png_compression_filter == 10)
10484     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10485
10486   else if (mng_info->write_png_compression_filter == 8)
10487     {
10488 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10489       if (mng_info->write_mng)
10490       {
10491          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10492              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10493         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10494       }
10495 #endif
10496       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10497     }
10498
10499   else if (mng_info->write_png_compression_filter == 9)
10500     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10501
10502   else if (mng_info->write_png_compression_filter != 0)
10503     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10504        mng_info->write_png_compression_filter-1);
10505
10506   if (mng_info->write_png_compression_strategy != 0)
10507     png_set_compression_strategy(ping,
10508        mng_info->write_png_compression_strategy-1);
10509
10510   ping_interlace_method=image_info->interlace != NoInterlace;
10511
10512   if (mng_info->write_mng)
10513     png_set_sig_bytes(ping,8);
10514
10515   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10516
10517   if (mng_info->write_png_colortype != 0)
10518     {
10519      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10520        if (ping_have_color != MagickFalse)
10521          {
10522            ping_color_type = PNG_COLOR_TYPE_RGB;
10523
10524            if (ping_bit_depth < 8)
10525              ping_bit_depth=8;
10526          }
10527
10528      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10529        if (ping_have_color != MagickFalse)
10530          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10531     }
10532
10533   if (ping_need_colortype_warning != MagickFalse ||
10534      ((mng_info->write_png_depth &&
10535      (int) mng_info->write_png_depth != ping_bit_depth) ||
10536      (mng_info->write_png_colortype &&
10537      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10538       mng_info->write_png_colortype != 7 &&
10539       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10540     {
10541       if (logging != MagickFalse)
10542         {
10543           if (ping_need_colortype_warning != MagickFalse)
10544             {
10545               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10546                  "  Image has transparency but tRNS chunk was excluded");
10547             }
10548
10549           if (mng_info->write_png_depth)
10550             {
10551               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10552                   "  Defined png:bit-depth=%u, Computed depth=%u",
10553                   mng_info->write_png_depth,
10554                   ping_bit_depth);
10555             }
10556
10557           if (mng_info->write_png_colortype)
10558             {
10559               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10560                   "  Defined png:color-type=%u, Computed color type=%u",
10561                   mng_info->write_png_colortype-1,
10562                   ping_color_type);
10563             }
10564         }
10565
10566       png_warning(ping,
10567         "Cannot write image with defined png:bit-depth or png:color-type.");
10568     }
10569
10570   if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10571     {
10572       /* Add an opaque matte channel */
10573       image->alpha_trait = BlendPixelTrait;
10574       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10575
10576       if (logging != MagickFalse)
10577         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10578           "  Added an opaque matte channel");
10579     }
10580
10581   if (number_transparent != 0 || number_semitransparent != 0)
10582     {
10583       if (ping_color_type < 4)
10584         {
10585            ping_have_tRNS=MagickTrue;
10586            if (logging != MagickFalse)
10587              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10588                "  Setting ping_have_tRNS=MagickTrue.");
10589         }
10590     }
10591
10592   if (logging != MagickFalse)
10593     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10594       "  Writing PNG header chunks");
10595
10596   png_set_IHDR(ping,ping_info,ping_width,ping_height,
10597                ping_bit_depth,ping_color_type,
10598                ping_interlace_method,ping_compression_method,
10599                ping_filter_method);
10600
10601   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10602     {
10603       png_set_PLTE(ping,ping_info,palette,number_colors);
10604
10605       if (logging != MagickFalse)
10606         {
10607           for (i=0; i< (ssize_t) number_colors; i++)
10608           {
10609             if (i < ping_num_trans)
10610               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10611                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10612                       (int) i,
10613                       (int) palette[i].red,
10614                       (int) palette[i].green,
10615                       (int) palette[i].blue,
10616                       (int) i,
10617                       (int) ping_trans_alpha[i]);
10618              else
10619               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10620                 "     PLTE[%d] = (%d,%d,%d)",
10621                       (int) i,
10622                       (int) palette[i].red,
10623                       (int) palette[i].green,
10624                       (int) palette[i].blue);
10625            }
10626          }
10627     }
10628
10629   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10630   if (ping_exclude_sRGB != MagickFalse ||
10631      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10632   {
10633     if ((ping_exclude_tEXt == MagickFalse ||
10634        ping_exclude_zTXt == MagickFalse) &&
10635        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10636     {
10637       ResetImageProfileIterator(image);
10638       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10639       {
10640         profile=GetImageProfile(image,name);
10641
10642         if (profile != (StringInfo *) NULL)
10643           {
10644 #ifdef PNG_WRITE_iCCP_SUPPORTED
10645             if ((LocaleCompare(name,"ICC") == 0) ||
10646                 (LocaleCompare(name,"ICM") == 0))
10647              {
10648
10649                if (ping_exclude_iCCP == MagickFalse)
10650                  {
10651                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10652                           "  Setting up iCCP chunk");
10653     
10654                        png_set_iCCP(ping,ping_info,(png_charp) name,0,
10655 #if (PNG_LIBPNG_VER < 10500)
10656                          (png_charp) GetStringInfoDatum(profile),
10657 #else
10658                          (png_const_bytep) GetStringInfoDatum(profile),
10659 #endif
10660                          (png_uint_32) GetStringInfoLength(profile));
10661                        ping_have_iCCP = MagickTrue;
10662                  }
10663              }
10664
10665             else
10666 #endif
10667               if (ping_exclude_zCCP == MagickFalse)
10668                 {
10669                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10670                       "  Setting up zTXT chunk with uuencoded ICC");
10671                   Magick_png_write_raw_profile(image_info,ping,ping_info,
10672                     (unsigned char *) name,(unsigned char *) name,
10673                     GetStringInfoDatum(profile),
10674                     (png_uint_32) GetStringInfoLength(profile));
10675                   ping_have_iCCP = MagickTrue;
10676                 }
10677           }
10678
10679           if (logging != MagickFalse)
10680             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10681               "  Setting up text chunk with %s profile",name);
10682
10683         name=GetNextImageProfile(image);
10684       }
10685     }
10686   }
10687
10688 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10689   if ((mng_info->have_write_global_srgb == 0) &&
10690       ping_have_iCCP != MagickTrue &&
10691       (ping_have_sRGB != MagickFalse ||
10692       png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10693     {
10694       if (ping_exclude_sRGB == MagickFalse)
10695         {
10696           /*
10697             Note image rendering intent.
10698           */
10699           if (logging != MagickFalse)
10700             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10701                 "  Setting up sRGB chunk");
10702
10703           (void) png_set_sRGB(ping,ping_info,(
10704             Magick_RenderingIntent_to_PNG_RenderingIntent(
10705               image->rendering_intent)));
10706
10707           ping_have_sRGB = MagickTrue;
10708         }
10709     }
10710
10711   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10712 #endif
10713     {
10714       if (ping_exclude_gAMA == MagickFalse &&
10715           ping_have_iCCP == MagickFalse &&
10716           ping_have_sRGB == MagickFalse &&
10717           (ping_exclude_sRGB == MagickFalse ||
10718           (image->gamma < .45 || image->gamma > .46)))
10719       {
10720       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10721         {
10722           /*
10723             Note image gamma.
10724             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10725           */
10726           if (logging != MagickFalse)
10727             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10728               "  Setting up gAMA chunk");
10729
10730           png_set_gAMA(ping,ping_info,image->gamma);
10731         }
10732       }
10733
10734       if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10735         {
10736           if ((mng_info->have_write_global_chrm == 0) &&
10737               (image->chromaticity.red_primary.x != 0.0))
10738             {
10739               /*
10740                 Note image chromaticity.
10741                 Note: if cHRM+gAMA == sRGB write sRGB instead.
10742               */
10743                PrimaryInfo
10744                  bp,
10745                  gp,
10746                  rp,
10747                  wp;
10748
10749                wp=image->chromaticity.white_point;
10750                rp=image->chromaticity.red_primary;
10751                gp=image->chromaticity.green_primary;
10752                bp=image->chromaticity.blue_primary;
10753
10754                if (logging != MagickFalse)
10755                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10756                    "  Setting up cHRM chunk");
10757
10758                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10759                    bp.x,bp.y);
10760            }
10761         }
10762     }
10763
10764   if (ping_exclude_bKGD == MagickFalse)
10765     {
10766       if (ping_have_bKGD != MagickFalse)
10767         {
10768           png_set_bKGD(ping,ping_info,&ping_background);
10769           if (logging)
10770             {
10771               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10772                    "    Setting up bKGD chunk");
10773               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10774                    "      background color = (%d,%d,%d)",
10775                         (int) ping_background.red,
10776                         (int) ping_background.green,
10777                         (int) ping_background.blue);
10778               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10779                    "      index = %d, gray=%d",
10780                         (int) ping_background.index,
10781                         (int) ping_background.gray);
10782             }
10783          }
10784     }
10785
10786   if (ping_exclude_pHYs == MagickFalse)
10787     {
10788       if (ping_have_pHYs != MagickFalse)
10789         {
10790           png_set_pHYs(ping,ping_info,
10791              ping_pHYs_x_resolution,
10792              ping_pHYs_y_resolution,
10793              ping_pHYs_unit_type);
10794
10795           if (logging)
10796             {
10797               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10798                    "    Setting up pHYs chunk");
10799               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10800                    "      x_resolution=%lu",
10801                    (unsigned long) ping_pHYs_x_resolution);
10802               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10803                    "      y_resolution=%lu",
10804                    (unsigned long) ping_pHYs_y_resolution);
10805               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10806                    "      unit_type=%lu",
10807                    (unsigned long) ping_pHYs_unit_type);
10808             }
10809         }
10810     }
10811
10812 #if defined(PNG_oFFs_SUPPORTED)
10813   if (ping_exclude_oFFs == MagickFalse)
10814     {
10815       if (image->page.x || image->page.y)
10816         {
10817            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10818               (png_int_32) image->page.y, 0);
10819
10820            if (logging != MagickFalse)
10821              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10822                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
10823                  (int) image->page.x, (int) image->page.y);
10824         }
10825     }
10826 #endif
10827
10828   if (mng_info->need_blob != MagickFalse)
10829   {
10830     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10831        MagickFalse)
10832        png_error(ping,"WriteBlob Failed");
10833
10834      ping_have_blob=MagickTrue;
10835   }
10836
10837   png_write_info_before_PLTE(ping, ping_info);
10838
10839   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10840     {
10841       if (logging != MagickFalse)
10842         {
10843           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10844               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10845         }
10846
10847       if (ping_color_type == 3)
10848          (void) png_set_tRNS(ping, ping_info,
10849                 ping_trans_alpha,
10850                 ping_num_trans,
10851                 NULL);
10852
10853       else
10854         {
10855            (void) png_set_tRNS(ping, ping_info,
10856                   NULL,
10857                   0,
10858                   &ping_trans_color);
10859
10860            if (logging != MagickFalse)
10861              {
10862                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10863                  "     tRNS color   =(%d,%d,%d)",
10864                        (int) ping_trans_color.red,
10865                        (int) ping_trans_color.green,
10866                        (int) ping_trans_color.blue);
10867              }
10868          }
10869     }
10870
10871   /* write any png-chunk-b profiles */
10872   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10873
10874   png_write_info(ping,ping_info);
10875
10876   /* write any PNG-chunk-m profiles */
10877   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10878
10879   if (ping_exclude_vpAg == MagickFalse)
10880     {
10881       if ((image->page.width != 0 && image->page.width != image->columns) ||
10882           (image->page.height != 0 && image->page.height != image->rows))
10883         {
10884           unsigned char
10885             chunk[14];
10886
10887           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
10888           PNGType(chunk,mng_vpAg);
10889           LogPNGChunk(logging,mng_vpAg,9L);
10890           PNGLong(chunk+4,(png_uint_32) image->page.width);
10891           PNGLong(chunk+8,(png_uint_32) image->page.height);
10892           chunk[12]=0;   /* unit = pixels */
10893           (void) WriteBlob(image,13,chunk);
10894           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10895         }
10896     }
10897
10898 #if (PNG_LIBPNG_VER == 10206)
10899     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10900 #define PNG_HAVE_IDAT               0x04
10901     ping->mode |= PNG_HAVE_IDAT;
10902 #undef PNG_HAVE_IDAT
10903 #endif
10904
10905   png_set_packing(ping);
10906   /*
10907     Allocate memory.
10908   */
10909   rowbytes=image->columns;
10910   if (image_depth > 8)
10911     rowbytes*=2;
10912   switch (ping_color_type)
10913     {
10914       case PNG_COLOR_TYPE_RGB:
10915         rowbytes*=3;
10916         break;
10917
10918       case PNG_COLOR_TYPE_GRAY_ALPHA:
10919         rowbytes*=2;
10920         break;
10921
10922       case PNG_COLOR_TYPE_RGBA:
10923         rowbytes*=4;
10924         break;
10925
10926       default:
10927         break;
10928     }
10929
10930   if (logging != MagickFalse)
10931     {
10932       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10933         "  Writing PNG image data");
10934
10935       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10936         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10937     }
10938   pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10939   if (pixel_info == (MemoryInfo *) NULL)
10940     png_error(ping,"Allocation of memory for pixels failed");
10941   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
10942
10943   /*
10944     Initialize image scanlines.
10945   */
10946   quantum_info=AcquireQuantumInfo(image_info,image);
10947   if (quantum_info == (QuantumInfo *) NULL)
10948     png_error(ping,"Memory allocation for quantum_info failed");
10949   quantum_info->format=UndefinedQuantumFormat;
10950   quantum_info->depth=image_depth;
10951   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10952   num_passes=png_set_interlace_handling(ping);
10953
10954   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10955        !mng_info->write_png48 && !mng_info->write_png64 &&
10956        !mng_info->write_png32) &&
10957        (mng_info->IsPalette ||
10958        (image_info->type == BilevelType)) &&
10959        image_matte == MagickFalse &&
10960        ping_have_non_bw == MagickFalse)
10961     {
10962       /* Palette, Bilevel, or Opaque Monochrome */
10963       register const Quantum
10964         *p;
10965
10966       quantum_info->depth=8;
10967       for (pass=0; pass < num_passes; pass++)
10968       {
10969         /*
10970           Convert PseudoClass image to a PNG monochrome image.
10971         */
10972         for (y=0; y < (ssize_t) image->rows; y++)
10973         {
10974           if (logging != MagickFalse && y == 0)
10975              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10976                  "    Writing row of pixels (0)");
10977
10978           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10979
10980           if (p == (const Quantum *) NULL)
10981             break;
10982
10983           if (mng_info->IsPalette)
10984             {
10985               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10986                 quantum_info,GrayQuantum,ping_pixels,exception);
10987               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10988                   mng_info->write_png_depth &&
10989                   mng_info->write_png_depth != old_bit_depth)
10990                 {
10991                   /* Undo pixel scaling */
10992                   for (i=0; i < (ssize_t) image->columns; i++)
10993                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10994                      >> (8-old_bit_depth));
10995                 }
10996             }
10997
10998           else
10999             {
11000               (void) ExportQuantumPixels(image,(CacheView *) NULL,
11001                 quantum_info,RedQuantum,ping_pixels,exception);
11002             }
11003
11004           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11005             for (i=0; i < (ssize_t) image->columns; i++)
11006                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11007                       255 : 0);
11008
11009           if (logging != MagickFalse && y == 0)
11010             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11011                 "    Writing row of pixels (1)");
11012
11013           png_write_row(ping,ping_pixels);
11014         }
11015         if (image->previous == (Image *) NULL)
11016           {
11017             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11018             if (status == MagickFalse)
11019               break;
11020           }
11021       }
11022     }
11023
11024   else   /* Not Palette, Bilevel, or Opaque Monochrome */
11025     {
11026       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11027           !mng_info->write_png48 && !mng_info->write_png64 &&
11028           !mng_info->write_png32) && (image_matte != MagickFalse ||
11029           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11030           (mng_info->IsPalette) && ping_have_color == MagickFalse)
11031         {
11032           register const Quantum
11033             *p;
11034
11035           for (pass=0; pass < num_passes; pass++)
11036           {
11037
11038           for (y=0; y < (ssize_t) image->rows; y++)
11039           {
11040             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11041
11042             if (p == (const Quantum *) NULL)
11043               break;
11044
11045             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11046               {
11047                 if (mng_info->IsPalette)
11048                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11049                     quantum_info,GrayQuantum,ping_pixels,exception);
11050
11051                 else
11052                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11053                     quantum_info,RedQuantum,ping_pixels,exception);
11054
11055                 if (logging != MagickFalse && y == 0)
11056                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11057                        "    Writing GRAY PNG pixels (2)");
11058               }
11059
11060             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11061               {
11062                 if (logging != MagickFalse && y == 0)
11063                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11064                          "    Writing GRAY_ALPHA PNG pixels (2)");
11065
11066                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11067                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11068               }
11069
11070             if (logging != MagickFalse && y == 0)
11071               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11072                   "    Writing row of pixels (2)");
11073
11074             png_write_row(ping,ping_pixels);
11075           }
11076
11077           if (image->previous == (Image *) NULL)
11078             {
11079               status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11080               if (status == MagickFalse)
11081                 break;
11082             }
11083           }
11084         }
11085
11086       else
11087         {
11088           register const Quantum
11089             *p;
11090
11091           for (pass=0; pass < num_passes; pass++)
11092           {
11093             if ((image_depth > 8) ||
11094                 mng_info->write_png24 ||
11095                 mng_info->write_png32 ||
11096                 mng_info->write_png48 ||
11097                 mng_info->write_png64 ||
11098                 (!mng_info->write_png8 && !mng_info->IsPalette))
11099             {
11100               for (y=0; y < (ssize_t) image->rows; y++)
11101               {
11102                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11103
11104                 if (p == (const Quantum *) NULL)
11105                   break;
11106
11107                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11108                   {
11109                     if (image->storage_class == DirectClass)
11110                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11111                         quantum_info,RedQuantum,ping_pixels,exception);
11112
11113                     else
11114                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11115                         quantum_info,GrayQuantum,ping_pixels,exception);
11116                   }
11117
11118                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11119                   {
11120                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11121                       quantum_info,GrayAlphaQuantum,ping_pixels,
11122                       exception);
11123
11124                     if (logging != MagickFalse && y == 0)
11125                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11126                            "    Writing GRAY_ALPHA PNG pixels (3)");
11127                   }
11128
11129                 else if (image_matte != MagickFalse)
11130                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11131                     quantum_info,RGBAQuantum,ping_pixels,exception);
11132
11133                 else
11134                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11135                     quantum_info,RGBQuantum,ping_pixels,exception);
11136
11137                 if (logging != MagickFalse && y == 0)
11138                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11139                       "    Writing row of pixels (3)");
11140
11141                 png_write_row(ping,ping_pixels);
11142               }
11143             }
11144
11145           else
11146             /* not ((image_depth > 8) ||
11147                 mng_info->write_png24 || mng_info->write_png32 ||
11148                 mng_info->write_png48 || mng_info->write_png64 ||
11149                 (!mng_info->write_png8 && !mng_info->IsPalette))
11150              */
11151             {
11152               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11153                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11154                 {
11155                   if (logging != MagickFalse)
11156                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11157                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11158
11159                   quantum_info->depth=8;
11160                   image_depth=8;
11161                 }
11162
11163               for (y=0; y < (ssize_t) image->rows; y++)
11164               {
11165                 if (logging != MagickFalse && y == 0)
11166                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11167                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
11168
11169                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11170
11171                 if (p == (const Quantum *) NULL)
11172                   break;
11173
11174                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11175                   {
11176                     quantum_info->depth=image->depth;
11177
11178                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11179                        quantum_info,GrayQuantum,ping_pixels,exception);
11180                   }
11181
11182                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11183                   {
11184                     if (logging != MagickFalse && y == 0)
11185                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11186                            "  Writing GRAY_ALPHA PNG pixels (4)");
11187
11188                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11189                          quantum_info,GrayAlphaQuantum,ping_pixels,
11190                          exception);
11191                   }
11192
11193                 else
11194                   {
11195                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11196                       quantum_info,IndexQuantum,ping_pixels,exception);
11197
11198                     if (logging != MagickFalse && y <= 2)
11199                     {
11200                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11201                           "  Writing row of non-gray pixels (4)");
11202
11203                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11204                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
11205                           (int)ping_pixels[0],(int)ping_pixels[1]);
11206                     }
11207                   }
11208                 png_write_row(ping,ping_pixels);
11209               }
11210             }
11211
11212             if (image->previous == (Image *) NULL)
11213               {
11214                 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11215                 if (status == MagickFalse)
11216                   break;
11217               }
11218           }
11219         }
11220     }
11221
11222   if (quantum_info != (QuantumInfo *) NULL)
11223     quantum_info=DestroyQuantumInfo(quantum_info);
11224
11225   if (logging != MagickFalse)
11226     {
11227       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11228         "  Wrote PNG image data");
11229
11230       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11231         "    Width: %.20g",(double) ping_width);
11232
11233       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11234         "    Height: %.20g",(double) ping_height);
11235
11236       if (mng_info->write_png_depth)
11237         {
11238           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11239             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
11240         }
11241
11242       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11243         "    PNG bit-depth written: %d",ping_bit_depth);
11244
11245       if (mng_info->write_png_colortype)
11246         {
11247           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11248             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
11249         }
11250
11251       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11252         "    PNG color-type written: %d",ping_color_type);
11253
11254       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11255         "    PNG Interlace method: %d",ping_interlace_method);
11256     }
11257   /*
11258     Generate text chunks after IDAT.
11259   */
11260   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11261   {
11262     ResetImagePropertyIterator(image);
11263     property=GetNextImageProperty(image);
11264     while (property != (const char *) NULL)
11265     {
11266       png_textp
11267         text;
11268
11269       value=GetImageProperty(image,property,exception);
11270
11271       /* Don't write any "png:" or "jpeg:" properties; those are just for
11272        * "identify" or for passing through to another JPEG
11273        */
11274       if ((LocaleNCompare(property,"png:",4) != 0 &&
11275            LocaleNCompare(property,"jpeg:",5) != 0) &&
11276
11277
11278           /* Suppress density and units if we wrote a pHYs chunk */
11279           (ping_exclude_pHYs != MagickFalse      ||
11280           LocaleCompare(property,"density") != 0 ||
11281           LocaleCompare(property,"units") != 0) &&
11282
11283           /* Suppress the IM-generated Date:create and Date:modify */
11284           (ping_exclude_date == MagickFalse      ||
11285           LocaleNCompare(property, "Date:",5) != 0))
11286         {
11287         if (value != (const char *) NULL)
11288           {
11289
11290 #if PNG_LIBPNG_VER >= 10400
11291             text=(png_textp) png_malloc(ping,
11292                  (png_alloc_size_t) sizeof(png_text));
11293 #else
11294             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11295 #endif
11296             text[0].key=(char *) property;
11297             text[0].text=(char *) value;
11298             text[0].text_length=strlen(value);
11299
11300             if (ping_exclude_tEXt != MagickFalse)
11301                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11302
11303             else if (ping_exclude_zTXt != MagickFalse)
11304                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11305
11306             else
11307             {
11308                text[0].compression=image_info->compression == NoCompression ||
11309                  (image_info->compression == UndefinedCompression &&
11310                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11311                  PNG_TEXT_COMPRESSION_zTXt ;
11312             }
11313
11314             if (logging != MagickFalse)
11315               {
11316                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11317                   "  Setting up text chunk");
11318
11319                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11320                   "    keyword: '%s'",text[0].key);
11321               }
11322
11323             png_set_text(ping,ping_info,text,1);
11324             png_free(ping,text);
11325           }
11326         }
11327       property=GetNextImageProperty(image);
11328     }
11329   }
11330
11331   /* write any PNG-chunk-e profiles */
11332   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11333
11334   if (logging != MagickFalse)
11335     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11336       "  Writing PNG end info");
11337
11338   png_write_end(ping,ping_info);
11339
11340   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11341     {
11342       if (mng_info->page.x || mng_info->page.y ||
11343           (ping_width != mng_info->page.width) ||
11344           (ping_height != mng_info->page.height))
11345         {
11346           unsigned char
11347             chunk[32];
11348
11349           /*
11350             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11351           */
11352           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11353           PNGType(chunk,mng_FRAM);
11354           LogPNGChunk(logging,mng_FRAM,27L);
11355           chunk[4]=4;
11356           chunk[5]=0;  /* frame name separator (no name) */
11357           chunk[6]=1;  /* flag for changing delay, for next frame only */
11358           chunk[7]=0;  /* flag for changing frame timeout */
11359           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11360           chunk[9]=0;  /* flag for changing frame sync_id */
11361           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11362           chunk[14]=0; /* clipping boundaries delta type */
11363           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11364           PNGLong(chunk+19,
11365              (png_uint_32) (mng_info->page.x + ping_width));
11366           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11367           PNGLong(chunk+27,
11368              (png_uint_32) (mng_info->page.y + ping_height));
11369           (void) WriteBlob(image,31,chunk);
11370           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11371           mng_info->old_framing_mode=4;
11372           mng_info->framing_mode=1;
11373         }
11374
11375       else
11376         mng_info->framing_mode=3;
11377     }
11378   if (mng_info->write_mng && !mng_info->need_fram &&
11379       ((int) image->dispose == 3))
11380      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11381
11382   /*
11383     Free PNG resources.
11384   */
11385
11386   png_destroy_write_struct(&ping,&ping_info);
11387
11388   pixel_info=RelinquishVirtualMemory(pixel_info);
11389
11390   if (ping_have_blob != MagickFalse)
11391      (void) CloseBlob(image);
11392
11393   image_info=DestroyImageInfo(image_info);
11394   image=DestroyImage(image);
11395
11396   /* Store bit depth actually written */
11397   s[0]=(char) ping_bit_depth;
11398   s[1]='\0';
11399
11400   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11401
11402   if (logging != MagickFalse)
11403     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11404       "  exit WriteOnePNGImage()");
11405
11406 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11407   UnlockSemaphoreInfo(ping_semaphore);
11408 #endif
11409
11410    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11411     *    Throwing an Exception when an error occurs.
11412     */
11413
11414   return(MagickTrue);
11415 /*  End write one PNG image */
11416
11417 }
11418
11419 /*
11420 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11421 %                                                                             %
11422 %                                                                             %
11423 %                                                                             %
11424 %   W r i t e P N G I m a g e                                                 %
11425 %                                                                             %
11426 %                                                                             %
11427 %                                                                             %
11428 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11429 %
11430 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11431 %  Multiple-image Network Graphics (MNG) image file.
11432 %
11433 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11434 %
11435 %  The format of the WritePNGImage method is:
11436 %
11437 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11438 %        Image *image,ExceptionInfo *exception)
11439 %
11440 %  A description of each parameter follows:
11441 %
11442 %    o image_info: the image info.
11443 %
11444 %    o image:  The image.
11445 %
11446 %    o exception: return any errors or warnings in this structure.
11447 %
11448 %  Returns MagickTrue on success, MagickFalse on failure.
11449 %
11450 %  Communicating with the PNG encoder:
11451 %
11452 %  While the datastream written is always in PNG format and normally would
11453 %  be given the "png" file extension, this method also writes the following
11454 %  pseudo-formats which are subsets of png:
11455 %
11456 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11457 %               a depth greater than 8, the depth is reduced. If transparency
11458 %               is present, the tRNS chunk must only have values 0 and 255
11459 %               (i.e., transparency is binary: fully opaque or fully
11460 %               transparent).  If other values are present they will be
11461 %               50%-thresholded to binary transparency.  If more than 256
11462 %               colors are present, they will be quantized to the 4-4-4-1,
11463 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11464 %               of any resulting fully-transparent pixels is changed to
11465 %               the image's background color.
11466 %
11467 %               If you want better quantization or dithering of the colors
11468 %               or alpha than that, you need to do it before calling the
11469 %               PNG encoder. The pixels contain 8-bit indices even if
11470 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11471 %               images will be written as indexed PNG files even though the
11472 %               PNG grayscale type might be slightly more efficient.  Please
11473 %               note that writing to the PNG8 format may result in loss
11474 %               of color and alpha data.
11475 %
11476 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11477 %               chunk can be present to convey binary transparency by naming
11478 %               one of the colors as transparent.  The only loss incurred
11479 %               is reduction of sample depth to 8.  If the image has more
11480 %               than one transparent color, has semitransparent pixels, or
11481 %               has an opaque pixel with the same RGB components as the
11482 %               transparent color, an image is not written.
11483 %
11484 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
11485 %               transparency is permitted, i.e., the alpha sample for
11486 %               each pixel can have any value from 0 to 255. The alpha
11487 %               channel is present even if the image is fully opaque.
11488 %               The only loss in data is the reduction of the sample depth
11489 %               to 8.
11490 %
11491 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
11492 %               chunk can be present to convey binary transparency by naming
11493 %               one of the colors as transparent.  If the image has more
11494 %               than one transparent color, has semitransparent pixels, or
11495 %               has an opaque pixel with the same RGB components as the
11496 %               transparent color, an image is not written.
11497 %
11498 %    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
11499 %               transparency is permitted, i.e., the alpha sample for
11500 %               each pixel can have any value from 0 to 65535. The alpha
11501 %               channel is present even if the image is fully opaque.
11502 %
11503 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
11504 %               image, if the input was a PNG, is written.  If these values
11505 %               cannot be found, then "PNG00" falls back to the regular "PNG"
11506 %               format.
11507 %
11508 %    o -define: For more precise control of the PNG output, you can use the
11509 %               Image options "png:bit-depth" and "png:color-type".  These
11510 %               can be set from the commandline with "-define" and also
11511 %               from the application programming interfaces.  The options
11512 %               are case-independent and are converted to lowercase before
11513 %               being passed to this encoder.
11514 %
11515 %               png:color-type can be 0, 2, 3, 4, or 6.
11516 %
11517 %               When png:color-type is 0 (Grayscale), png:bit-depth can
11518 %               be 1, 2, 4, 8, or 16.
11519 %
11520 %               When png:color-type is 2 (RGB), png:bit-depth can
11521 %               be 8 or 16.
11522 %
11523 %               When png:color-type is 3 (Indexed), png:bit-depth can
11524 %               be 1, 2, 4, or 8.  This refers to the number of bits
11525 %               used to store the index.  The color samples always have
11526 %               bit-depth 8 in indexed PNG files.
11527 %
11528 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11529 %               png:bit-depth can be 8 or 16.
11530 %
11531 %               If the image cannot be written without loss with the
11532 %               requested bit-depth and color-type, a PNG file will not
11533 %               be written, a warning will be issued, and the encoder will
11534 %               return MagickFalse.
11535 %
11536 %  Since image encoders should not be responsible for the "heavy lifting",
11537 %  the user should make sure that ImageMagick has already reduced the
11538 %  image depth and number of colors and limit transparency to binary
11539 %  transparency prior to attempting to write the image with depth, color,
11540 %  or transparency limitations.
11541 %
11542 %  Note that another definition, "png:bit-depth-written" exists, but it
11543 %  is not intended for external use.  It is only used internally by the
11544 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11545 %
11546 %  It is possible to request that the PNG encoder write previously-formatted
11547 %  ancillary chunks in the output PNG file, using the "-profile" commandline
11548 %  option as shown below or by setting the profile via a programming
11549 %  interface:
11550 %
11551 %     -profile PNG-chunk-x:<file>
11552 %
11553 %  where x is a location flag and <file> is a file containing the chunk
11554 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11555 %  This encoder will compute the chunk length and CRC, so those must not
11556 %  be included in the file.
11557 %
11558 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11559 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
11560 %  of the same type, then add a short unique string after the "x" to prevent
11561 %  subsequent profiles from overwriting the preceding ones, e.g.,
11562 %
11563 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11564 %
11565 %  As of version 6.6.6 the following optimizations are always done:
11566 %
11567 %   o  32-bit depth is reduced to 16.
11568 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
11569 %      high byte and low byte are identical.
11570 %   o  Palette is sorted to remove unused entries and to put a
11571 %      transparent color first, if BUILD_PNG_PALETTE is defined.
11572 %   o  Opaque matte channel is removed when writing an indexed PNG.
11573 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
11574 %      this can be done without loss and a larger bit depth N was not
11575 %      requested via the "-define png:bit-depth=N" option.
11576 %   o  If matte channel is present but only one transparent color is
11577 %      present, RGB+tRNS is written instead of RGBA
11578 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
11579 %      was requested when converting an opaque image).
11580 %
11581 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11582 */
11583 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11584   Image *image,ExceptionInfo *exception)
11585 {
11586   MagickBooleanType
11587     excluding,
11588     logging,
11589     have_mng_structure,
11590     status;
11591
11592   MngInfo
11593     *mng_info;
11594
11595   const char
11596     *value;
11597
11598   int
11599     i,
11600     source;
11601
11602   /*
11603     Open image file.
11604   */
11605   assert(image_info != (const ImageInfo *) NULL);
11606   assert(image_info->signature == MagickSignature);
11607   assert(image != (Image *) NULL);
11608   assert(image->signature == MagickSignature);
11609   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11610   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11611   /*
11612     Allocate a MngInfo structure.
11613   */
11614   have_mng_structure=MagickFalse;
11615   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11616
11617   if (mng_info == (MngInfo *) NULL)
11618     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11619
11620   /*
11621     Initialize members of the MngInfo structure.
11622   */
11623   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11624   mng_info->image=image;
11625   mng_info->equal_backgrounds=MagickTrue;
11626   have_mng_structure=MagickTrue;
11627
11628   /* See if user has requested a specific PNG subformat */
11629
11630   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11631   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11632   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11633   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11634   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11635
11636   value=GetImageOption(image_info,"png:format");
11637
11638   if (value != (char *) NULL)
11639     {
11640       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11641          "  Format=%s",value);
11642
11643       mng_info->write_png8 = MagickFalse;
11644       mng_info->write_png24 = MagickFalse;
11645       mng_info->write_png32 = MagickFalse;
11646       mng_info->write_png48 = MagickFalse;
11647       mng_info->write_png64 = MagickFalse;
11648
11649       if (LocaleCompare(value,"png8") == 0)
11650         mng_info->write_png8 = MagickTrue;
11651
11652       else if (LocaleCompare(value,"png24") == 0)
11653         mng_info->write_png24 = MagickTrue;
11654
11655       else if (LocaleCompare(value,"png32") == 0)
11656         mng_info->write_png32 = MagickTrue;
11657
11658       else if (LocaleCompare(value,"png48") == 0)
11659         mng_info->write_png48 = MagickTrue;
11660
11661       else if (LocaleCompare(value,"png64") == 0)
11662         mng_info->write_png64 = MagickTrue;
11663
11664       else if (LocaleCompare(value,"png00") == 0)
11665         {
11666           /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11667           value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11668     
11669           if (value != (char *) NULL)
11670             {
11671               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11672                  "  png00 inherited bit depth=%s",value);
11673     
11674               if (LocaleCompare(value,"1") == 0)
11675                 mng_info->write_png_depth = 1;
11676     
11677               else if (LocaleCompare(value,"1") == 0)
11678                 mng_info->write_png_depth = 2;
11679     
11680               else if (LocaleCompare(value,"2") == 0)
11681                 mng_info->write_png_depth = 4;
11682     
11683               else if (LocaleCompare(value,"8") == 0)
11684                 mng_info->write_png_depth = 8;
11685     
11686               else if (LocaleCompare(value,"16") == 0)
11687                 mng_info->write_png_depth = 16;
11688             }
11689     
11690           value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11691     
11692           if (value != (char *) NULL)
11693             {
11694               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11695                  "  png00 inherited color type=%s",value);
11696     
11697               if (LocaleCompare(value,"0") == 0)
11698                 mng_info->write_png_colortype = 1;
11699     
11700               else if (LocaleCompare(value,"2") == 0)
11701                 mng_info->write_png_colortype = 3;
11702     
11703               else if (LocaleCompare(value,"3") == 0)
11704                 mng_info->write_png_colortype = 4;
11705     
11706               else if (LocaleCompare(value,"4") == 0)
11707                 mng_info->write_png_colortype = 5;
11708     
11709               else if (LocaleCompare(value,"6") == 0)
11710                 mng_info->write_png_colortype = 7;
11711             }
11712         }
11713     }
11714
11715   if (mng_info->write_png8)
11716     {
11717       mng_info->write_png_colortype = /* 3 */ 4;
11718       mng_info->write_png_depth = 8;
11719       image->depth = 8;
11720     }
11721
11722   if (mng_info->write_png24)
11723     {
11724       mng_info->write_png_colortype = /* 2 */ 3;
11725       mng_info->write_png_depth = 8;
11726       image->depth = 8;
11727
11728       if (image->alpha_trait == BlendPixelTrait)
11729         (void) SetImageType(image,TrueColorMatteType,exception);
11730
11731       else
11732         (void) SetImageType(image,TrueColorType,exception);
11733
11734       (void) SyncImage(image,exception);
11735     }
11736
11737   if (mng_info->write_png32)
11738     {
11739       mng_info->write_png_colortype = /* 6 */  7;
11740       mng_info->write_png_depth = 8;
11741       image->depth = 8;
11742
11743       if (image->alpha_trait == BlendPixelTrait)
11744         (void) SetImageType(image,TrueColorMatteType,exception);
11745
11746       else
11747         (void) SetImageType(image,TrueColorType,exception);
11748
11749       (void) SyncImage(image,exception);
11750     }
11751
11752   if (mng_info->write_png48)
11753     {
11754       mng_info->write_png_colortype = /* 2 */ 3;
11755       mng_info->write_png_depth = 16;
11756       image->depth = 16;
11757
11758       if (image->alpha_trait == BlendPixelTrait)
11759         (void) SetImageType(image,TrueColorMatteType,exception);
11760
11761       else
11762         (void) SetImageType(image,TrueColorType,exception);
11763
11764       (void) SyncImage(image,exception);
11765     }
11766
11767   if (mng_info->write_png64)
11768     {
11769       mng_info->write_png_colortype = /* 6 */  7;
11770       mng_info->write_png_depth = 16;
11771       image->depth = 16;
11772
11773       if (image->alpha_trait == BlendPixelTrait)
11774         (void) SetImageType(image,TrueColorMatteType,exception);
11775
11776       else
11777         (void) SetImageType(image,TrueColorType,exception);
11778
11779       (void) SyncImage(image,exception);
11780     }
11781
11782   value=GetImageOption(image_info,"png:bit-depth");
11783
11784   if (value != (char *) NULL)
11785     {
11786       if (LocaleCompare(value,"1") == 0)
11787         mng_info->write_png_depth = 1;
11788
11789       else if (LocaleCompare(value,"2") == 0)
11790         mng_info->write_png_depth = 2;
11791
11792       else if (LocaleCompare(value,"4") == 0)
11793         mng_info->write_png_depth = 4;
11794
11795       else if (LocaleCompare(value,"8") == 0)
11796         mng_info->write_png_depth = 8;
11797
11798       else if (LocaleCompare(value,"16") == 0)
11799         mng_info->write_png_depth = 16;
11800
11801       else
11802         (void) ThrowMagickException(exception,
11803              GetMagickModule(),CoderWarning,
11804              "ignoring invalid defined png:bit-depth",
11805              "=%s",value);
11806
11807       if (logging != MagickFalse)
11808         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11809           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11810     }
11811
11812   value=GetImageOption(image_info,"png:color-type");
11813
11814   if (value != (char *) NULL)
11815     {
11816       /* We must store colortype+1 because 0 is a valid colortype */
11817       if (LocaleCompare(value,"0") == 0)
11818         mng_info->write_png_colortype = 1;
11819
11820       else if (LocaleCompare(value,"1") == 0)
11821         mng_info->write_png_colortype = 2;
11822
11823       else if (LocaleCompare(value,"2") == 0)
11824         mng_info->write_png_colortype = 3;
11825
11826       else if (LocaleCompare(value,"3") == 0)
11827         mng_info->write_png_colortype = 4;
11828
11829       else if (LocaleCompare(value,"4") == 0)
11830         mng_info->write_png_colortype = 5;
11831
11832       else if (LocaleCompare(value,"6") == 0)
11833         mng_info->write_png_colortype = 7;
11834
11835       else
11836         (void) ThrowMagickException(exception,
11837              GetMagickModule(),CoderWarning,
11838              "ignoring invalid defined png:color-type",
11839              "=%s",value);
11840
11841       if (logging != MagickFalse)
11842         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11843           "  png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11844     }
11845
11846   /* Check for chunks to be excluded:
11847    *
11848    * The default is to not exclude any known chunks except for any
11849    * listed in the "unused_chunks" array, above.
11850    *
11851    * Chunks can be listed for exclusion via a "png:exclude-chunk"
11852    * define (in the image properties or in the image artifacts)
11853    * or via a mng_info member.  For convenience, in addition
11854    * to or instead of a comma-separated list of chunks, the
11855    * "exclude-chunk" string can be simply "all" or "none".
11856    *
11857    * The exclude-chunk define takes priority over the mng_info.
11858    *
11859    * A "png:include-chunk" define takes  priority over both the
11860    * mng_info and the "png:exclude-chunk" define.  Like the
11861    * "exclude-chunk" string, it can define "all" or "none" as
11862    * well as a comma-separated list.  Chunks that are unknown to
11863    * ImageMagick are always excluded, regardless of their "copy-safe"
11864    * status according to the PNG specification, and even if they
11865    * appear in the "include-chunk" list. Such defines appearing among
11866    * the image options take priority over those found among the image
11867    * artifacts.
11868    *
11869    * Finally, all chunks listed in the "unused_chunks" array are
11870    * automatically excluded, regardless of the other instructions
11871    * or lack thereof.
11872    *
11873    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11874    * will not be written and the gAMA chunk will only be written if it
11875    * is not between .45 and .46, or approximately (1.0/2.2).
11876    *
11877    * If you exclude tRNS and the image has transparency, the colortype
11878    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11879    *
11880    * The -strip option causes StripImage() to set the png:include-chunk
11881    * artifact to "none,trns,gama".
11882    */
11883
11884   mng_info->ping_exclude_bKGD=MagickFalse;
11885   mng_info->ping_exclude_cHRM=MagickFalse;
11886   mng_info->ping_exclude_date=MagickFalse;
11887   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11888   mng_info->ping_exclude_gAMA=MagickFalse;
11889   mng_info->ping_exclude_iCCP=MagickFalse;
11890   /* mng_info->ping_exclude_iTXt=MagickFalse; */
11891   mng_info->ping_exclude_oFFs=MagickFalse;
11892   mng_info->ping_exclude_pHYs=MagickFalse;
11893   mng_info->ping_exclude_sRGB=MagickFalse;
11894   mng_info->ping_exclude_tEXt=MagickFalse;
11895   mng_info->ping_exclude_tRNS=MagickFalse;
11896   mng_info->ping_exclude_vpAg=MagickFalse;
11897   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11898   mng_info->ping_exclude_zTXt=MagickFalse;
11899
11900   mng_info->ping_preserve_colormap=MagickFalse;
11901
11902   value=GetImageOption(image_info,"png:preserve-colormap");
11903   if (value == NULL)
11904      value=GetImageArtifact(image,"png:preserve-colormap");
11905   if (value != NULL)
11906      mng_info->ping_preserve_colormap=MagickTrue;
11907
11908   mng_info->ping_preserve_iCCP=MagickFalse;
11909
11910   value=GetImageOption(image_info,"png:preserve-iCCP");
11911   if (value == NULL)
11912      value=GetImageArtifact(image,"png:preserve-iCCP");
11913   if (value != NULL)
11914      mng_info->ping_preserve_iCCP=MagickTrue;
11915
11916   /* These compression-level, compression-strategy, and compression-filter
11917    * defines take precedence over values from the -quality option.
11918    */
11919   value=GetImageOption(image_info,"png:compression-level");
11920   if (value == NULL)
11921      value=GetImageArtifact(image,"png:compression-level");
11922   if (value != NULL)
11923   {
11924       /* We have to add 1 to everything because 0 is a valid input,
11925        * and we want to use 0 (the default) to mean undefined.
11926        */
11927       if (LocaleCompare(value,"0") == 0)
11928         mng_info->write_png_compression_level = 1;
11929
11930       else if (LocaleCompare(value,"1") == 0)
11931         mng_info->write_png_compression_level = 2;
11932
11933       else if (LocaleCompare(value,"2") == 0)
11934         mng_info->write_png_compression_level = 3;
11935
11936       else if (LocaleCompare(value,"3") == 0)
11937         mng_info->write_png_compression_level = 4;
11938
11939       else if (LocaleCompare(value,"4") == 0)
11940         mng_info->write_png_compression_level = 5;
11941
11942       else if (LocaleCompare(value,"5") == 0)
11943         mng_info->write_png_compression_level = 6;
11944
11945       else if (LocaleCompare(value,"6") == 0)
11946         mng_info->write_png_compression_level = 7;
11947
11948       else if (LocaleCompare(value,"7") == 0)
11949         mng_info->write_png_compression_level = 8;
11950
11951       else if (LocaleCompare(value,"8") == 0)
11952         mng_info->write_png_compression_level = 9;
11953
11954       else if (LocaleCompare(value,"9") == 0)
11955         mng_info->write_png_compression_level = 10;
11956
11957       else
11958         (void) ThrowMagickException(exception,
11959              GetMagickModule(),CoderWarning,
11960              "ignoring invalid defined png:compression-level",
11961              "=%s",value);
11962     }
11963
11964   value=GetImageOption(image_info,"png:compression-strategy");
11965   if (value == NULL)
11966      value=GetImageArtifact(image,"png:compression-strategy");
11967   if (value != NULL)
11968   {
11969
11970       if (LocaleCompare(value,"0") == 0)
11971         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11972
11973       else if (LocaleCompare(value,"1") == 0)
11974         mng_info->write_png_compression_strategy = Z_FILTERED+1;
11975
11976       else if (LocaleCompare(value,"2") == 0)
11977         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11978
11979       else if (LocaleCompare(value,"3") == 0)
11980 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
11981         mng_info->write_png_compression_strategy = Z_RLE+1;
11982 #else
11983         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11984 #endif
11985
11986       else if (LocaleCompare(value,"4") == 0)
11987 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
11988         mng_info->write_png_compression_strategy = Z_FIXED+1;
11989 #else
11990         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11991 #endif
11992
11993       else
11994         (void) ThrowMagickException(exception,
11995              GetMagickModule(),CoderWarning,
11996              "ignoring invalid defined png:compression-strategy",
11997              "=%s",value);
11998     }
11999
12000   value=GetImageOption(image_info,"png:compression-filter");
12001   if (value == NULL)
12002      value=GetImageArtifact(image,"png:compression-filter");
12003   if (value != NULL)
12004   {
12005
12006       /* To do: combinations of filters allowed by libpng
12007        * masks 0x08 through 0xf8
12008        *
12009        * Implement this as a comma-separated list of 0,1,2,3,4,5
12010        * where 5 is a special case meaning PNG_ALL_FILTERS.
12011        */
12012
12013       if (LocaleCompare(value,"0") == 0)
12014         mng_info->write_png_compression_filter = 1;
12015
12016       else if (LocaleCompare(value,"1") == 0)
12017         mng_info->write_png_compression_filter = 2;
12018
12019       else if (LocaleCompare(value,"2") == 0)
12020         mng_info->write_png_compression_filter = 3;
12021
12022       else if (LocaleCompare(value,"3") == 0)
12023         mng_info->write_png_compression_filter = 4;
12024
12025       else if (LocaleCompare(value,"4") == 0)
12026         mng_info->write_png_compression_filter = 5;
12027
12028       else if (LocaleCompare(value,"5") == 0)
12029         mng_info->write_png_compression_filter = 6;
12030
12031       else
12032         (void) ThrowMagickException(exception,
12033              GetMagickModule(),CoderWarning,
12034              "ignoring invalid defined png:compression-filter",
12035              "=%s",value);
12036     }
12037
12038   excluding=MagickFalse;
12039
12040   for (source=0; source<1; source++)
12041   {
12042     if (source==0)
12043       {
12044        value=GetImageOption(image_info,"png:exclude-chunk");
12045
12046        if (value == NULL)
12047          value=GetImageArtifact(image,"png:exclude-chunk");
12048       }
12049     else
12050       {
12051        value=GetImageOption(image_info,"png:exclude-chunks");
12052
12053        if (value == NULL)
12054          value=GetImageArtifact(image,"png:exclude-chunks");
12055       }
12056
12057     if (value != NULL)
12058     {
12059
12060     size_t
12061       last;
12062
12063     excluding=MagickTrue;
12064
12065     if (logging != MagickFalse)
12066       {
12067         if (source == 0)
12068            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12069               "  png:exclude-chunk=%s found in image artifacts.\n", value);
12070         else
12071            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12072               "  png:exclude-chunk=%s found in image properties.\n", value);
12073       }
12074
12075     last=strlen(value);
12076
12077     for (i=0; i<(int) last; i+=5)
12078     {
12079
12080       if (LocaleNCompare(value+i,"none",4) == 0)
12081       {
12082         mng_info->ping_exclude_bKGD=MagickFalse;
12083         mng_info->ping_exclude_cHRM=MagickFalse;
12084         mng_info->ping_exclude_date=MagickFalse;
12085         mng_info->ping_exclude_EXIF=MagickFalse;
12086         mng_info->ping_exclude_gAMA=MagickFalse;
12087         mng_info->ping_exclude_iCCP=MagickFalse;
12088         /* mng_info->ping_exclude_iTXt=MagickFalse; */
12089         mng_info->ping_exclude_oFFs=MagickFalse;
12090         mng_info->ping_exclude_pHYs=MagickFalse;
12091         mng_info->ping_exclude_sRGB=MagickFalse;
12092         mng_info->ping_exclude_tEXt=MagickFalse;
12093         mng_info->ping_exclude_tRNS=MagickFalse;
12094         mng_info->ping_exclude_vpAg=MagickFalse;
12095         mng_info->ping_exclude_zCCP=MagickFalse;
12096         mng_info->ping_exclude_zTXt=MagickFalse;
12097       }
12098
12099       if (LocaleNCompare(value+i,"bkgd",4) == 0)
12100         mng_info->ping_exclude_bKGD=MagickTrue;
12101
12102       if (LocaleNCompare(value+i,"chrm",4) == 0)
12103         mng_info->ping_exclude_cHRM=MagickTrue;
12104
12105       if (LocaleNCompare(value+i,"date",4) == 0)
12106         mng_info->ping_exclude_date=MagickTrue;
12107
12108       if (LocaleNCompare(value+i,"exif",4) == 0)
12109         mng_info->ping_exclude_EXIF=MagickTrue;
12110
12111       if (LocaleNCompare(value+i,"gama",4) == 0)
12112         mng_info->ping_exclude_gAMA=MagickTrue;
12113
12114       if (LocaleNCompare(value+i,"iccp",4) == 0)
12115         mng_info->ping_exclude_iCCP=MagickTrue;
12116
12117     /*
12118       if (LocaleNCompare(value+i,"itxt",4) == 0)
12119         mng_info->ping_exclude_iTXt=MagickTrue;
12120      */
12121
12122       if (LocaleNCompare(value+i,"gama",4) == 0)
12123         mng_info->ping_exclude_gAMA=MagickTrue;
12124
12125       if (LocaleNCompare(value+i,"offs",4) == 0)
12126         mng_info->ping_exclude_oFFs=MagickTrue;
12127
12128       if (LocaleNCompare(value+i,"phys",4) == 0)
12129         mng_info->ping_exclude_pHYs=MagickTrue;
12130
12131       if (LocaleNCompare(value+i,"srgb",4) == 0)
12132         mng_info->ping_exclude_sRGB=MagickTrue;
12133
12134       if (LocaleNCompare(value+i,"text",4) == 0)
12135         mng_info->ping_exclude_tEXt=MagickTrue;
12136
12137       if (LocaleNCompare(value+i,"trns",4) == 0)
12138         mng_info->ping_exclude_tRNS=MagickTrue;
12139
12140       if (LocaleNCompare(value+i,"vpag",4) == 0)
12141         mng_info->ping_exclude_vpAg=MagickTrue;
12142
12143       if (LocaleNCompare(value+i,"zccp",4) == 0)
12144         mng_info->ping_exclude_zCCP=MagickTrue;
12145
12146       if (LocaleNCompare(value+i,"ztxt",4) == 0)
12147         mng_info->ping_exclude_zTXt=MagickTrue;
12148
12149       if (LocaleNCompare(value+i,"all",3) == 0)
12150       {
12151         mng_info->ping_exclude_bKGD=MagickTrue;
12152         mng_info->ping_exclude_cHRM=MagickTrue;
12153         mng_info->ping_exclude_date=MagickTrue;
12154         mng_info->ping_exclude_EXIF=MagickTrue;
12155         mng_info->ping_exclude_gAMA=MagickTrue;
12156         mng_info->ping_exclude_iCCP=MagickTrue;
12157         /* mng_info->ping_exclude_iTXt=MagickTrue; */
12158         mng_info->ping_exclude_oFFs=MagickTrue;
12159         mng_info->ping_exclude_pHYs=MagickTrue;
12160         mng_info->ping_exclude_sRGB=MagickTrue;
12161         mng_info->ping_exclude_tEXt=MagickTrue;
12162         mng_info->ping_exclude_tRNS=MagickTrue;
12163         mng_info->ping_exclude_vpAg=MagickTrue;
12164         mng_info->ping_exclude_zCCP=MagickTrue;
12165         mng_info->ping_exclude_zTXt=MagickTrue;
12166         i--;
12167       }
12168       }
12169     }
12170   }
12171
12172   for (source=0; source<1; source++)
12173   {
12174     if (source==0)
12175       {
12176        value=GetImageOption(image_info,"png:include-chunk");
12177
12178        if (value == NULL)
12179          value=GetImageArtifact(image,"png:include-chunk");
12180       }
12181     else
12182       {
12183        value=GetImageOption(image_info,"png:include-chunks");
12184
12185        if (value == NULL)
12186          value=GetImageArtifact(image,"png:include-chunks");
12187       }
12188
12189     if (value != NULL)
12190     {
12191     size_t
12192       last;
12193
12194     excluding=MagickTrue;
12195
12196     if (logging != MagickFalse)
12197       {
12198         if (source == 0)
12199            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12200               "  png:include-chunk=%s found in image artifacts.\n", value);
12201         else
12202            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12203               "  png:include-chunk=%s found in image properties.\n", value);
12204       }
12205
12206     last=strlen(value);
12207
12208     for (i=0; i<(int) last; i+=5)
12209       {
12210       if (LocaleNCompare(value+i,"none",4) == 0)
12211         {
12212           mng_info->ping_exclude_bKGD=MagickTrue;
12213           mng_info->ping_exclude_cHRM=MagickTrue;
12214           mng_info->ping_exclude_date=MagickTrue;
12215           mng_info->ping_exclude_EXIF=MagickTrue;
12216           mng_info->ping_exclude_gAMA=MagickTrue;
12217           mng_info->ping_exclude_iCCP=MagickTrue;
12218           /* mng_info->ping_exclude_iTXt=MagickTrue; */
12219           mng_info->ping_exclude_oFFs=MagickTrue;
12220           mng_info->ping_exclude_pHYs=MagickTrue;
12221           mng_info->ping_exclude_sRGB=MagickTrue;
12222           mng_info->ping_exclude_tEXt=MagickTrue;
12223           mng_info->ping_exclude_tRNS=MagickTrue;
12224           mng_info->ping_exclude_vpAg=MagickTrue;
12225           mng_info->ping_exclude_zCCP=MagickTrue;
12226           mng_info->ping_exclude_zTXt=MagickTrue;
12227         }
12228
12229       if (LocaleNCompare(value+i,"bkgd",4) == 0)
12230         mng_info->ping_exclude_bKGD=MagickFalse;
12231
12232       if (LocaleNCompare(value+i,"chrm",4) == 0)
12233         mng_info->ping_exclude_cHRM=MagickFalse;
12234
12235       if (LocaleNCompare(value+i,"date",4) == 0)
12236         mng_info->ping_exclude_date=MagickFalse;
12237
12238       if (LocaleNCompare(value+i,"exif",4) == 0)
12239         mng_info->ping_exclude_EXIF=MagickFalse;
12240
12241       if (LocaleNCompare(value+i,"gama",4) == 0)
12242         mng_info->ping_exclude_gAMA=MagickFalse;
12243
12244       if (LocaleNCompare(value+i,"iccp",4) == 0)
12245         mng_info->ping_exclude_iCCP=MagickFalse;
12246
12247     /*
12248       if (LocaleNCompare(value+i,"itxt",4) == 0)
12249         mng_info->ping_exclude_iTXt=MagickFalse;
12250      */
12251
12252       if (LocaleNCompare(value+i,"gama",4) == 0)
12253         mng_info->ping_exclude_gAMA=MagickFalse;
12254
12255       if (LocaleNCompare(value+i,"offs",4) == 0)
12256         mng_info->ping_exclude_oFFs=MagickFalse;
12257
12258       if (LocaleNCompare(value+i,"phys",4) == 0)
12259         mng_info->ping_exclude_pHYs=MagickFalse;
12260
12261       if (LocaleNCompare(value+i,"srgb",4) == 0)
12262         mng_info->ping_exclude_sRGB=MagickFalse;
12263
12264       if (LocaleNCompare(value+i,"text",4) == 0)
12265         mng_info->ping_exclude_tEXt=MagickFalse;
12266
12267       if (LocaleNCompare(value+i,"trns",4) == 0)
12268         mng_info->ping_exclude_tRNS=MagickFalse;
12269
12270       if (LocaleNCompare(value+i,"vpag",4) == 0)
12271         mng_info->ping_exclude_vpAg=MagickFalse;
12272
12273       if (LocaleNCompare(value+i,"zccp",4) == 0)
12274         mng_info->ping_exclude_zCCP=MagickFalse;
12275
12276       if (LocaleNCompare(value+i,"ztxt",4) == 0)
12277         mng_info->ping_exclude_zTXt=MagickFalse;
12278
12279       if (LocaleNCompare(value+i,"all",3) == 0)
12280         {
12281           mng_info->ping_exclude_bKGD=MagickFalse;
12282           mng_info->ping_exclude_cHRM=MagickFalse;
12283           mng_info->ping_exclude_date=MagickFalse;
12284           mng_info->ping_exclude_EXIF=MagickFalse;
12285           mng_info->ping_exclude_gAMA=MagickFalse;
12286           mng_info->ping_exclude_iCCP=MagickFalse;
12287           /* mng_info->ping_exclude_iTXt=MagickFalse; */
12288           mng_info->ping_exclude_oFFs=MagickFalse;
12289           mng_info->ping_exclude_pHYs=MagickFalse;
12290           mng_info->ping_exclude_sRGB=MagickFalse;
12291           mng_info->ping_exclude_tEXt=MagickFalse;
12292           mng_info->ping_exclude_tRNS=MagickFalse;
12293           mng_info->ping_exclude_vpAg=MagickFalse;
12294           mng_info->ping_exclude_zCCP=MagickFalse;
12295           mng_info->ping_exclude_zTXt=MagickFalse;
12296           i--;
12297         }
12298       }
12299     }
12300   }
12301
12302   if (excluding != MagickFalse && logging != MagickFalse)
12303   {
12304     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12305       "  Chunks to be excluded from the output png:");
12306     if (mng_info->ping_exclude_bKGD != MagickFalse)
12307       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12308           "    bKGD");
12309     if (mng_info->ping_exclude_cHRM != MagickFalse)
12310       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12311           "    cHRM");
12312     if (mng_info->ping_exclude_date != MagickFalse)
12313       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12314           "    date");
12315     if (mng_info->ping_exclude_EXIF != MagickFalse)
12316       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12317           "    EXIF");
12318     if (mng_info->ping_exclude_gAMA != MagickFalse)
12319       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12320           "    gAMA");
12321     if (mng_info->ping_exclude_iCCP != MagickFalse)
12322       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12323           "    iCCP");
12324 /*
12325     if (mng_info->ping_exclude_iTXt != MagickFalse)
12326       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12327           "    iTXt");
12328 */
12329     if (mng_info->ping_exclude_oFFs != MagickFalse)
12330       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12331           "    oFFs");
12332     if (mng_info->ping_exclude_pHYs != MagickFalse)
12333       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12334           "    pHYs");
12335     if (mng_info->ping_exclude_sRGB != MagickFalse)
12336       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12337           "    sRGB");
12338     if (mng_info->ping_exclude_tEXt != MagickFalse)
12339       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12340           "    tEXt");
12341     if (mng_info->ping_exclude_tRNS != MagickFalse)
12342       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12343           "    tRNS");
12344     if (mng_info->ping_exclude_vpAg != MagickFalse)
12345       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12346           "    vpAg");
12347     if (mng_info->ping_exclude_zCCP != MagickFalse)
12348       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12349           "    zCCP");
12350     if (mng_info->ping_exclude_zTXt != MagickFalse)
12351       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12352           "    zTXt");
12353   }
12354
12355   mng_info->need_blob = MagickTrue;
12356
12357   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12358
12359   MngInfoFreeStruct(mng_info,&have_mng_structure);
12360
12361   if (logging != MagickFalse)
12362     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12363
12364   return(status);
12365 }
12366
12367 #if defined(JNG_SUPPORTED)
12368
12369 /* Write one JNG image */
12370 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12371    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12372 {
12373   Image
12374     *jpeg_image;
12375
12376   ImageInfo
12377     *jpeg_image_info;
12378
12379   MagickBooleanType
12380     logging,
12381     status;
12382
12383   size_t
12384     length;
12385
12386   unsigned char
12387     *blob,
12388     chunk[80],
12389     *p;
12390
12391   unsigned int
12392     jng_alpha_compression_method,
12393     jng_alpha_sample_depth,
12394     jng_color_type,
12395     transparent;
12396
12397   size_t
12398     jng_alpha_quality,
12399     jng_quality;
12400
12401   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12402     "  Enter WriteOneJNGImage()");
12403
12404   blob=(unsigned char *) NULL;
12405   jpeg_image=(Image *) NULL;
12406   jpeg_image_info=(ImageInfo *) NULL;
12407
12408   status=MagickTrue;
12409   transparent=image_info->type==GrayscaleMatteType ||
12410      image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
12411
12412   jng_alpha_sample_depth = 0;
12413
12414   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12415
12416   jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12417
12418   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12419       image_info->quality;
12420
12421   if (jng_alpha_quality >= 1000)
12422     jng_alpha_quality /= 1000;
12423
12424   length=0;
12425
12426   if (transparent)
12427     {
12428       jng_color_type=14;
12429
12430       /* Create JPEG blob, image, and image_info */
12431       if (logging != MagickFalse)
12432         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12433           "  Creating jpeg_image_info for alpha.");
12434
12435       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12436
12437       if (jpeg_image_info == (ImageInfo *) NULL)
12438         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12439
12440       if (logging != MagickFalse)
12441         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12442           "  Creating jpeg_image.");
12443
12444       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12445       if (jpeg_image == (Image *) NULL)
12446         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12447       (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12448       jpeg_image->alpha_trait=UndefinedPixelTrait;
12449       jpeg_image->quality=jng_alpha_quality;
12450       jpeg_image_info->type=GrayscaleType;
12451       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12452       (void) AcquireUniqueFilename(jpeg_image->filename);
12453       (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
12454         "%s",jpeg_image->filename);
12455     }
12456   else
12457     {
12458       jng_alpha_compression_method=0;
12459       jng_color_type=10;
12460       jng_alpha_sample_depth=0;
12461     }
12462
12463   /* To do: check bit depth of PNG alpha channel */
12464
12465   /* Check if image is grayscale. */
12466   if (image_info->type != TrueColorMatteType && image_info->type !=
12467     TrueColorType && IsImageGray(image,exception))
12468     jng_color_type-=2;
12469
12470   if (logging != MagickFalse)
12471     {
12472         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12473           "    JNG Quality           = %d",(int) jng_quality);
12474         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12475           "    JNG Color Type        = %d",jng_color_type);
12476         if (transparent)
12477           {
12478             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12479               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12480             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12481               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12482             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12483               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12484           }
12485     }
12486
12487   if (transparent)
12488     {
12489       if (jng_alpha_compression_method==0)
12490         {
12491           const char
12492             *value;
12493
12494           /* Encode alpha as a grayscale PNG blob */
12495           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12496             exception);
12497           if (logging != MagickFalse)
12498             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12499               "  Creating PNG blob.");
12500
12501           (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12502           (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12503           jpeg_image_info->interlace=NoInterlace;
12504
12505           /* Exclude all ancillary chunks */
12506           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12507
12508           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12509             exception);
12510
12511           /* Retrieve sample depth used */
12512           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12513           if (value != (char *) NULL)
12514             jng_alpha_sample_depth= (unsigned int) value[0];
12515         }
12516       else
12517         {
12518           /* Encode alpha as a grayscale JPEG blob */
12519
12520           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12521             exception);
12522
12523           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12524           (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12525           jpeg_image_info->interlace=NoInterlace;
12526           if (logging != MagickFalse)
12527             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12528               "  Creating blob.");
12529           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12530            exception);
12531           jng_alpha_sample_depth=8;
12532
12533           if (logging != MagickFalse)
12534             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12535               "  Successfully read jpeg_image into a blob, length=%.20g.",
12536               (double) length);
12537
12538         }
12539       /* Destroy JPEG image and image_info */
12540       jpeg_image=DestroyImage(jpeg_image);
12541       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12542       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12543     }
12544
12545   /* Write JHDR chunk */
12546   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12547   PNGType(chunk,mng_JHDR);
12548   LogPNGChunk(logging,mng_JHDR,16L);
12549   PNGLong(chunk+4,(png_uint_32) image->columns);
12550   PNGLong(chunk+8,(png_uint_32) image->rows);
12551   chunk[12]=jng_color_type;
12552   chunk[13]=8;  /* sample depth */
12553   chunk[14]=8; /*jng_image_compression_method */
12554   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12555   chunk[16]=jng_alpha_sample_depth;
12556   chunk[17]=jng_alpha_compression_method;
12557   chunk[18]=0; /*jng_alpha_filter_method */
12558   chunk[19]=0; /*jng_alpha_interlace_method */
12559   (void) WriteBlob(image,20,chunk);
12560   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12561   if (logging != MagickFalse)
12562     {
12563       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12564         "    JNG width:%15lu",(unsigned long) image->columns);
12565
12566       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12567         "    JNG height:%14lu",(unsigned long) image->rows);
12568
12569       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12570         "    JNG color type:%10d",jng_color_type);
12571
12572       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12573         "    JNG sample depth:%8d",8);
12574
12575       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12576         "    JNG compression:%9d",8);
12577
12578       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12579         "    JNG interlace:%11d",0);
12580
12581       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12582         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
12583
12584       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12585         "    JNG alpha compression:%3d",jng_alpha_compression_method);
12586
12587       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12588         "    JNG alpha filter:%8d",0);
12589
12590       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12591         "    JNG alpha interlace:%5d",0);
12592     }
12593
12594   /* Write any JNG-chunk-b profiles */
12595   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12596
12597   /*
12598      Write leading ancillary chunks
12599   */
12600
12601   if (transparent)
12602   {
12603     /*
12604       Write JNG bKGD chunk
12605     */
12606
12607     unsigned char
12608       blue,
12609       green,
12610       red;
12611
12612     ssize_t
12613       num_bytes;
12614
12615     if (jng_color_type == 8 || jng_color_type == 12)
12616       num_bytes=6L;
12617     else
12618       num_bytes=10L;
12619     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12620     PNGType(chunk,mng_bKGD);
12621     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12622     red=ScaleQuantumToChar(image->background_color.red);
12623     green=ScaleQuantumToChar(image->background_color.green);
12624     blue=ScaleQuantumToChar(image->background_color.blue);
12625     *(chunk+4)=0;
12626     *(chunk+5)=red;
12627     *(chunk+6)=0;
12628     *(chunk+7)=green;
12629     *(chunk+8)=0;
12630     *(chunk+9)=blue;
12631     (void) WriteBlob(image,(size_t) num_bytes,chunk);
12632     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12633   }
12634
12635   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12636     {
12637       /*
12638         Write JNG sRGB chunk
12639       */
12640       (void) WriteBlobMSBULong(image,1L);
12641       PNGType(chunk,mng_sRGB);
12642       LogPNGChunk(logging,mng_sRGB,1L);
12643
12644       if (image->rendering_intent != UndefinedIntent)
12645         chunk[4]=(unsigned char)
12646           Magick_RenderingIntent_to_PNG_RenderingIntent(
12647           (image->rendering_intent));
12648
12649       else
12650         chunk[4]=(unsigned char)
12651           Magick_RenderingIntent_to_PNG_RenderingIntent(
12652           (PerceptualIntent));
12653
12654       (void) WriteBlob(image,5,chunk);
12655       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12656     }
12657   else
12658     {
12659       if (image->gamma != 0.0)
12660         {
12661           /*
12662              Write JNG gAMA chunk
12663           */
12664           (void) WriteBlobMSBULong(image,4L);
12665           PNGType(chunk,mng_gAMA);
12666           LogPNGChunk(logging,mng_gAMA,4L);
12667           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12668           (void) WriteBlob(image,8,chunk);
12669           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12670         }
12671
12672       if ((mng_info->equal_chrms == MagickFalse) &&
12673           (image->chromaticity.red_primary.x != 0.0))
12674         {
12675           PrimaryInfo
12676             primary;
12677
12678           /*
12679              Write JNG cHRM chunk
12680           */
12681           (void) WriteBlobMSBULong(image,32L);
12682           PNGType(chunk,mng_cHRM);
12683           LogPNGChunk(logging,mng_cHRM,32L);
12684           primary=image->chromaticity.white_point;
12685           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12686           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12687           primary=image->chromaticity.red_primary;
12688           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12689           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12690           primary=image->chromaticity.green_primary;
12691           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12692           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12693           primary=image->chromaticity.blue_primary;
12694           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12695           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12696           (void) WriteBlob(image,36,chunk);
12697           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12698         }
12699     }
12700
12701   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12702     {
12703       /*
12704          Write JNG pHYs chunk
12705       */
12706       (void) WriteBlobMSBULong(image,9L);
12707       PNGType(chunk,mng_pHYs);
12708       LogPNGChunk(logging,mng_pHYs,9L);
12709       if (image->units == PixelsPerInchResolution)
12710         {
12711           PNGLong(chunk+4,(png_uint_32)
12712             (image->resolution.x*100.0/2.54+0.5));
12713
12714           PNGLong(chunk+8,(png_uint_32)
12715             (image->resolution.y*100.0/2.54+0.5));
12716
12717           chunk[12]=1;
12718         }
12719
12720       else
12721         {
12722           if (image->units == PixelsPerCentimeterResolution)
12723             {
12724               PNGLong(chunk+4,(png_uint_32)
12725                 (image->resolution.x*100.0+0.5));
12726
12727               PNGLong(chunk+8,(png_uint_32)
12728                 (image->resolution.y*100.0+0.5));
12729
12730               chunk[12]=1;
12731             }
12732
12733           else
12734             {
12735               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12736               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12737               chunk[12]=0;
12738             }
12739         }
12740       (void) WriteBlob(image,13,chunk);
12741       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12742     }
12743
12744   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12745     {
12746       /*
12747          Write JNG oFFs chunk
12748       */
12749       (void) WriteBlobMSBULong(image,9L);
12750       PNGType(chunk,mng_oFFs);
12751       LogPNGChunk(logging,mng_oFFs,9L);
12752       PNGsLong(chunk+4,(ssize_t) (image->page.x));
12753       PNGsLong(chunk+8,(ssize_t) (image->page.y));
12754       chunk[12]=0;
12755       (void) WriteBlob(image,13,chunk);
12756       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12757     }
12758   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12759     {
12760        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
12761        PNGType(chunk,mng_vpAg);
12762        LogPNGChunk(logging,mng_vpAg,9L);
12763        PNGLong(chunk+4,(png_uint_32) image->page.width);
12764        PNGLong(chunk+8,(png_uint_32) image->page.height);
12765        chunk[12]=0;   /* unit = pixels */
12766        (void) WriteBlob(image,13,chunk);
12767        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12768     }
12769
12770
12771   if (transparent)
12772     {
12773       if (jng_alpha_compression_method==0)
12774         {
12775           register ssize_t
12776             i;
12777
12778           ssize_t
12779             len;
12780
12781           /* Write IDAT chunk header */
12782           if (logging != MagickFalse)
12783             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12784               "  Write IDAT chunks from blob, length=%.20g.",(double)
12785               length);
12786
12787           /* Copy IDAT chunks */
12788           len=0;
12789           p=blob+8;
12790           for (i=8; i<(ssize_t) length; i+=len+12)
12791           {
12792             len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12793             p+=4;
12794
12795             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12796               {
12797                 /* Found an IDAT chunk. */
12798                 (void) WriteBlobMSBULong(image,(size_t) len);
12799                 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12800                 (void) WriteBlob(image,(size_t) len+4,p);
12801                 (void) WriteBlobMSBULong(image,
12802                     crc32(0,p,(uInt) len+4));
12803               }
12804
12805             else
12806               {
12807                 if (logging != MagickFalse)
12808                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12809                     "    Skipping %c%c%c%c chunk, length=%.20g.",
12810                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
12811               }
12812             p+=(8+len);
12813           }
12814         }
12815       else
12816         {
12817           /* Write JDAA chunk header */
12818           if (logging != MagickFalse)
12819             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12820               "  Write JDAA chunk, length=%.20g.",(double) length);
12821           (void) WriteBlobMSBULong(image,(size_t) length);
12822           PNGType(chunk,mng_JDAA);
12823           LogPNGChunk(logging,mng_JDAA,length);
12824           /* Write JDAT chunk(s) data */
12825           (void) WriteBlob(image,4,chunk);
12826           (void) WriteBlob(image,length,blob);
12827           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12828              (uInt) length));
12829         }
12830       blob=(unsigned char *) RelinquishMagickMemory(blob);
12831     }
12832
12833   /* Encode image as a JPEG blob */
12834   if (logging != MagickFalse)
12835     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12836       "  Creating jpeg_image_info.");
12837   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12838   if (jpeg_image_info == (ImageInfo *) NULL)
12839     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12840
12841   if (logging != MagickFalse)
12842     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12843       "  Creating jpeg_image.");
12844
12845   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12846   if (jpeg_image == (Image *) NULL)
12847     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12848   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12849
12850   (void) AcquireUniqueFilename(jpeg_image->filename);
12851   (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12852     jpeg_image->filename);
12853
12854   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12855     exception);
12856
12857   if (logging != MagickFalse)
12858     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12859       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12860       (double) jpeg_image->rows);
12861
12862   if (jng_color_type == 8 || jng_color_type == 12)
12863     jpeg_image_info->type=GrayscaleType;
12864
12865   jpeg_image_info->quality=jng_quality;
12866   jpeg_image->quality=jng_quality;
12867   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12868   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12869
12870   if (logging != MagickFalse)
12871     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12872       "  Creating blob.");
12873
12874   blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12875
12876   if (logging != MagickFalse)
12877     {
12878       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12879         "  Successfully read jpeg_image into a blob, length=%.20g.",
12880         (double) length);
12881
12882       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12883         "  Write JDAT chunk, length=%.20g.",(double) length);
12884     }
12885
12886   /* Write JDAT chunk(s) */
12887   (void) WriteBlobMSBULong(image,(size_t) length);
12888   PNGType(chunk,mng_JDAT);
12889   LogPNGChunk(logging,mng_JDAT,length);
12890   (void) WriteBlob(image,4,chunk);
12891   (void) WriteBlob(image,length,blob);
12892   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12893
12894   jpeg_image=DestroyImage(jpeg_image);
12895   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12896   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12897   blob=(unsigned char *) RelinquishMagickMemory(blob);
12898
12899   /* Write any JNG-chunk-e profiles */
12900   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12901
12902   /* Write IEND chunk */
12903   (void) WriteBlobMSBULong(image,0L);
12904   PNGType(chunk,mng_IEND);
12905   LogPNGChunk(logging,mng_IEND,0);
12906   (void) WriteBlob(image,4,chunk);
12907   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12908
12909   if (logging != MagickFalse)
12910     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12911       "  exit WriteOneJNGImage()");
12912
12913   return(status);
12914 }
12915
12916
12917 /*
12918 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12919 %                                                                             %
12920 %                                                                             %
12921 %                                                                             %
12922 %   W r i t e J N G I m a g e                                                 %
12923 %                                                                             %
12924 %                                                                             %
12925 %                                                                             %
12926 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12927 %
12928 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12929 %
12930 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
12931 %
12932 %  The format of the WriteJNGImage method is:
12933 %
12934 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12935 %        Image *image,ExceptionInfo *exception)
12936 %
12937 %  A description of each parameter follows:
12938 %
12939 %    o image_info: the image info.
12940 %
12941 %    o image:  The image.
12942 %
12943 %    o exception: return any errors or warnings in this structure.
12944 %
12945 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12946 */
12947 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12948   ExceptionInfo *exception)
12949 {
12950   MagickBooleanType
12951     have_mng_structure,
12952     logging,
12953     status;
12954
12955   MngInfo
12956     *mng_info;
12957
12958   /*
12959     Open image file.
12960   */
12961   assert(image_info != (const ImageInfo *) NULL);
12962   assert(image_info->signature == MagickSignature);
12963   assert(image != (Image *) NULL);
12964   assert(image->signature == MagickSignature);
12965   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12966   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12967   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12968   if (status == MagickFalse)
12969     return(status);
12970
12971   /*
12972     Allocate a MngInfo structure.
12973   */
12974   have_mng_structure=MagickFalse;
12975   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12976   if (mng_info == (MngInfo *) NULL)
12977     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12978   /*
12979     Initialize members of the MngInfo structure.
12980   */
12981   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12982   mng_info->image=image;
12983   have_mng_structure=MagickTrue;
12984
12985   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12986
12987   status=WriteOneJNGImage(mng_info,image_info,image,exception);
12988   (void) CloseBlob(image);
12989
12990   (void) CatchImageException(image);
12991   MngInfoFreeStruct(mng_info,&have_mng_structure);
12992   if (logging != MagickFalse)
12993     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12994   return(status);
12995 }
12996 #endif
12997
12998 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12999   ExceptionInfo *exception)
13000 {
13001   const char
13002     *option;
13003
13004   Image
13005     *next_image;
13006
13007   MagickBooleanType
13008     have_mng_structure,
13009     status;
13010
13011   volatile MagickBooleanType
13012     logging;
13013
13014   MngInfo
13015     *mng_info;
13016
13017   int
13018     image_count,
13019     need_iterations,
13020     need_matte;
13021
13022   volatile int
13023 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13024     defined(PNG_MNG_FEATURES_SUPPORTED)
13025     need_local_plte,
13026 #endif
13027     all_images_are_gray,
13028     need_defi,
13029     use_global_plte;
13030
13031   register ssize_t
13032     i;
13033
13034   unsigned char
13035     chunk[800];
13036
13037   volatile unsigned int
13038     write_jng,
13039     write_mng;
13040
13041   volatile size_t
13042     scene;
13043
13044   size_t
13045     final_delay=0,
13046     initial_delay;
13047
13048 #if (PNG_LIBPNG_VER < 10200)
13049     if (image_info->verbose)
13050       printf("Your PNG library (libpng-%s) is rather old.\n",
13051          PNG_LIBPNG_VER_STRING);
13052 #endif
13053
13054   /*
13055     Open image file.
13056   */
13057   assert(image_info != (const ImageInfo *) NULL);
13058   assert(image_info->signature == MagickSignature);
13059   assert(image != (Image *) NULL);
13060   assert(image->signature == MagickSignature);
13061   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13062   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13063   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13064   if (status == MagickFalse)
13065     return(status);
13066
13067   /*
13068     Allocate a MngInfo structure.
13069   */
13070   have_mng_structure=MagickFalse;
13071   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13072   if (mng_info == (MngInfo *) NULL)
13073     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13074   /*
13075     Initialize members of the MngInfo structure.
13076   */
13077   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13078   mng_info->image=image;
13079   have_mng_structure=MagickTrue;
13080   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13081
13082   /*
13083    * See if user has requested a specific PNG subformat to be used
13084    * for all of the PNGs in the MNG being written, e.g.,
13085    *
13086    *    convert *.png png8:animation.mng
13087    *
13088    * To do: check -define png:bit_depth and png:color_type as well,
13089    * or perhaps use mng:bit_depth and mng:color_type instead for
13090    * global settings.
13091    */
13092
13093   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13094   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13095   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13096
13097   write_jng=MagickFalse;
13098   if (image_info->compression == JPEGCompression)
13099     write_jng=MagickTrue;
13100
13101   mng_info->adjoin=image_info->adjoin &&
13102     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13103
13104   if (logging != MagickFalse)
13105     {
13106       /* Log some info about the input */
13107       Image
13108         *p;
13109
13110       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13111         "  Checking input image(s)");
13112
13113       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13114         "    Image_info depth: %.20g",(double) image_info->depth);
13115
13116       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13117         "    Type: %d",image_info->type);
13118
13119       scene=0;
13120       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13121       {
13122         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13123           "    Scene: %.20g",(double) scene++);
13124
13125         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13126           "      Image depth: %.20g",(double) p->depth);
13127
13128         if (p->alpha_trait == BlendPixelTrait)
13129           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13130             "      Matte: True");
13131
13132         else
13133           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13134             "      Matte: False");
13135
13136         if (p->storage_class == PseudoClass)
13137           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13138             "      Storage class: PseudoClass");
13139
13140         else
13141           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13142             "      Storage class: DirectClass");
13143
13144         if (p->colors)
13145           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13146             "      Number of colors: %.20g",(double) p->colors);
13147
13148         else
13149           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13150             "      Number of colors: unspecified");
13151
13152         if (mng_info->adjoin == MagickFalse)
13153           break;
13154       }
13155     }
13156
13157   use_global_plte=MagickFalse;
13158   all_images_are_gray=MagickFalse;
13159 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13160   need_local_plte=MagickTrue;
13161 #endif
13162   need_defi=MagickFalse;
13163   need_matte=MagickFalse;
13164   mng_info->framing_mode=1;
13165   mng_info->old_framing_mode=1;
13166
13167   if (write_mng)
13168       if (image_info->page != (char *) NULL)
13169         {
13170           /*
13171             Determine image bounding box.
13172           */
13173           SetGeometry(image,&mng_info->page);
13174           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13175             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13176         }
13177   if (write_mng)
13178     {
13179       unsigned int
13180         need_geom;
13181
13182       unsigned short
13183         red,
13184         green,
13185         blue;
13186
13187       mng_info->page=image->page;
13188       need_geom=MagickTrue;
13189       if (mng_info->page.width || mng_info->page.height)
13190          need_geom=MagickFalse;
13191       /*
13192         Check all the scenes.
13193       */
13194       initial_delay=image->delay;
13195       need_iterations=MagickFalse;
13196       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13197       mng_info->equal_physs=MagickTrue,
13198       mng_info->equal_gammas=MagickTrue;
13199       mng_info->equal_srgbs=MagickTrue;
13200       mng_info->equal_backgrounds=MagickTrue;
13201       image_count=0;
13202 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13203     defined(PNG_MNG_FEATURES_SUPPORTED)
13204       all_images_are_gray=MagickTrue;
13205       mng_info->equal_palettes=MagickFalse;
13206       need_local_plte=MagickFalse;
13207 #endif
13208       for (next_image=image; next_image != (Image *) NULL; )
13209       {
13210         if (need_geom)
13211           {
13212             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13213               mng_info->page.width=next_image->columns+next_image->page.x;
13214
13215             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13216               mng_info->page.height=next_image->rows+next_image->page.y;
13217           }
13218
13219         if (next_image->page.x || next_image->page.y)
13220           need_defi=MagickTrue;
13221
13222         if (next_image->alpha_trait == BlendPixelTrait)
13223           need_matte=MagickTrue;
13224
13225         if ((int) next_image->dispose >= BackgroundDispose)
13226           if ((next_image->alpha_trait == BlendPixelTrait) ||
13227                next_image->page.x || next_image->page.y ||
13228               ((next_image->columns < mng_info->page.width) &&
13229                (next_image->rows < mng_info->page.height)))
13230             mng_info->need_fram=MagickTrue;
13231
13232         if (next_image->iterations)
13233           need_iterations=MagickTrue;
13234
13235         final_delay=next_image->delay;
13236
13237         if (final_delay != initial_delay || final_delay > 1UL*
13238            next_image->ticks_per_second)
13239           mng_info->need_fram=1;
13240
13241 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13242     defined(PNG_MNG_FEATURES_SUPPORTED)
13243         /*
13244           check for global palette possibility.
13245         */
13246         if (image->alpha_trait == BlendPixelTrait)
13247            need_local_plte=MagickTrue;
13248
13249         if (need_local_plte == 0)
13250           {
13251             if (IsImageGray(image,exception) == MagickFalse)
13252               all_images_are_gray=MagickFalse;
13253             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13254             if (use_global_plte == 0)
13255               use_global_plte=mng_info->equal_palettes;
13256             need_local_plte=!mng_info->equal_palettes;
13257           }
13258 #endif
13259         if (GetNextImageInList(next_image) != (Image *) NULL)
13260           {
13261             if (next_image->background_color.red !=
13262                 next_image->next->background_color.red ||
13263                 next_image->background_color.green !=
13264                 next_image->next->background_color.green ||
13265                 next_image->background_color.blue !=
13266                 next_image->next->background_color.blue)
13267               mng_info->equal_backgrounds=MagickFalse;
13268
13269             if (next_image->gamma != next_image->next->gamma)
13270               mng_info->equal_gammas=MagickFalse;
13271
13272             if (next_image->rendering_intent !=
13273                 next_image->next->rendering_intent)
13274               mng_info->equal_srgbs=MagickFalse;
13275
13276             if ((next_image->units != next_image->next->units) ||
13277                 (next_image->resolution.x != next_image->next->resolution.x) ||
13278                 (next_image->resolution.y != next_image->next->resolution.y))
13279               mng_info->equal_physs=MagickFalse;
13280
13281             if (mng_info->equal_chrms)
13282               {
13283                 if (next_image->chromaticity.red_primary.x !=
13284                     next_image->next->chromaticity.red_primary.x ||
13285                     next_image->chromaticity.red_primary.y !=
13286                     next_image->next->chromaticity.red_primary.y ||
13287                     next_image->chromaticity.green_primary.x !=
13288                     next_image->next->chromaticity.green_primary.x ||
13289                     next_image->chromaticity.green_primary.y !=
13290                     next_image->next->chromaticity.green_primary.y ||
13291                     next_image->chromaticity.blue_primary.x !=
13292                     next_image->next->chromaticity.blue_primary.x ||
13293                     next_image->chromaticity.blue_primary.y !=
13294                     next_image->next->chromaticity.blue_primary.y ||
13295                     next_image->chromaticity.white_point.x !=
13296                     next_image->next->chromaticity.white_point.x ||
13297                     next_image->chromaticity.white_point.y !=
13298                     next_image->next->chromaticity.white_point.y)
13299                   mng_info->equal_chrms=MagickFalse;
13300               }
13301           }
13302         image_count++;
13303         next_image=GetNextImageInList(next_image);
13304       }
13305       if (image_count < 2)
13306         {
13307           mng_info->equal_backgrounds=MagickFalse;
13308           mng_info->equal_chrms=MagickFalse;
13309           mng_info->equal_gammas=MagickFalse;
13310           mng_info->equal_srgbs=MagickFalse;
13311           mng_info->equal_physs=MagickFalse;
13312           use_global_plte=MagickFalse;
13313 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13314           need_local_plte=MagickTrue;
13315 #endif
13316           need_iterations=MagickFalse;
13317         }
13318
13319      if (mng_info->need_fram == MagickFalse)
13320        {
13321          /*
13322            Only certain framing rates 100/n are exactly representable without
13323            the FRAM chunk but we'll allow some slop in VLC files
13324          */
13325          if (final_delay == 0)
13326            {
13327              if (need_iterations != MagickFalse)
13328                {
13329                  /*
13330                    It's probably a GIF with loop; don't run it *too* fast.
13331                  */
13332                  if (mng_info->adjoin)
13333                    {
13334                      final_delay=10;
13335                      (void) ThrowMagickException(exception,GetMagickModule(),
13336                        CoderWarning,
13337                        "input has zero delay between all frames; assuming",
13338                        " 10 cs `%s'","");
13339                    }
13340                }
13341              else
13342                mng_info->ticks_per_second=0;
13343            }
13344          if (final_delay != 0)
13345            mng_info->ticks_per_second=(png_uint_32)
13346               (image->ticks_per_second/final_delay);
13347          if (final_delay > 50)
13348            mng_info->ticks_per_second=2;
13349
13350          if (final_delay > 75)
13351            mng_info->ticks_per_second=1;
13352
13353          if (final_delay > 125)
13354            mng_info->need_fram=MagickTrue;
13355
13356          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13357             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13358             (final_delay != 25) && (final_delay != 50) && (1UL*final_delay !=
13359                1UL*image->ticks_per_second))
13360            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13361        }
13362
13363      if (mng_info->need_fram != MagickFalse)
13364         mng_info->ticks_per_second=1UL*image->ticks_per_second;
13365      /*
13366         If pseudocolor, we should also check to see if all the
13367         palettes are identical and write a global PLTE if they are.
13368         ../glennrp Feb 99.
13369      */
13370      /*
13371         Write the MNG version 1.0 signature and MHDR chunk.
13372      */
13373      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13374      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13375      PNGType(chunk,mng_MHDR);
13376      LogPNGChunk(logging,mng_MHDR,28L);
13377      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13378      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13379      PNGLong(chunk+12,mng_info->ticks_per_second);
13380      PNGLong(chunk+16,0L);  /* layer count=unknown */
13381      PNGLong(chunk+20,0L);  /* frame count=unknown */
13382      PNGLong(chunk+24,0L);  /* play time=unknown   */
13383      if (write_jng)
13384        {
13385          if (need_matte)
13386            {
13387              if (need_defi || mng_info->need_fram || use_global_plte)
13388                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13389
13390              else
13391                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13392            }
13393
13394          else
13395            {
13396              if (need_defi || mng_info->need_fram || use_global_plte)
13397                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13398
13399              else
13400                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13401            }
13402        }
13403
13404      else
13405        {
13406          if (need_matte)
13407            {
13408              if (need_defi || mng_info->need_fram || use_global_plte)
13409                PNGLong(chunk+28,11L);    /* simplicity=LC */
13410
13411              else
13412                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13413            }
13414
13415          else
13416            {
13417              if (need_defi || mng_info->need_fram || use_global_plte)
13418                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13419
13420              else
13421                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13422            }
13423        }
13424      (void) WriteBlob(image,32,chunk);
13425      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13426      option=GetImageOption(image_info,"mng:need-cacheoff");
13427      if (option != (const char *) NULL)
13428        {
13429          size_t
13430            length;
13431
13432          /*
13433            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13434          */
13435          PNGType(chunk,mng_nEED);
13436          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13437          (void) WriteBlobMSBULong(image,(size_t) length);
13438          LogPNGChunk(logging,mng_nEED,(size_t) length);
13439          length+=4;
13440          (void) WriteBlob(image,length,chunk);
13441          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13442        }
13443      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13444          (GetNextImageInList(image) != (Image *) NULL) &&
13445          (image->iterations != 1))
13446        {
13447          /*
13448            Write MNG TERM chunk
13449          */
13450          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13451          PNGType(chunk,mng_TERM);
13452          LogPNGChunk(logging,mng_TERM,10L);
13453          chunk[4]=3;  /* repeat animation */
13454          chunk[5]=0;  /* show last frame when done */
13455          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13456             final_delay/MagickMax(image->ticks_per_second,1)));
13457
13458          if (image->iterations == 0)
13459            PNGLong(chunk+10,PNG_UINT_31_MAX);
13460
13461          else
13462            PNGLong(chunk+10,(png_uint_32) image->iterations);
13463
13464          if (logging != MagickFalse)
13465            {
13466              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13467                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13468               final_delay/MagickMax(image->ticks_per_second,1)));
13469
13470              if (image->iterations == 0)
13471                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13472                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13473
13474              else
13475                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13476                  "     Image iterations: %.20g",(double) image->iterations);
13477            }
13478          (void) WriteBlob(image,14,chunk);
13479          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13480        }
13481      /*
13482        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13483      */
13484      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13485           mng_info->equal_srgbs)
13486        {
13487          /*
13488            Write MNG sRGB chunk
13489          */
13490          (void) WriteBlobMSBULong(image,1L);
13491          PNGType(chunk,mng_sRGB);
13492          LogPNGChunk(logging,mng_sRGB,1L);
13493
13494          if (image->rendering_intent != UndefinedIntent)
13495            chunk[4]=(unsigned char)
13496              Magick_RenderingIntent_to_PNG_RenderingIntent(
13497              (image->rendering_intent));
13498
13499          else
13500            chunk[4]=(unsigned char)
13501              Magick_RenderingIntent_to_PNG_RenderingIntent(
13502                (PerceptualIntent));
13503
13504          (void) WriteBlob(image,5,chunk);
13505          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13506          mng_info->have_write_global_srgb=MagickTrue;
13507        }
13508
13509      else
13510        {
13511          if (image->gamma && mng_info->equal_gammas)
13512            {
13513              /*
13514                 Write MNG gAMA chunk
13515              */
13516              (void) WriteBlobMSBULong(image,4L);
13517              PNGType(chunk,mng_gAMA);
13518              LogPNGChunk(logging,mng_gAMA,4L);
13519              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13520              (void) WriteBlob(image,8,chunk);
13521              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13522              mng_info->have_write_global_gama=MagickTrue;
13523            }
13524          if (mng_info->equal_chrms)
13525            {
13526              PrimaryInfo
13527                primary;
13528
13529              /*
13530                 Write MNG cHRM chunk
13531              */
13532              (void) WriteBlobMSBULong(image,32L);
13533              PNGType(chunk,mng_cHRM);
13534              LogPNGChunk(logging,mng_cHRM,32L);
13535              primary=image->chromaticity.white_point;
13536              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13537              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13538              primary=image->chromaticity.red_primary;
13539              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13540              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13541              primary=image->chromaticity.green_primary;
13542              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13543              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13544              primary=image->chromaticity.blue_primary;
13545              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13546              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13547              (void) WriteBlob(image,36,chunk);
13548              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13549              mng_info->have_write_global_chrm=MagickTrue;
13550            }
13551        }
13552      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13553        {
13554          /*
13555             Write MNG pHYs chunk
13556          */
13557          (void) WriteBlobMSBULong(image,9L);
13558          PNGType(chunk,mng_pHYs);
13559          LogPNGChunk(logging,mng_pHYs,9L);
13560
13561          if (image->units == PixelsPerInchResolution)
13562            {
13563              PNGLong(chunk+4,(png_uint_32)
13564                (image->resolution.x*100.0/2.54+0.5));
13565
13566              PNGLong(chunk+8,(png_uint_32)
13567                (image->resolution.y*100.0/2.54+0.5));
13568
13569              chunk[12]=1;
13570            }
13571
13572          else
13573            {
13574              if (image->units == PixelsPerCentimeterResolution)
13575                {
13576                  PNGLong(chunk+4,(png_uint_32)
13577                    (image->resolution.x*100.0+0.5));
13578
13579                  PNGLong(chunk+8,(png_uint_32)
13580                    (image->resolution.y*100.0+0.5));
13581
13582                  chunk[12]=1;
13583                }
13584
13585              else
13586                {
13587                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13588                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13589                  chunk[12]=0;
13590                }
13591            }
13592          (void) WriteBlob(image,13,chunk);
13593          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13594        }
13595      /*
13596        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13597        or does not cover the entire frame.
13598      */
13599      if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
13600          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13601          (image->page.width+image->page.x < mng_info->page.width))
13602          || (image->page.height && (image->page.height+image->page.y
13603          < mng_info->page.height))))
13604        {
13605          (void) WriteBlobMSBULong(image,6L);
13606          PNGType(chunk,mng_BACK);
13607          LogPNGChunk(logging,mng_BACK,6L);
13608          red=ScaleQuantumToShort(image->background_color.red);
13609          green=ScaleQuantumToShort(image->background_color.green);
13610          blue=ScaleQuantumToShort(image->background_color.blue);
13611          PNGShort(chunk+4,red);
13612          PNGShort(chunk+6,green);
13613          PNGShort(chunk+8,blue);
13614          (void) WriteBlob(image,10,chunk);
13615          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13616          if (mng_info->equal_backgrounds)
13617            {
13618              (void) WriteBlobMSBULong(image,6L);
13619              PNGType(chunk,mng_bKGD);
13620              LogPNGChunk(logging,mng_bKGD,6L);
13621              (void) WriteBlob(image,10,chunk);
13622              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13623            }
13624        }
13625
13626 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13627      if ((need_local_plte == MagickFalse) &&
13628          (image->storage_class == PseudoClass) &&
13629          (all_images_are_gray == MagickFalse))
13630        {
13631          size_t
13632            data_length;
13633
13634          /*
13635            Write MNG PLTE chunk
13636          */
13637          data_length=3*image->colors;
13638          (void) WriteBlobMSBULong(image,data_length);
13639          PNGType(chunk,mng_PLTE);
13640          LogPNGChunk(logging,mng_PLTE,data_length);
13641
13642          for (i=0; i < (ssize_t) image->colors; i++)
13643          {
13644            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13645              image->colormap[i].red) & 0xff);
13646            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13647              image->colormap[i].green) & 0xff);
13648            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13649              image->colormap[i].blue) & 0xff);
13650          }
13651
13652          (void) WriteBlob(image,data_length+4,chunk);
13653          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13654          mng_info->have_write_global_plte=MagickTrue;
13655        }
13656 #endif
13657     }
13658   scene=0;
13659   mng_info->delay=0;
13660 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13661     defined(PNG_MNG_FEATURES_SUPPORTED)
13662   mng_info->equal_palettes=MagickFalse;
13663 #endif
13664   do
13665   {
13666     if (mng_info->adjoin)
13667     {
13668 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13669     defined(PNG_MNG_FEATURES_SUPPORTED)
13670     /*
13671       If we aren't using a global palette for the entire MNG, check to
13672       see if we can use one for two or more consecutive images.
13673     */
13674     if (need_local_plte && use_global_plte && !all_images_are_gray)
13675       {
13676         if (mng_info->IsPalette)
13677           {
13678             /*
13679               When equal_palettes is true, this image has the same palette
13680               as the previous PseudoClass image
13681             */
13682             mng_info->have_write_global_plte=mng_info->equal_palettes;
13683             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13684             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13685               {
13686                 /*
13687                   Write MNG PLTE chunk
13688                 */
13689                 size_t
13690                   data_length;
13691
13692                 data_length=3*image->colors;
13693                 (void) WriteBlobMSBULong(image,data_length);
13694                 PNGType(chunk,mng_PLTE);
13695                 LogPNGChunk(logging,mng_PLTE,data_length);
13696
13697                 for (i=0; i < (ssize_t) image->colors; i++)
13698                 {
13699                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13700                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13701                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13702                 }
13703
13704                 (void) WriteBlob(image,data_length+4,chunk);
13705                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13706                    (uInt) (data_length+4)));
13707                 mng_info->have_write_global_plte=MagickTrue;
13708               }
13709           }
13710         else
13711           mng_info->have_write_global_plte=MagickFalse;
13712       }
13713 #endif
13714     if (need_defi)
13715       {
13716         ssize_t
13717           previous_x,
13718           previous_y;
13719
13720         if (scene)
13721           {
13722             previous_x=mng_info->page.x;
13723             previous_y=mng_info->page.y;
13724           }
13725         else
13726           {
13727             previous_x=0;
13728             previous_y=0;
13729           }
13730         mng_info->page=image->page;
13731         if ((mng_info->page.x !=  previous_x) ||
13732             (mng_info->page.y != previous_y))
13733           {
13734              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
13735              PNGType(chunk,mng_DEFI);
13736              LogPNGChunk(logging,mng_DEFI,12L);
13737              chunk[4]=0; /* object 0 MSB */
13738              chunk[5]=0; /* object 0 LSB */
13739              chunk[6]=0; /* visible  */
13740              chunk[7]=0; /* abstract */
13741              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13742              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13743              (void) WriteBlob(image,16,chunk);
13744              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13745           }
13746       }
13747     }
13748
13749    mng_info->write_mng=write_mng;
13750
13751    if ((int) image->dispose >= 3)
13752      mng_info->framing_mode=3;
13753
13754    if (mng_info->need_fram && mng_info->adjoin &&
13755        ((image->delay != mng_info->delay) ||
13756         (mng_info->framing_mode != mng_info->old_framing_mode)))
13757      {
13758        if (image->delay == mng_info->delay)
13759          {
13760            /*
13761              Write a MNG FRAM chunk with the new framing mode.
13762            */
13763            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
13764            PNGType(chunk,mng_FRAM);
13765            LogPNGChunk(logging,mng_FRAM,1L);
13766            chunk[4]=(unsigned char) mng_info->framing_mode;
13767            (void) WriteBlob(image,5,chunk);
13768            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13769          }
13770        else
13771          {
13772            /*
13773              Write a MNG FRAM chunk with the delay.
13774            */
13775            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13776            PNGType(chunk,mng_FRAM);
13777            LogPNGChunk(logging,mng_FRAM,10L);
13778            chunk[4]=(unsigned char) mng_info->framing_mode;
13779            chunk[5]=0;  /* frame name separator (no name) */
13780            chunk[6]=2;  /* flag for changing default delay */
13781            chunk[7]=0;  /* flag for changing frame timeout */
13782            chunk[8]=0;  /* flag for changing frame clipping */
13783            chunk[9]=0;  /* flag for changing frame sync_id */
13784            PNGLong(chunk+10,(png_uint_32)
13785              ((mng_info->ticks_per_second*
13786              image->delay)/MagickMax(image->ticks_per_second,1)));
13787            (void) WriteBlob(image,14,chunk);
13788            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13789            mng_info->delay=(png_uint_32) image->delay;
13790          }
13791        mng_info->old_framing_mode=mng_info->framing_mode;
13792      }
13793
13794 #if defined(JNG_SUPPORTED)
13795    if (image_info->compression == JPEGCompression)
13796      {
13797        ImageInfo
13798          *write_info;
13799
13800        if (logging != MagickFalse)
13801          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13802            "  Writing JNG object.");
13803        /* To do: specify the desired alpha compression method. */
13804        write_info=CloneImageInfo(image_info);
13805        write_info->compression=UndefinedCompression;
13806        status=WriteOneJNGImage(mng_info,write_info,image,exception);
13807        write_info=DestroyImageInfo(write_info);
13808      }
13809    else
13810 #endif
13811      {
13812        if (logging != MagickFalse)
13813          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13814            "  Writing PNG object.");
13815
13816        mng_info->need_blob = MagickFalse;
13817        mng_info->ping_preserve_colormap = MagickFalse;
13818
13819        /* We don't want any ancillary chunks written */
13820        mng_info->ping_exclude_bKGD=MagickTrue;
13821        mng_info->ping_exclude_cHRM=MagickTrue;
13822        mng_info->ping_exclude_date=MagickTrue;
13823        mng_info->ping_exclude_EXIF=MagickTrue;
13824        mng_info->ping_exclude_gAMA=MagickTrue;
13825        mng_info->ping_exclude_iCCP=MagickTrue;
13826        /* mng_info->ping_exclude_iTXt=MagickTrue; */
13827        mng_info->ping_exclude_oFFs=MagickTrue;
13828        mng_info->ping_exclude_pHYs=MagickTrue;
13829        mng_info->ping_exclude_sRGB=MagickTrue;
13830        mng_info->ping_exclude_tEXt=MagickTrue;
13831        mng_info->ping_exclude_tRNS=MagickTrue;
13832        mng_info->ping_exclude_vpAg=MagickTrue;
13833        mng_info->ping_exclude_zCCP=MagickTrue;
13834        mng_info->ping_exclude_zTXt=MagickTrue;
13835
13836        status=WriteOnePNGImage(mng_info,image_info,image,exception);
13837      }
13838
13839     if (status == MagickFalse)
13840       {
13841         MngInfoFreeStruct(mng_info,&have_mng_structure);
13842         (void) CloseBlob(image);
13843         return(MagickFalse);
13844       }
13845     (void) CatchImageException(image);
13846     if (GetNextImageInList(image) == (Image *) NULL)
13847       break;
13848     image=SyncNextImageInList(image);
13849     status=SetImageProgress(image,SaveImagesTag,scene++,
13850       GetImageListLength(image));
13851
13852     if (status == MagickFalse)
13853       break;
13854
13855   } while (mng_info->adjoin);
13856
13857   if (write_mng)
13858     {
13859       while (GetPreviousImageInList(image) != (Image *) NULL)
13860         image=GetPreviousImageInList(image);
13861       /*
13862         Write the MEND chunk.
13863       */
13864       (void) WriteBlobMSBULong(image,0x00000000L);
13865       PNGType(chunk,mng_MEND);
13866       LogPNGChunk(logging,mng_MEND,0L);
13867       (void) WriteBlob(image,4,chunk);
13868       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13869     }
13870   /*
13871     Relinquish resources.
13872   */
13873   (void) CloseBlob(image);
13874   MngInfoFreeStruct(mng_info,&have_mng_structure);
13875
13876   if (logging != MagickFalse)
13877     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13878
13879   return(MagickTrue);
13880 }
13881 #else /* PNG_LIBPNG_VER > 10011 */
13882
13883 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13884 {
13885   (void) image;
13886   printf("Your PNG library is too old: You have libpng-%s\n",
13887      PNG_LIBPNG_VER_STRING);
13888
13889   ThrowBinaryException(CoderError,"PNG library is too old",
13890      image_info->filename);
13891 }
13892
13893 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13894 {
13895   return(WritePNGImage(image_info,image));
13896 }
13897 #endif /* PNG_LIBPNG_VER > 10011 */
13898 #endif