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