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