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