]> granicus.if.org Git - imagemagick/blob - coders/png.c
Combined some Log() calls to reduce code size.
[imagemagick] / coders / png.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            PPPP   N   N   GGGG                              %
7 %                            P   P  NN  N  G                                  %
8 %                            PPPP   N N N  G  GG                              %
9 %                            P      N  NN  G   G                              %
10 %                            P      N   N   GGG                               %
11 %                                                                             %
12 %                                                                             %
13 %              Read/Write Portable Network Graphics Image Format              %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                           Glenn Randers-Pehrson                             %
18 %                               November 1997                                 %
19 %                                                                             %
20 %                                                                             %
21 %  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
22 %  dedicated to making software imaging solutions freely available.           %
23 %                                                                             %
24 %  You may not use this file except in compliance with the License.  You may  %
25 %  obtain a copy of the License at                                            %
26 %                                                                             %
27 %    http://www.imagemagick.org/script/license.php                            %
28 %                                                                             %
29 %  Unless required by applicable law or agreed to in writing, software        %
30 %  distributed under the License is distributed on an "AS IS" BASIS,          %
31 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32 %  See the License for the specific language governing permissions and        %
33 %  limitations under the License.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39
40 \f
41 /*
42   Include declarations.
43 */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
52 #include "MagickCore/color-private.h"
53 #include "MagickCore/colormap.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/histogram.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/layer.h"
65 #include "MagickCore/list.h"
66 #include "MagickCore/log.h"
67 #include "MagickCore/MagickCore.h"
68 #include "MagickCore/memory_.h"
69 #include "MagickCore/module.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/profile.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/static.h"
82 #include "MagickCore/statistic.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/utility.h"
87 #if defined(MAGICKCORE_PNG_DELEGATE)
88
89 /* Suppress libpng pedantic warnings that were added in
90  * libpng-1.2.41 and libpng-1.4.0.  If you are working on
91  * migration to libpng-1.5, remove these defines and then
92  * fix any code that generates warnings.
93  */
94 /* #define PNG_DEPRECATED   Use of this function is deprecated */
95 /* #define PNG_USE_RESULT   The result of this function must be checked */
96 /* #define PNG_NORETURN     This function does not return */
97 /* #define PNG_ALLOCATED    The result of the function is new memory */
98 /* #define PNG_DEPSTRUCT    Access to this struct member is deprecated */
99
100 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
101 #define PNG_PTR_NORETURN
102
103 #include "png.h"
104 #include "zlib.h"
105 \f
106 /* ImageMagick differences */
107 #define first_scene scene
108
109 #if PNG_LIBPNG_VER > 10011
110 /*
111   Optional declarations. Define or undefine them as you like.
112 */
113 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
114
115 /*
116   Features under construction.  Define these to work on them.
117 */
118 #undef MNG_OBJECT_BUFFERS
119 #undef MNG_BASI_SUPPORTED
120 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121 #define MNG_INSERT_LAYERS   /* Troublesome, but seem to work as of 5.4.4 */
122 #if defined(MAGICKCORE_JPEG_DELEGATE)
123 #  define JNG_SUPPORTED /* Not finished as of 5.5.2.  See "To do" comments. */
124 #endif
125 #if !defined(RGBColorMatchExact)
126 #define IsPNGColorEqual(color,target) \
127        (((color).red == (target).red) && \
128         ((color).green == (target).green) && \
129         ((color).blue == (target).blue))
130 #endif
131
132 /* Table of recognized sRGB ICC profiles */
133 struct sRGB_info_struct
134 {
135     png_uint_32 len;
136     png_uint_32 crc;
137     png_byte intent;
138 };
139
140 const struct sRGB_info_struct sRGB_info[] =
141
142     /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
143     { 3048, 0x3b8772b9UL, 0},
144
145     /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
146     { 3052, 0x427ebb21UL, 1},
147
148     /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
149     {60988, 0x306fd8aeUL, 0},
150
151     /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
152      {60960, 0xbbef7812UL, 0},
153
154     /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
155      { 3024, 0x5d5129ceUL, 1},
156
157      /* HP-Microsoft sRGB v2 perceptual */
158      { 3144, 0x182ea552UL, 0},
159
160      /* HP-Microsoft sRGB v2 media-relative */
161      { 3144, 0xf29e526dUL, 1},
162
163      /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
164      {  524, 0xd4938c39UL, 0},
165
166      /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
167      { 3212, 0x034af5a1UL, 0},
168
169      /* Not recognized */
170      {    0, 0x00000000UL, 0},
171 };
172
173 /* Macros for left-bit-replication to ensure that pixels
174  * and PixelInfos all have the same image->depth, and for use
175  * in PNG8 quantization.
176  */
177
178 /* LBR01: Replicate top bit */
179
180 #define LBR01PacketRed(pixelpacket) \
181      (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
182         0 : QuantumRange);
183
184 #define LBR01PacketGreen(pixelpacket) \
185      (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
186         0 : QuantumRange);
187
188 #define LBR01PacketBlue(pixelpacket) \
189      (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
190         0 : QuantumRange);
191
192 #define LBR01PacketAlpha(pixelpacket) \
193      (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
194         0 : QuantumRange);
195
196 #define LBR01PacketRGB(pixelpacket) \
197         { \
198         LBR01PacketRed((pixelpacket)); \
199         LBR01PacketGreen((pixelpacket)); \
200         LBR01PacketBlue((pixelpacket)); \
201         }
202
203 #define LBR01PacketRGBO(pixelpacket) \
204         { \
205         LBR01PacketRGB((pixelpacket)); \
206         LBR01PacketAlpha((pixelpacket)); \
207         }
208
209 #define LBR01PixelRed(pixel) \
210         (SetPixelRed(image, \
211         ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
212         0 : QuantumRange,(pixel)));
213
214 #define LBR01PixelGreen(pixel) \
215         (SetPixelGreen(image, \
216         ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
217         0 : QuantumRange,(pixel)));
218
219 #define LBR01PixelBlue(pixel) \
220         (SetPixelBlue(image, \
221         ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
222         0 : QuantumRange,(pixel)));
223
224 #define LBR01PixelAlpha(pixel) \
225         (SetPixelAlpha(image, \
226         ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
227         0 : QuantumRange,(pixel)));
228
229 #define LBR01PixelRGB(pixel) \
230         { \
231         LBR01PixelRed((pixel)); \
232         LBR01PixelGreen((pixel)); \
233         LBR01PixelBlue((pixel)); \
234         }
235
236 #define LBR01PixelRGBA(pixel) \
237         { \
238         LBR01PixelRGB((pixel)); \
239         LBR01PixelAlpha((pixel)); \
240         }
241
242 /* LBR02: Replicate top 2 bits */
243
244 #define LBR02PacketRed(pixelpacket) \
245    { \
246      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
247      (pixelpacket).red=ScaleCharToQuantum( \
248        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
249    }
250 #define LBR02PacketGreen(pixelpacket) \
251    { \
252      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
253      (pixelpacket).green=ScaleCharToQuantum( \
254        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
255    }
256 #define LBR02PacketBlue(pixelpacket) \
257    { \
258      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
259      (pixelpacket).blue=ScaleCharToQuantum( \
260        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
261    }
262 #define LBR02PacketAlpha(pixelpacket) \
263    { \
264      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
265      (pixelpacket).alpha=ScaleCharToQuantum( \
266        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
267    }
268
269 #define LBR02PacketRGB(pixelpacket) \
270         { \
271         LBR02PacketRed((pixelpacket)); \
272         LBR02PacketGreen((pixelpacket)); \
273         LBR02PacketBlue((pixelpacket)); \
274         }
275
276 #define LBR02PacketRGBO(pixelpacket) \
277         { \
278         LBR02PacketRGB((pixelpacket)); \
279         LBR02PacketAlpha((pixelpacket)); \
280         }
281
282 #define LBR02PixelRed(pixel) \
283    { \
284      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
285        & 0xc0; \
286      SetPixelRed(image, ScaleCharToQuantum( \
287        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
288        (pixel)); \
289    }
290 #define LBR02PixelGreen(pixel) \
291    { \
292      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
293        & 0xc0; \
294      SetPixelGreen(image, ScaleCharToQuantum( \
295        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
296        (pixel)); \
297    }
298 #define LBR02PixelBlue(pixel) \
299    { \
300      unsigned char lbr_bits= \
301        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
302      SetPixelBlue(image, ScaleCharToQuantum( \
303        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
304        (pixel)); \
305    }
306 #define LBR02PixelAlpha(pixel) \
307    { \
308      unsigned char lbr_bits= \
309        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
310      SetPixelAlpha(image, ScaleCharToQuantum( \
311        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
312        (pixel) ); \
313    }
314
315 #define LBR02PixelRGB(pixel) \
316         { \
317         LBR02PixelRed((pixel)); \
318         LBR02PixelGreen((pixel)); \
319         LBR02PixelBlue((pixel)); \
320         }
321
322 #define LBR02PixelRGBA(pixel) \
323         { \
324         LBR02PixelRGB((pixel)); \
325         LBR02PixelAlpha((pixel)); \
326         }
327
328 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
329    PNG8 quantization) */
330
331 #define LBR03PacketRed(pixelpacket) \
332    { \
333      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
334      (pixelpacket).red=ScaleCharToQuantum( \
335        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
336    }
337 #define LBR03PacketGreen(pixelpacket) \
338    { \
339      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
340      (pixelpacket).green=ScaleCharToQuantum( \
341        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
342    }
343 #define LBR03PacketBlue(pixelpacket) \
344    { \
345      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
346      (pixelpacket).blue=ScaleCharToQuantum( \
347        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
348    }
349
350 #define LBR03PacketRGB(pixelpacket) \
351         { \
352         LBR03PacketRed((pixelpacket)); \
353         LBR03PacketGreen((pixelpacket)); \
354         LBR03PacketBlue((pixelpacket)); \
355         }
356
357 #define LBR03PixelRed(pixel) \
358    { \
359      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
360        & 0xe0; \
361      SetPixelRed(image, ScaleCharToQuantum( \
362        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
363    }
364 #define LBR03Green(pixel) \
365    { \
366      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
367        & 0xe0; \
368      SetPixelGreen(image, ScaleCharToQuantum( \
369        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
370    }
371 #define LBR03Blue(pixel) \
372    { \
373      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
374        & 0xe0; \
375      SetPixelBlue(image, ScaleCharToQuantum( \
376        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
377    }
378
379 #define LBR03RGB(pixel) \
380         { \
381         LBR03PixelRed((pixel)); \
382         LBR03Green((pixel)); \
383         LBR03Blue((pixel)); \
384         }
385
386 /* LBR04: Replicate top 4 bits */
387
388 #define LBR04PacketRed(pixelpacket) \
389    { \
390      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
391      (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
392    }
393 #define LBR04PacketGreen(pixelpacket) \
394    { \
395      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
396      (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
397    }
398 #define LBR04PacketBlue(pixelpacket) \
399    { \
400      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
401      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
402    }
403 #define LBR04PacketAlpha(pixelpacket) \
404    { \
405      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
406      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
407    }
408
409 #define LBR04PacketRGB(pixelpacket) \
410         { \
411         LBR04PacketRed((pixelpacket)); \
412         LBR04PacketGreen((pixelpacket)); \
413         LBR04PacketBlue((pixelpacket)); \
414         }
415
416 #define LBR04PacketRGBO(pixelpacket) \
417         { \
418         LBR04PacketRGB((pixelpacket)); \
419         LBR04PacketAlpha((pixelpacket)); \
420         }
421
422 #define LBR04PixelRed(pixel) \
423    { \
424      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
425        & 0xf0; \
426      SetPixelRed(image,\
427        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
428    }
429 #define LBR04PixelGreen(pixel) \
430    { \
431      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
432        & 0xf0; \
433      SetPixelGreen(image,\
434        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
435    }
436 #define LBR04PixelBlue(pixel) \
437    { \
438      unsigned char lbr_bits= \
439        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
440      SetPixelBlue(image,\
441        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
442    }
443 #define LBR04PixelAlpha(pixel) \
444    { \
445      unsigned char lbr_bits= \
446        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
447      SetPixelAlpha(image,\
448        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
449    }
450
451 #define LBR04PixelRGB(pixel) \
452         { \
453         LBR04PixelRed((pixel)); \
454         LBR04PixelGreen((pixel)); \
455         LBR04PixelBlue((pixel)); \
456         }
457
458 #define LBR04PixelRGBA(pixel) \
459         { \
460         LBR04PixelRGB((pixel)); \
461         LBR04PixelAlpha((pixel)); \
462         }
463
464
465 #if MAGICKCORE_QUANTUM_DEPTH > 8
466 /* LBR08: Replicate top 8 bits */
467
468 #define LBR08PacketRed(pixelpacket) \
469    { \
470      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
471      (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
472    }
473 #define LBR08PacketGreen(pixelpacket) \
474    { \
475      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
476      (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
477    }
478 #define LBR08PacketBlue(pixelpacket) \
479    { \
480      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
481      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
482    }
483 #define LBR08PacketAlpha(pixelpacket) \
484    { \
485      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
486      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
487    }
488
489 #define LBR08PacketRGB(pixelpacket) \
490         { \
491         LBR08PacketRed((pixelpacket)); \
492         LBR08PacketGreen((pixelpacket)); \
493         LBR08PacketBlue((pixelpacket)); \
494         }
495
496 #define LBR08PacketRGBO(pixelpacket) \
497         { \
498         LBR08PacketRGB((pixelpacket)); \
499         LBR08PacketAlpha((pixelpacket)); \
500         }
501
502 #define LBR08PixelRed(pixel) \
503    { \
504      unsigned char lbr_bits= \
505        ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
506      SetPixelRed(image,\
507        ScaleCharToQuantum((lbr_bits)), (pixel)); \
508    }
509 #define LBR08PixelGreen(pixel) \
510    { \
511      unsigned char lbr_bits= \
512        ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
513      SetPixelGreen(image,\
514        ScaleCharToQuantum((lbr_bits)), (pixel)); \
515    }
516 #define LBR08PixelBlue(pixel) \
517    { \
518      unsigned char lbr_bits= \
519        ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
520      SetPixelBlue(image,\
521        ScaleCharToQuantum((lbr_bits)), (pixel)); \
522    }
523 #define LBR08PixelAlpha(pixel) \
524    { \
525      unsigned char lbr_bits= \
526        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
527      SetPixelAlpha(image,\
528        ScaleCharToQuantum((lbr_bits)), (pixel)); \
529    }
530
531 #define LBR08PixelRGB(pixel) \
532         { \
533         LBR08PixelRed((pixel)); \
534         LBR08PixelGreen((pixel)); \
535         LBR08PixelBlue((pixel)); \
536         }
537
538 #define LBR08PixelRGBA(pixel) \
539         { \
540         LBR08PixelRGB((pixel)); \
541         LBR08PixelAlpha((pixel)); \
542         }
543 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
544
545
546 #if MAGICKCORE_QUANTUM_DEPTH > 16
547 /* LBR16: Replicate top 16 bits */
548
549 #define LBR16PacketRed(pixelpacket) \
550    { \
551      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
552      (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
553    }
554 #define LBR16PacketGreen(pixelpacket) \
555    { \
556      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
557      (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
558    }
559 #define LBR16PacketBlue(pixelpacket) \
560    { \
561      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
562      (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
563    }
564 #define LBR16PacketAlpha(pixelpacket) \
565    { \
566      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
567      (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
568    }
569
570 #define LBR16PacketRGB(pixelpacket) \
571         { \
572         LBR16PacketRed((pixelpacket)); \
573         LBR16PacketGreen((pixelpacket)); \
574         LBR16PacketBlue((pixelpacket)); \
575         }
576
577 #define LBR16PacketRGBO(pixelpacket) \
578         { \
579         LBR16PacketRGB((pixelpacket)); \
580         LBR16PacketAlpha((pixelpacket)); \
581         }
582
583 #define LBR16PixelRed(pixel) \
584    { \
585      unsigned short lbr_bits= \
586        ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
587      SetPixelRed(image,\
588        ScaleShortToQuantum((lbr_bits)),(pixel)); \
589    }
590 #define LBR16PixelGreen(pixel) \
591    { \
592      unsigned short lbr_bits= \
593        ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
594      SetPixelGreen(image,\
595        ScaleShortToQuantum((lbr_bits)),(pixel)); \
596    }
597 #define LBR16PixelBlue(pixel) \
598    { \
599      unsigned short lbr_bits= \
600        ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
601      SetPixelBlue(image,\
602        ScaleShortToQuantum((lbr_bits)),(pixel)); \
603    }
604 #define LBR16PixelAlpha(pixel) \
605    { \
606      unsigned short lbr_bits= \
607        ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
608      SetPixelAlpha(image,\
609        ScaleShortToQuantum((lbr_bits)),(pixel)); \
610    }
611
612 #define LBR16PixelRGB(pixel) \
613         { \
614         LBR16PixelRed((pixel)); \
615         LBR16PixelGreen((pixel)); \
616         LBR16PixelBlue((pixel)); \
617         }
618
619 #define LBR16PixelRGBA(pixel) \
620         { \
621         LBR16PixelRGB((pixel)); \
622         LBR16PixelAlpha((pixel)); \
623         }
624 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
625
626 /*
627   Establish thread safety.
628   setjmp/longjmp is claimed to be safe on these platforms:
629   setjmp/longjmp is alleged to be unsafe on these platforms:
630 */
631 #ifdef PNG_SETJMP_SUPPORTED
632 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
633 #   define IMPNG_SETJMP_NOT_THREAD_SAFE
634 # endif
635
636 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
637 static SemaphoreInfo
638   *ping_semaphore = (SemaphoreInfo *) NULL;
639 # endif
640 #endif
641
642 /*
643   This temporary until I set up malloc'ed object attributes array.
644   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
645   waste more memory.
646 */
647 #define MNG_MAX_OBJECTS 256
648
649 /*
650   If this not defined, spec is interpreted strictly.  If it is
651   defined, an attempt will be made to recover from some errors,
652   including
653       o global PLTE too short
654 */
655 #undef MNG_LOOSE
656
657 /*
658   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
659   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
660   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
661   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
662   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
663   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
664   will be enabled by default in libpng-1.2.0.
665 */
666 #ifdef PNG_MNG_FEATURES_SUPPORTED
667 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
668 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
669 #  endif
670 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
671 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
672 #  endif
673 #endif
674
675 /*
676   Maximum valid size_t in PNG/MNG chunks is (2^31)-1
677   This macro is only defined in libpng-1.0.3 and later.
678   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
679 */
680 #ifndef PNG_UINT_31_MAX
681 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
682 #endif
683
684 /*
685   Constant strings for known chunk types.  If you need to add a chunk,
686   add a string holding the name here.   To make the code more
687   portable, we use ASCII numbers like this, not characters.
688 */
689
690 static png_byte mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
691 static png_byte mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
692 static png_byte mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
693 static png_byte mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
694 static png_byte mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
695 static png_byte mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
696 static png_byte mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
697 static png_byte mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
698 static png_byte mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
699 static png_byte mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
700 static png_byte mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
701 static png_byte mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
702 static png_byte mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
703 static png_byte mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
704 static png_byte mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
705 static png_byte mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
706 static png_byte mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
707 static png_byte mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
708 static png_byte mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
709 static png_byte mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
710 static png_byte mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
711 static png_byte mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
712 static png_byte mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
713 static png_byte mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
714 static png_byte mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
715 static png_byte mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
716 static png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
717 static png_byte mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
718 static png_byte mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
719 static png_byte mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
720 static png_byte mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
721 static png_byte mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
722 static png_byte mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
723 static png_byte mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
724
725 #if defined(JNG_SUPPORTED)
726 static png_byte mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
727 static png_byte mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
728 static png_byte mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
729 static png_byte mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
730 static png_byte mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
731 static png_byte mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
732 #endif
733
734 #if 0
735 /* Other known chunks that are not yet supported by ImageMagick: */
736 static png_byte mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
737 static png_byte mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
738 static png_byte mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
739 static png_byte mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
740 static png_byte mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
741 static png_byte mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
742 static png_byte mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
743 #endif
744
745 typedef struct _MngBox
746 {
747   long
748     left,
749     right,
750     top,
751     bottom;
752 } MngBox;
753
754 typedef struct _MngPair
755 {
756   volatile long
757     a,
758     b;
759 } MngPair;
760
761 #ifdef MNG_OBJECT_BUFFERS
762 typedef struct _MngBuffer
763 {
764
765   size_t
766     height,
767     width;
768
769   Image
770     *image;
771
772   png_color
773     plte[256];
774
775   int
776     reference_count;
777
778   unsigned char
779     alpha_sample_depth,
780     compression_method,
781     color_type,
782     concrete,
783     filter_method,
784     frozen,
785     image_type,
786     interlace_method,
787     pixel_sample_depth,
788     plte_length,
789     sample_depth,
790     viewable;
791 } MngBuffer;
792 #endif
793
794 typedef struct _MngInfo
795 {
796
797 #ifdef MNG_OBJECT_BUFFERS
798   MngBuffer
799     *ob[MNG_MAX_OBJECTS];
800 #endif
801
802   Image *
803     image;
804
805   RectangleInfo
806     page;
807
808   int
809     adjoin,
810 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
811     bytes_in_read_buffer,
812     found_empty_plte,
813 #endif
814     equal_backgrounds,
815     equal_chrms,
816     equal_gammas,
817 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
818     defined(PNG_MNG_FEATURES_SUPPORTED)
819     equal_palettes,
820 #endif
821     equal_physs,
822     equal_srgbs,
823     framing_mode,
824     have_global_bkgd,
825     have_global_chrm,
826     have_global_gama,
827     have_global_phys,
828     have_global_sbit,
829     have_global_srgb,
830     have_saved_bkgd_index,
831     have_write_global_chrm,
832     have_write_global_gama,
833     have_write_global_plte,
834     have_write_global_srgb,
835     need_fram,
836     object_id,
837     old_framing_mode,
838     saved_bkgd_index;
839
840   int
841     new_number_colors;
842
843   ssize_t
844     image_found,
845     loop_count[256],
846     loop_iteration[256],
847     scenes_found,
848     x_off[MNG_MAX_OBJECTS],
849     y_off[MNG_MAX_OBJECTS];
850
851   MngBox
852     clip,
853     frame,
854     image_box,
855     object_clip[MNG_MAX_OBJECTS];
856
857   unsigned char
858     /* These flags could be combined into one byte */
859     exists[MNG_MAX_OBJECTS],
860     frozen[MNG_MAX_OBJECTS],
861     loop_active[256],
862     invisible[MNG_MAX_OBJECTS],
863     viewable[MNG_MAX_OBJECTS];
864
865   MagickOffsetType
866     loop_jump[256];
867
868   png_colorp
869     global_plte;
870
871   png_color_8
872     global_sbit;
873
874   png_byte
875 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
876     read_buffer[8],
877 #endif
878     global_trns[256];
879
880   float
881     global_gamma;
882
883   ChromaticityInfo
884     global_chrm;
885
886   RenderingIntent
887     global_srgb_intent;
888
889   unsigned int
890     delay,
891     global_plte_length,
892     global_trns_length,
893     global_x_pixels_per_unit,
894     global_y_pixels_per_unit,
895     mng_width,
896     mng_height,
897     ticks_per_second;
898
899   MagickBooleanType
900     need_blob;
901
902   unsigned int
903     IsPalette,
904     global_phys_unit_type,
905     basi_warning,
906     clon_warning,
907     dhdr_warning,
908     jhdr_warning,
909     magn_warning,
910     past_warning,
911     phyg_warning,
912     phys_warning,
913     sbit_warning,
914     show_warning,
915     mng_type,
916     write_mng,
917     write_png_colortype,
918     write_png_depth,
919     write_png_compression_level,
920     write_png_compression_strategy,
921     write_png_compression_filter,
922     write_png8,
923     write_png24,
924     write_png32,
925     write_png48,
926     write_png64;
927
928 #ifdef MNG_BASI_SUPPORTED
929   size_t
930     basi_width,
931     basi_height;
932
933   unsigned int
934     basi_depth,
935     basi_color_type,
936     basi_compression_method,
937     basi_filter_type,
938     basi_interlace_method,
939     basi_red,
940     basi_green,
941     basi_blue,
942     basi_alpha,
943     basi_viewable;
944 #endif
945
946   png_uint_16
947     magn_first,
948     magn_last,
949     magn_mb,
950     magn_ml,
951     magn_mr,
952     magn_mt,
953     magn_mx,
954     magn_my,
955     magn_methx,
956     magn_methy;
957
958   PixelInfo
959     mng_global_bkgd;
960
961   /* Added at version 6.6.6-7 */
962   MagickBooleanType
963     ping_exclude_bKGD,
964     ping_exclude_cHRM,
965     ping_exclude_date,
966     ping_exclude_EXIF,
967     ping_exclude_gAMA,
968     ping_exclude_iCCP,
969     /* ping_exclude_iTXt, */
970     ping_exclude_oFFs,
971     ping_exclude_pHYs,
972     ping_exclude_sRGB,
973     ping_exclude_tEXt,
974     ping_exclude_tRNS,
975     ping_exclude_vpAg,
976     ping_exclude_zCCP, /* hex-encoded iCCP */
977     ping_exclude_zTXt,
978     ping_preserve_colormap,
979   /* Added at version 6.8.5-7 */
980     ping_preserve_iCCP;
981
982 } MngInfo;
983 #endif /* VER */
984 \f
985 /*
986   Forward declarations.
987 */
988 static MagickBooleanType
989   WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
990
991 static MagickBooleanType
992   WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
993
994 #if defined(JNG_SUPPORTED)
995 static MagickBooleanType
996   WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
997 #endif
998
999 #if PNG_LIBPNG_VER > 10011
1000
1001
1002 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
1003 static MagickBooleanType
1004 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
1005 {
1006     /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
1007      *
1008      * This is true if the high byte and the next highest byte of
1009      * each sample of the image, the colormap, and the background color
1010      * are equal to each other.  We check this by seeing if the samples
1011      * are unchanged when we scale them down to 8 and back up to Quantum.
1012      *
1013      * We don't use the method GetImageDepth() because it doesn't check
1014      * background and doesn't handle PseudoClass specially.
1015      */
1016
1017 #define QuantumToCharToQuantumEqQuantum(quantum) \
1018   ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
1019
1020     MagickBooleanType
1021       ok_to_reduce=MagickFalse;
1022
1023     if (image->depth >= 16)
1024       {
1025
1026         const Quantum
1027           *p;
1028
1029         ok_to_reduce=
1030            QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
1031            QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
1032            QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
1033            MagickTrue : MagickFalse;
1034
1035         if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
1036           {
1037             int indx;
1038
1039             for (indx=0; indx < (ssize_t) image->colors; indx++)
1040               {
1041                 ok_to_reduce=(
1042                    QuantumToCharToQuantumEqQuantum(
1043                    image->colormap[indx].red) &&
1044                    QuantumToCharToQuantumEqQuantum(
1045                    image->colormap[indx].green) &&
1046                    QuantumToCharToQuantumEqQuantum(
1047                    image->colormap[indx].blue)) ?
1048                    MagickTrue : MagickFalse;
1049
1050                 if (ok_to_reduce == MagickFalse)
1051                    break;
1052               }
1053           }
1054
1055         if ((ok_to_reduce != MagickFalse) &&
1056             (image->storage_class != PseudoClass))
1057           {
1058             ssize_t
1059               y;
1060
1061             register ssize_t
1062               x;
1063
1064             for (y=0; y < (ssize_t) image->rows; y++)
1065             {
1066               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1067
1068               if (p == (const Quantum *) NULL)
1069                 {
1070                   ok_to_reduce = MagickFalse;
1071                   break;
1072                 }
1073
1074               for (x=(ssize_t) image->columns-1; x >= 0; x--)
1075               {
1076                 ok_to_reduce=
1077                    QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1078                    QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1079                    QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
1080                    MagickTrue : MagickFalse;
1081
1082                 if (ok_to_reduce == MagickFalse)
1083                   break;
1084
1085                 p+=GetPixelChannels(image);
1086               }
1087               if (x >= 0)
1088                 break;
1089             }
1090           }
1091
1092         if (ok_to_reduce != MagickFalse)
1093           {
1094             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1095                 "    OK to reduce PNG bit depth to 8 without loss of info");
1096           }
1097         else
1098           {
1099             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1100                 "    Not OK to reduce PNG bit depth to 8 without loss of info");
1101           }
1102       }
1103
1104     return ok_to_reduce;
1105 }
1106 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1107
1108 static const char* PngColorTypeToString(const unsigned int color_type)
1109 {
1110   const char
1111     *result = "Unknown";
1112
1113   switch (color_type)
1114     {
1115     case PNG_COLOR_TYPE_GRAY:
1116       result = "Gray";
1117       break;
1118     case PNG_COLOR_TYPE_GRAY_ALPHA:
1119       result = "Gray+Alpha";
1120       break;
1121     case PNG_COLOR_TYPE_PALETTE:
1122       result = "Palette";
1123       break;
1124     case PNG_COLOR_TYPE_RGB:
1125       result = "RGB";
1126       break;
1127     case PNG_COLOR_TYPE_RGB_ALPHA:
1128       result = "RGB+Alpha";
1129       break;
1130     }
1131
1132   return result;
1133 }
1134
1135 static int
1136 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1137 {
1138   switch (intent)
1139   {
1140     case PerceptualIntent:
1141        return 0;
1142
1143     case RelativeIntent:
1144        return 1;
1145
1146     case SaturationIntent:
1147        return 2;
1148
1149     case AbsoluteIntent:
1150        return 3;
1151
1152     default:
1153        return -1;
1154   }
1155 }
1156
1157 static RenderingIntent
1158 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1159 {
1160   switch (ping_intent)
1161   {
1162     case 0:
1163       return PerceptualIntent;
1164
1165     case 1:
1166       return RelativeIntent;
1167
1168     case 2:
1169       return SaturationIntent;
1170
1171     case 3:
1172       return AbsoluteIntent;
1173
1174     default:
1175       return UndefinedIntent;
1176     }
1177 }
1178
1179 static const char *
1180 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1181 {
1182   switch (ping_intent)
1183   {
1184     case 0:
1185       return "Perceptual Intent";
1186
1187     case 1:
1188       return "Relative Intent";
1189
1190     case 2:
1191       return "Saturation Intent";
1192
1193     case 3:
1194       return "Absolute Intent";
1195
1196     default:
1197       return "Undefined Intent";
1198     }
1199 }
1200
1201 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1202 {
1203   if (x > y)
1204     return(x);
1205
1206   return(y);
1207 }
1208
1209 static const char *
1210 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1211 {
1212   switch (ping_colortype)
1213   {
1214     case 0:
1215       return "Grayscale";
1216
1217     case 2:
1218       return "Truecolor";
1219
1220     case 3:
1221       return "Indexed";
1222
1223     case 4:
1224       return "GrayAlpha";
1225
1226     case 6:
1227       return "RGBA";
1228
1229     default:
1230       return "UndefinedColorType";
1231     }
1232 }
1233
1234
1235 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1236 {
1237   if (x < y)
1238     return(x);
1239
1240   return(y);
1241 }
1242 #endif /* PNG_LIBPNG_VER > 10011 */
1243 #endif /* MAGICKCORE_PNG_DELEGATE */
1244 \f
1245 /*
1246 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1247 %                                                                             %
1248 %                                                                             %
1249 %                                                                             %
1250 %   I s M N G                                                                 %
1251 %                                                                             %
1252 %                                                                             %
1253 %                                                                             %
1254 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1255 %
1256 %  IsMNG() returns MagickTrue if the image format type, identified by the
1257 %  magick string, is MNG.
1258 %
1259 %  The format of the IsMNG method is:
1260 %
1261 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1262 %
1263 %  A description of each parameter follows:
1264 %
1265 %    o magick: compare image format pattern against these bytes.
1266 %
1267 %    o length: Specifies the length of the magick string.
1268 %
1269 %
1270 */
1271 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1272 {
1273   if (length < 8)
1274     return(MagickFalse);
1275
1276   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1277     return(MagickTrue);
1278
1279   return(MagickFalse);
1280 }
1281 \f
1282 /*
1283 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1284 %                                                                             %
1285 %                                                                             %
1286 %                                                                             %
1287 %   I s J N G                                                                 %
1288 %                                                                             %
1289 %                                                                             %
1290 %                                                                             %
1291 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1292 %
1293 %  IsJNG() returns MagickTrue if the image format type, identified by the
1294 %  magick string, is JNG.
1295 %
1296 %  The format of the IsJNG method is:
1297 %
1298 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1299 %
1300 %  A description of each parameter follows:
1301 %
1302 %    o magick: compare image format pattern against these bytes.
1303 %
1304 %    o length: Specifies the length of the magick string.
1305 %
1306 %
1307 */
1308 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1309 {
1310   if (length < 8)
1311     return(MagickFalse);
1312
1313   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1314     return(MagickTrue);
1315
1316   return(MagickFalse);
1317 }
1318 \f
1319 /*
1320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1321 %                                                                             %
1322 %                                                                             %
1323 %                                                                             %
1324 %   I s P N G                                                                 %
1325 %                                                                             %
1326 %                                                                             %
1327 %                                                                             %
1328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1329 %
1330 %  IsPNG() returns MagickTrue if the image format type, identified by the
1331 %  magick string, is PNG.
1332 %
1333 %  The format of the IsPNG method is:
1334 %
1335 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1336 %
1337 %  A description of each parameter follows:
1338 %
1339 %    o magick: compare image format pattern against these bytes.
1340 %
1341 %    o length: Specifies the length of the magick string.
1342 %
1343 */
1344 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1345 {
1346   if (length < 8)
1347     return(MagickFalse);
1348
1349   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1350     return(MagickTrue);
1351
1352   return(MagickFalse);
1353 }
1354 \f
1355 #if defined(MAGICKCORE_PNG_DELEGATE)
1356 #if defined(__cplusplus) || defined(c_plusplus)
1357 extern "C" {
1358 #endif
1359
1360 #if (PNG_LIBPNG_VER > 10011)
1361 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1362 {
1363   unsigned char
1364     buffer[4];
1365
1366   assert(image != (Image *) NULL);
1367   assert(image->signature == MagickSignature);
1368   buffer[0]=(unsigned char) (value >> 24);
1369   buffer[1]=(unsigned char) (value >> 16);
1370   buffer[2]=(unsigned char) (value >> 8);
1371   buffer[3]=(unsigned char) value;
1372   return((size_t) WriteBlob(image,4,buffer));
1373 }
1374
1375 static void PNGLong(png_bytep p,png_uint_32 value)
1376 {
1377   *p++=(png_byte) ((value >> 24) & 0xff);
1378   *p++=(png_byte) ((value >> 16) & 0xff);
1379   *p++=(png_byte) ((value >> 8) & 0xff);
1380   *p++=(png_byte) (value & 0xff);
1381 }
1382
1383 #if defined(JNG_SUPPORTED)
1384 static void PNGsLong(png_bytep p,png_int_32 value)
1385 {
1386   *p++=(png_byte) ((value >> 24) & 0xff);
1387   *p++=(png_byte) ((value >> 16) & 0xff);
1388   *p++=(png_byte) ((value >> 8) & 0xff);
1389   *p++=(png_byte) (value & 0xff);
1390 }
1391 #endif
1392
1393 static void PNGShort(png_bytep p,png_uint_16 value)
1394 {
1395   *p++=(png_byte) ((value >> 8) & 0xff);
1396   *p++=(png_byte) (value & 0xff);
1397 }
1398
1399 static void PNGType(png_bytep p,png_bytep type)
1400 {
1401   (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1402 }
1403
1404 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1405    size_t length)
1406 {
1407   if (logging != MagickFalse)
1408     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1409       "  Writing %c%c%c%c chunk, length: %.20g",
1410       type[0],type[1],type[2],type[3],(double) length);
1411 }
1412 #endif /* PNG_LIBPNG_VER > 10011 */
1413
1414 #if defined(__cplusplus) || defined(c_plusplus)
1415 }
1416 #endif
1417
1418 #if PNG_LIBPNG_VER > 10011
1419 /*
1420 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1421 %                                                                             %
1422 %                                                                             %
1423 %                                                                             %
1424 %   R e a d P N G I m a g e                                                   %
1425 %                                                                             %
1426 %                                                                             %
1427 %                                                                             %
1428 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1429 %
1430 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1431 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1432 %  allocates the memory necessary for the new Image structure and returns a
1433 %  pointer to the new image or set of images.
1434 %
1435 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1436 %
1437 %  The format of the ReadPNGImage method is:
1438 %
1439 %      Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1440 %
1441 %  A description of each parameter follows:
1442 %
1443 %    o image_info: the image info.
1444 %
1445 %    o exception: return any errors or warnings in this structure.
1446 %
1447 %  To do, more or less in chronological order (as of version 5.5.2,
1448 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1449 %
1450 %    Get 16-bit cheap transparency working.
1451 %
1452 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1453 %
1454 %    Preserve all unknown and not-yet-handled known chunks found in input
1455 %    PNG file and copy them into output PNG files according to the PNG
1456 %    copying rules.
1457 %
1458 %    (At this point, PNG encoding should be in full MNG compliance)
1459 %
1460 %    Provide options for choice of background to use when the MNG BACK
1461 %    chunk is not present or is not mandatory (i.e., leave transparent,
1462 %    user specified, MNG BACK, PNG bKGD)
1463 %
1464 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1465 %    efficiently by linking in the duplicate frames.].
1466 %
1467 %    Decode and act on the MHDR simplicity profile (offer option to reject
1468 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1469 %
1470 %    Upgrade to full MNG without Delta-PNG.
1471 %
1472 %        o  BACK [done a while ago except for background image ID]
1473 %        o  MOVE [done 15 May 1999]
1474 %        o  CLIP [done 15 May 1999]
1475 %        o  DISC [done 19 May 1999]
1476 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1477 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1478 %        o  SHOW
1479 %        o  PAST
1480 %        o  BASI
1481 %        o  MNG-level tEXt/iTXt/zTXt
1482 %        o  pHYg
1483 %        o  pHYs
1484 %        o  sBIT
1485 %        o  bKGD
1486 %        o  iTXt (wait for libpng implementation).
1487 %
1488 %    Use the scene signature to discover when an identical scene is
1489 %    being reused, and just point to the original image->exception instead
1490 %    of storing another set of pixels.  This not specific to MNG
1491 %    but could be applied generally.
1492 %
1493 %    Upgrade to full MNG with Delta-PNG.
1494 %
1495 %    JNG tEXt/iTXt/zTXt
1496 %
1497 %    We will not attempt to read files containing the CgBI chunk.
1498 %    They are really Xcode files meant for display on the iPhone.
1499 %    These are not valid PNG files and it is impossible to recover
1500 %    the original PNG from files that have been converted to Xcode-PNG,
1501 %    since irretrievable loss of color data has occurred due to the
1502 %    use of premultiplied alpha.
1503 */
1504
1505 #if defined(__cplusplus) || defined(c_plusplus)
1506 extern "C" {
1507 #endif
1508
1509 /*
1510   This the function that does the actual reading of data.  It is
1511   the same as the one supplied in libpng, except that it receives the
1512   datastream from the ReadBlob() function instead of standard input.
1513 */
1514 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1515 {
1516   Image
1517     *image;
1518
1519   image=(Image *) png_get_io_ptr(png_ptr);
1520   if (length)
1521     {
1522       png_size_t
1523         check;
1524
1525       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1526       if (check != length)
1527         {
1528           char
1529             msg[MaxTextExtent];
1530
1531           (void) FormatLocaleString(msg,MaxTextExtent,
1532             "Expected %.20g bytes; found %.20g bytes",(double) length,
1533             (double) check);
1534           png_warning(png_ptr,msg);
1535           png_error(png_ptr,"Read Exception");
1536         }
1537     }
1538 }
1539
1540 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1541     !defined(PNG_MNG_FEATURES_SUPPORTED)
1542 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1543  * older than libpng-1.0.3a, which was the first to allow the empty
1544  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1545  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1546  * encountered after an empty PLTE, so we have to look ahead for bKGD
1547  * chunks and remove them from the datastream that is passed to libpng,
1548  * and store their contents for later use.
1549  */
1550 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1551 {
1552   MngInfo
1553     *mng_info;
1554
1555   Image
1556     *image;
1557
1558   png_size_t
1559     check;
1560
1561   register ssize_t
1562     i;
1563
1564   i=0;
1565   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1566   image=(Image *) mng_info->image;
1567   while (mng_info->bytes_in_read_buffer && length)
1568   {
1569     data[i]=mng_info->read_buffer[i];
1570     mng_info->bytes_in_read_buffer--;
1571     length--;
1572     i++;
1573   }
1574   if (length)
1575     {
1576       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1577
1578       if (check != length)
1579         png_error(png_ptr,"Read Exception");
1580
1581       if (length == 4)
1582         {
1583           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1584               (data[3] == 0))
1585             {
1586               check=(png_size_t) ReadBlob(image,(size_t) length,
1587                 (char *) mng_info->read_buffer);
1588               mng_info->read_buffer[4]=0;
1589               mng_info->bytes_in_read_buffer=4;
1590               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1591                 mng_info->found_empty_plte=MagickTrue;
1592               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1593                 {
1594                   mng_info->found_empty_plte=MagickFalse;
1595                   mng_info->have_saved_bkgd_index=MagickFalse;
1596                 }
1597             }
1598
1599           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1600               (data[3] == 1))
1601             {
1602               check=(png_size_t) ReadBlob(image,(size_t) length,
1603                 (char *) mng_info->read_buffer);
1604               mng_info->read_buffer[4]=0;
1605               mng_info->bytes_in_read_buffer=4;
1606               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1607                 if (mng_info->found_empty_plte)
1608                   {
1609                     /*
1610                       Skip the bKGD data byte and CRC.
1611                     */
1612                     check=(png_size_t)
1613                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1614                     check=(png_size_t) ReadBlob(image,(size_t) length,
1615                       (char *) mng_info->read_buffer);
1616                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1617                     mng_info->have_saved_bkgd_index=MagickTrue;
1618                     mng_info->bytes_in_read_buffer=0;
1619                   }
1620             }
1621         }
1622     }
1623 }
1624 #endif
1625
1626 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1627 {
1628   Image
1629     *image;
1630
1631   image=(Image *) png_get_io_ptr(png_ptr);
1632   if (length)
1633     {
1634       png_size_t
1635         check;
1636
1637       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1638
1639       if (check != length)
1640         png_error(png_ptr,"WriteBlob Failed");
1641     }
1642 }
1643
1644 static void png_flush_data(png_structp png_ptr)
1645 {
1646   (void) png_ptr;
1647 }
1648
1649 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1650 static int PalettesAreEqual(Image *a,Image *b)
1651 {
1652   ssize_t
1653     i;
1654
1655   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1656     return((int) MagickFalse);
1657
1658   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1659     return((int) MagickFalse);
1660
1661   if (a->colors != b->colors)
1662     return((int) MagickFalse);
1663
1664   for (i=0; i < (ssize_t) a->colors; i++)
1665   {
1666     if ((a->colormap[i].red != b->colormap[i].red) ||
1667         (a->colormap[i].green != b->colormap[i].green) ||
1668         (a->colormap[i].blue != b->colormap[i].blue))
1669       return((int) MagickFalse);
1670   }
1671
1672   return((int) MagickTrue);
1673 }
1674 #endif
1675
1676 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1677 {
1678   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1679       mng_info->exists[i] && !mng_info->frozen[i])
1680     {
1681 #ifdef MNG_OBJECT_BUFFERS
1682       if (mng_info->ob[i] != (MngBuffer *) NULL)
1683         {
1684           if (mng_info->ob[i]->reference_count > 0)
1685             mng_info->ob[i]->reference_count--;
1686
1687           if (mng_info->ob[i]->reference_count == 0)
1688             {
1689               if (mng_info->ob[i]->image != (Image *) NULL)
1690                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1691
1692               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1693             }
1694         }
1695       mng_info->ob[i]=(MngBuffer *) NULL;
1696 #endif
1697       mng_info->exists[i]=MagickFalse;
1698       mng_info->invisible[i]=MagickFalse;
1699       mng_info->viewable[i]=MagickFalse;
1700       mng_info->frozen[i]=MagickFalse;
1701       mng_info->x_off[i]=0;
1702       mng_info->y_off[i]=0;
1703       mng_info->object_clip[i].left=0;
1704       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1705       mng_info->object_clip[i].top=0;
1706       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1707     }
1708 }
1709
1710 static void MngInfoFreeStruct(MngInfo *mng_info,
1711     MagickBooleanType *have_mng_structure)
1712 {
1713   if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1714     {
1715       register ssize_t
1716         i;
1717
1718       for (i=1; i < MNG_MAX_OBJECTS; i++)
1719         MngInfoDiscardObject(mng_info,i);
1720
1721       if (mng_info->global_plte != (png_colorp) NULL)
1722         mng_info->global_plte=(png_colorp)
1723           RelinquishMagickMemory(mng_info->global_plte);
1724
1725       mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1726       *have_mng_structure=MagickFalse;
1727     }
1728 }
1729
1730 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1731 {
1732   MngBox
1733     box;
1734
1735   box=box1;
1736   if (box.left < box2.left)
1737     box.left=box2.left;
1738
1739   if (box.top < box2.top)
1740     box.top=box2.top;
1741
1742   if (box.right > box2.right)
1743     box.right=box2.right;
1744
1745   if (box.bottom > box2.bottom)
1746     box.bottom=box2.bottom;
1747
1748   return box;
1749 }
1750
1751 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1752 {
1753    MngBox
1754       box;
1755
1756   /*
1757     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1758   */
1759   box.left=(ssize_t) ((p[0]  << 24) | (p[1]  << 16) | (p[2]  << 8) | p[3]);
1760   box.right=(ssize_t) ((p[4]  << 24) | (p[5]  << 16) | (p[6]  << 8) | p[7]);
1761   box.top=(ssize_t) ((p[8]  << 24) | (p[9]  << 16) | (p[10] << 8) | p[11]);
1762   box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1763   if (delta_type != 0)
1764     {
1765       box.left+=previous_box.left;
1766       box.right+=previous_box.right;
1767       box.top+=previous_box.top;
1768       box.bottom+=previous_box.bottom;
1769     }
1770
1771   return(box);
1772 }
1773
1774 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1775   unsigned char *p)
1776 {
1777   MngPair
1778     pair;
1779   /*
1780     Read two ssize_ts from CLON, MOVE or PAST chunk
1781   */
1782   pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1783   pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1784
1785   if (delta_type != 0)
1786     {
1787       pair.a+=previous_pair.a;
1788       pair.b+=previous_pair.b;
1789     }
1790
1791   return(pair);
1792 }
1793
1794 static long mng_get_long(unsigned char *p)
1795 {
1796   return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1797 }
1798
1799 typedef struct _PNGErrorInfo
1800 {
1801   Image
1802     *image;
1803
1804   ExceptionInfo
1805     *exception;
1806 } PNGErrorInfo;
1807
1808 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1809 {
1810   ExceptionInfo
1811     *exception;
1812
1813   Image
1814     *image;
1815
1816   PNGErrorInfo
1817     *error_info;
1818
1819   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1820   image=error_info->image;
1821   exception=error_info->exception;
1822
1823   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1824     "  libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1825
1826   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1827     "`%s'",image->filename);
1828
1829 #if (PNG_LIBPNG_VER < 10500)
1830   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1831    * are building with libpng-1.4.x and can be ignored.
1832    */
1833   longjmp(ping->jmpbuf,1);
1834 #else
1835   png_longjmp(ping,1);
1836 #endif
1837 }
1838
1839 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1840 {
1841   ExceptionInfo
1842     *exception;
1843
1844   Image
1845     *image;
1846
1847   PNGErrorInfo
1848     *error_info;
1849
1850   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1851     png_error(ping, message);
1852
1853   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1854   image=error_info->image;
1855   exception=error_info->exception;
1856   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1857     "  libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1858
1859   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1860     message,"`%s'",image->filename);
1861 }
1862
1863 #ifdef PNG_USER_MEM_SUPPORTED
1864 #if PNG_LIBPNG_VER >= 10400
1865 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1866 #else
1867 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1868 #endif
1869 {
1870   (void) png_ptr;
1871   return((png_voidp) AcquireMagickMemory((size_t) size));
1872 }
1873
1874 /*
1875   Free a pointer.  It is removed from the list at the same time.
1876 */
1877 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1878 {
1879   (void) png_ptr;
1880   ptr=RelinquishMagickMemory(ptr);
1881   return((png_free_ptr) NULL);
1882 }
1883 #endif
1884
1885 #if defined(__cplusplus) || defined(c_plusplus)
1886 }
1887 #endif
1888
1889 static int
1890 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1891    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1892 {
1893   register ssize_t
1894     i;
1895
1896   register unsigned char
1897     *dp;
1898
1899   register png_charp
1900     sp;
1901
1902   png_uint_32
1903     length,
1904     nibbles;
1905
1906   StringInfo
1907     *profile;
1908
1909   const unsigned char
1910     unhex[103]={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,0, 0,0,0,0,0,0,0,0,0,0,
1912                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1913                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1914                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1915                  13,14,15};
1916
1917   sp=text[ii].text+1;
1918   /* look for newline */
1919   while (*sp != '\n')
1920      sp++;
1921
1922   /* look for length */
1923   while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1924      sp++;
1925
1926   length=(png_uint_32) StringToLong(sp);
1927
1928   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1929        "      length: %lu",(unsigned long) length);
1930
1931   while (*sp != ' ' && *sp != '\n')
1932      sp++;
1933
1934   /* allocate space */
1935   if (length == 0)
1936   {
1937     png_warning(ping,"invalid profile length");
1938     return(MagickFalse);
1939   }
1940
1941   profile=BlobToStringInfo((const void *) NULL,length);
1942
1943   if (profile == (StringInfo *) NULL)
1944   {
1945     png_warning(ping, "unable to copy profile");
1946     return(MagickFalse);
1947   }
1948
1949   /* copy profile, skipping white space and column 1 "=" signs */
1950   dp=GetStringInfoDatum(profile);
1951   nibbles=length*2;
1952
1953   for (i=0; i < (ssize_t) nibbles; i++)
1954   {
1955     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1956     {
1957       if (*sp == '\0')
1958         {
1959           png_warning(ping, "ran out of profile data");
1960           profile=DestroyStringInfo(profile);
1961           return(MagickFalse);
1962         }
1963       sp++;
1964     }
1965
1966     if (i%2 == 0)
1967       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1968
1969     else
1970       (*dp++)+=unhex[(int) *sp++];
1971   }
1972   /*
1973     We have already read "Raw profile type.
1974   */
1975   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1976   profile=DestroyStringInfo(profile);
1977
1978   if (image_info->verbose)
1979     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1980
1981   return MagickTrue;
1982 }
1983
1984 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1985 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1986 {
1987   Image
1988     *image;
1989
1990
1991   /* The unknown chunk structure contains the chunk data:
1992      png_byte name[5];
1993      png_byte *data;
1994      png_size_t size;
1995
1996      Note that libpng has already taken care of the CRC handling.
1997   */
1998
1999   LogMagickEvent(CoderEvent,GetMagickModule(),
2000      " read_vpag_chunk: found %c%c%c%c chunk",
2001        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
2002
2003   if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
2004       chunk->name[2] != 65 ||chunk-> name[3] != 103)
2005     return(0); /* Did not recognize */
2006
2007   /* recognized vpAg */
2008
2009   if (chunk->size != 9)
2010     return(-1); /* Error return */
2011
2012   if (chunk->data[8] != 0)
2013     return(0);  /* ImageMagick requires pixel units */
2014
2015   image=(Image *) png_get_user_chunk_ptr(ping);
2016
2017   image->page.width=(size_t) ((chunk->data[0] << 24) |
2018      (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
2019
2020   image->page.height=(size_t) ((chunk->data[4] << 24) |
2021      (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
2022
2023   /* Return one of the following: */
2024      /* return(-n);  chunk had an error */
2025      /* return(0);  did not recognize */
2026      /* return(n);  success */
2027
2028   return(1);
2029
2030 }
2031 #endif
2032
2033 /*
2034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2035 %                                                                             %
2036 %                                                                             %
2037 %                                                                             %
2038 %   R e a d O n e P N G I m a g e                                             %
2039 %                                                                             %
2040 %                                                                             %
2041 %                                                                             %
2042 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2043 %
2044 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2045 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
2046 %  necessary for the new Image structure and returns a pointer to the new
2047 %  image.
2048 %
2049 %  The format of the ReadOnePNGImage method is:
2050 %
2051 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2052 %         ExceptionInfo *exception)
2053 %
2054 %  A description of each parameter follows:
2055 %
2056 %    o mng_info: Specifies a pointer to a MngInfo structure.
2057 %
2058 %    o image_info: the image info.
2059 %
2060 %    o exception: return any errors or warnings in this structure.
2061 %
2062 */
2063 static Image *ReadOnePNGImage(MngInfo *mng_info,
2064     const ImageInfo *image_info, ExceptionInfo *exception)
2065 {
2066   /* Read one PNG image */
2067
2068   /* To do: Read the tIME chunk into the date:modify property */
2069   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2070
2071   Image
2072     *image;
2073
2074   char
2075     im_vers[32],
2076     libpng_runv[32],
2077     libpng_vers[32],
2078     zlib_runv[32],
2079     zlib_vers[32];
2080
2081   int
2082     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2083     num_raw_profiles,
2084     num_text,
2085     num_text_total,
2086     num_passes,
2087     number_colors,
2088     pass,
2089     ping_bit_depth,
2090     ping_color_type,
2091     ping_file_depth,
2092     ping_interlace_method,
2093     ping_compression_method,
2094     ping_filter_method,
2095     ping_num_trans,
2096     unit_type;
2097
2098   double
2099     file_gamma;
2100
2101   MagickBooleanType
2102     logging,
2103     ping_found_cHRM,
2104     ping_found_gAMA,
2105     ping_found_iCCP,
2106     ping_found_sRGB,
2107     ping_found_sRGB_cHRM,
2108     ping_preserve_iCCP,
2109     status;
2110
2111   MemoryInfo
2112     *volatile pixel_info;
2113
2114   PixelInfo
2115     transparent_color;
2116
2117   PNGErrorInfo
2118     error_info;
2119
2120   png_bytep
2121      ping_trans_alpha;
2122
2123   png_color_16p
2124      ping_background,
2125      ping_trans_color;
2126
2127   png_info
2128     *end_info,
2129     *ping_info;
2130
2131   png_struct
2132     *ping;
2133
2134   png_textp
2135     text;
2136
2137   png_uint_32
2138     ping_height,
2139     ping_width,
2140     x_resolution,
2141     y_resolution;
2142
2143   QuantumInfo
2144     *quantum_info;
2145
2146   ssize_t
2147     ping_rowbytes,
2148     y;
2149
2150   register unsigned char
2151     *p;
2152
2153   register ssize_t
2154     i,
2155     x;
2156
2157   register Quantum
2158     *q;
2159
2160   size_t
2161     length,
2162     row_offset;
2163
2164   ssize_t
2165     j;
2166
2167   unsigned char
2168     *ping_pixels;
2169
2170 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2171   png_byte unused_chunks[]=
2172   {
2173     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2174     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2175     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2176     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2177     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2178     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2179 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2180                           /* ignore the APNG chunks */
2181      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2182     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2183     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2184 #endif
2185   };
2186 #endif
2187
2188   /* Define these outside of the following "if logging()" block so they will
2189    * show in debuggers.
2190    */
2191   *im_vers='\0';
2192   (void) ConcatenateMagickString(im_vers,
2193          MagickLibVersionText,32);
2194   (void) ConcatenateMagickString(im_vers,
2195          MagickLibAddendum,32);
2196
2197   *libpng_vers='\0';
2198   (void) ConcatenateMagickString(libpng_vers,
2199          PNG_LIBPNG_VER_STRING,32);
2200   *libpng_runv='\0';
2201   (void) ConcatenateMagickString(libpng_runv,
2202          png_get_libpng_ver(NULL),32);
2203
2204   *zlib_vers='\0';
2205   (void) ConcatenateMagickString(zlib_vers,
2206          ZLIB_VERSION,32);
2207   *zlib_runv='\0';
2208   (void) ConcatenateMagickString(zlib_runv,
2209          zlib_version,32);
2210
2211   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2212        "  Enter ReadOnePNGImage()\n"
2213        "    IM version     = %s\n"
2214        "    Libpng version = %s",
2215        im_vers, libpng_vers);
2216
2217   if (logging != MagickFalse)
2218   {
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:\n"
2258        "      image->alpha_trait=%d"
2259        "      image->rendering_intent=%d\n"
2260        "      image->colorspace=%d\n"
2261        "      image->gamma=%f",
2262        (int) image->alpha_trait, (int) image->rendering_intent,
2263        (int) image->colorspace, image->gamma);
2264   }
2265   intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2266
2267   /* Set to an out-of-range color unless tRNS chunk is present */
2268   transparent_color.red=65537;
2269   transparent_color.green=65537;
2270   transparent_color.blue=65537;
2271   transparent_color.alpha=65537;
2272
2273   number_colors=0;
2274   num_text = 0;
2275   num_text_total = 0;
2276   num_raw_profiles = 0;
2277
2278   ping_found_cHRM = MagickFalse;
2279   ping_found_gAMA = MagickFalse;
2280   ping_found_iCCP = MagickFalse;
2281   ping_found_sRGB = MagickFalse;
2282   ping_found_sRGB_cHRM = MagickFalse;
2283   ping_preserve_iCCP = MagickFalse;
2284
2285
2286   /*
2287     Allocate the PNG structures
2288   */
2289 #ifdef PNG_USER_MEM_SUPPORTED
2290  error_info.image=image;
2291  error_info.exception=exception;
2292  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2293    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2294    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2295 #else
2296   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2297     MagickPNGErrorHandler,MagickPNGWarningHandler);
2298 #endif
2299   if (ping == (png_struct *) NULL)
2300     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2301
2302   ping_info=png_create_info_struct(ping);
2303
2304   if (ping_info == (png_info *) NULL)
2305     {
2306       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2307       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2308     }
2309
2310   end_info=png_create_info_struct(ping);
2311
2312   if (end_info == (png_info *) NULL)
2313     {
2314       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2315       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2316     }
2317
2318   pixel_info=(MemoryInfo *) NULL;
2319
2320   if (setjmp(png_jmpbuf(ping)))
2321     {
2322       /*
2323         PNG image is corrupt.
2324       */
2325       png_destroy_read_struct(&ping,&ping_info,&end_info);
2326
2327 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2328       UnlockSemaphoreInfo(ping_semaphore);
2329 #endif
2330
2331       if (pixel_info != (MemoryInfo *) NULL)
2332         pixel_info=RelinquishVirtualMemory(pixel_info);
2333
2334       if (logging != MagickFalse)
2335         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2336           "  exit ReadOnePNGImage() with error.");
2337
2338       if (image != (Image *) NULL)
2339         {
2340           InheritException(exception,exception);
2341           image->columns=0;
2342         }
2343
2344       return(GetFirstImageInList(image));
2345     }
2346
2347   /* {  For navigation to end of SETJMP-protected block.  Within this
2348    *    block, use png_error() instead of Throwing an Exception, to ensure
2349    *    that libpng is able to clean up, and that the semaphore is unlocked.
2350    */
2351
2352 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2353   LockSemaphoreInfo(ping_semaphore);
2354 #endif
2355
2356 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2357   /* Allow benign errors */
2358   png_set_benign_errors(ping, 1);
2359 #endif
2360
2361   /*
2362     Prepare PNG for reading.
2363   */
2364
2365   mng_info->image_found++;
2366   png_set_sig_bytes(ping,8);
2367
2368   if (LocaleCompare(image_info->magick,"MNG") == 0)
2369     {
2370 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2371       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2372       png_set_read_fn(ping,image,png_get_data);
2373 #else
2374 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2375       png_permit_empty_plte(ping,MagickTrue);
2376       png_set_read_fn(ping,image,png_get_data);
2377 #else
2378       mng_info->image=image;
2379       mng_info->bytes_in_read_buffer=0;
2380       mng_info->found_empty_plte=MagickFalse;
2381       mng_info->have_saved_bkgd_index=MagickFalse;
2382       png_set_read_fn(ping,mng_info,mng_get_data);
2383 #endif
2384 #endif
2385     }
2386
2387   else
2388     png_set_read_fn(ping,image,png_get_data);
2389
2390   {
2391     const char
2392       *value;
2393
2394     value=GetImageOption(image_info,"profile:skip");
2395
2396     if (IsOptionMember("ICC",value) == MagickFalse)
2397     {
2398
2399        value=GetImageOption(image_info,"png:preserve-iCCP");
2400
2401        if (value == NULL)
2402           value=GetImageArtifact(image,"png:preserve-iCCP");
2403
2404        if (value != NULL)
2405           ping_preserve_iCCP=MagickTrue;
2406
2407 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2408        /* Don't let libpng check for ICC/sRGB profile because we're going
2409         * to do that anyway.  This feature was added at libpng-1.6.12.
2410         * If logging, go ahead and check and issue a warning as appropriate.
2411         */
2412        if (logging == MagickFalse)
2413           png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2414 #endif
2415     }
2416 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2417     else
2418     {
2419        png_set_keep_unknown_chunks(ping, 1, mng_iCCP, 1);
2420     }
2421 #endif
2422   }
2423 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2424   /* Ignore unused chunks and all unknown chunks except for vpAg */
2425 #if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2426   png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2427 #else
2428   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2429 #endif
2430   png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2431   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2432      (int)sizeof(unused_chunks)/5);
2433   /* Callback for other unknown chunks */
2434   png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2435 #endif
2436
2437 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2438 #  if (PNG_LIBPNG_VER >= 10400)
2439     /* Limit the size of the chunk storage cache used for sPLT, text,
2440      * and unknown chunks.
2441      */
2442     png_set_chunk_cache_max(ping, 32767);
2443 #  endif
2444 #endif
2445
2446 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2447     /* Disable new libpng-1.5.10 feature */
2448     png_set_check_for_invalid_index (ping, 0);
2449 #endif
2450
2451 #if (PNG_LIBPNG_VER < 10400)
2452 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2453    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2454   /* Disable thread-unsafe features of pnggccrd */
2455   if (png_access_version_number() >= 10200)
2456   {
2457     png_uint_32 mmx_disable_mask=0;
2458     png_uint_32 asm_flags;
2459
2460     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2461                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2462                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2463                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2464     asm_flags=png_get_asm_flags(ping);
2465     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2466   }
2467 #  endif
2468 #endif
2469
2470   png_read_info(ping,ping_info);
2471
2472   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2473                &ping_bit_depth,&ping_color_type,
2474                &ping_interlace_method,&ping_compression_method,
2475                &ping_filter_method);
2476
2477   ping_file_depth = ping_bit_depth;
2478
2479   /* Swap bytes if requested */
2480   if (ping_file_depth == 16)
2481   {
2482      const char
2483        *value;
2484  
2485      value=GetImageOption(image_info,"png:swap-bytes");
2486  
2487      if (value == NULL)
2488         value=GetImageArtifact(image,"png:swap-bytes");
2489  
2490      if (value != NULL)
2491         png_set_swap(ping);
2492   }
2493
2494   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2495   {
2496       char
2497         msg[MaxTextExtent];
2498
2499       (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2500       (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2501
2502       (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2503       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2504   }
2505
2506   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2507                       &ping_trans_color);
2508
2509   (void) png_get_bKGD(ping, ping_info, &ping_background);
2510
2511   if (ping_bit_depth < 8)
2512     {
2513        png_set_packing(ping);
2514        ping_bit_depth = 8;
2515     }
2516
2517   image->depth=ping_bit_depth;
2518   image->depth=GetImageQuantumDepth(image,MagickFalse);
2519   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2520
2521   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2522       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2523     {
2524       image->rendering_intent=UndefinedIntent;
2525       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2526       image->gamma=1.000;
2527       (void) ResetMagickMemory(&image->chromaticity,0,
2528         sizeof(image->chromaticity));
2529     }
2530
2531   if (logging != MagickFalse)
2532     {
2533       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2534         "    PNG width: %.20g, height: %.20g\n"
2535         "    PNG color_type: %d, bit_depth: %d\n"
2536         "    PNG compression_method: %d\n"
2537         "    PNG interlace_method: %d, filter_method: %d",
2538         (double) ping_width, (double) ping_height,
2539         ping_color_type, ping_bit_depth,
2540         ping_compression_method,
2541         ping_interlace_method,ping_filter_method);
2542
2543     }
2544
2545   if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2546     {
2547       ping_found_iCCP=MagickTrue;
2548       if (logging != MagickFalse)
2549         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2550           "    Found PNG iCCP chunk.");
2551     }
2552
2553   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2554     {
2555       ping_found_gAMA=MagickTrue;
2556       if (logging != MagickFalse)
2557         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2558           "    Found PNG gAMA chunk.");
2559     }
2560
2561   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2562     {
2563       ping_found_cHRM=MagickTrue;
2564       if (logging != MagickFalse)
2565         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2566           "    Found PNG cHRM chunk.");
2567     }
2568
2569   if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2570       PNG_INFO_sRGB))
2571     {
2572       ping_found_sRGB=MagickTrue;
2573       if (logging != MagickFalse)
2574         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2575           "    Found PNG sRGB chunk.");
2576     }
2577
2578 #ifdef PNG_READ_iCCP_SUPPORTED
2579     if (ping_found_iCCP !=MagickTrue &&
2580       ping_found_sRGB != MagickTrue &&
2581       png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2582     {
2583       ping_found_iCCP=MagickTrue;
2584       if (logging != MagickFalse)
2585         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2586           "    Found PNG iCCP chunk.");
2587     }
2588
2589   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2590     {
2591       int
2592         compression;
2593
2594 #if (PNG_LIBPNG_VER < 10500)
2595       png_charp
2596         info;
2597 #else
2598       png_bytep
2599         info;
2600 #endif
2601
2602       png_charp
2603         name;
2604
2605       png_uint_32
2606         profile_length;
2607
2608       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2609         &profile_length);
2610
2611       if (profile_length != 0)
2612         {
2613           StringInfo
2614             *profile;
2615
2616           if (logging != MagickFalse)
2617             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2618               "    Reading PNG iCCP chunk.");
2619
2620           profile=BlobToStringInfo(info,profile_length);
2621
2622           if (profile == (StringInfo *) NULL)
2623           {
2624             png_warning(ping, "ICC profile is NULL");
2625             profile=DestroyStringInfo(profile);
2626           }
2627           else
2628           {
2629             if (ping_preserve_iCCP == MagickFalse)
2630             {
2631                  int
2632                    icheck,
2633                    got_crc=0;
2634
2635
2636                  png_uint_32
2637                    length,
2638                    profile_crc=0;
2639
2640                  unsigned char
2641                    *data;
2642
2643                  length=(png_uint_32) GetStringInfoLength(profile);
2644
2645                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2646                  {
2647                    if (length == sRGB_info[icheck].len)
2648                    {
2649                      if (got_crc == 0)
2650                      {
2651                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2652                          "    Got a %lu-byte ICC profile (potentially sRGB)",
2653                          (unsigned long) length);
2654
2655                        data=GetStringInfoDatum(profile);
2656                        profile_crc=crc32(0,data,length);
2657
2658                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2659                            "      with crc=%8x",(unsigned int) profile_crc);
2660                        got_crc++;
2661                      }
2662
2663                      if (profile_crc == sRGB_info[icheck].crc)
2664                      {
2665                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2666                             "      It is sRGB with rendering intent = %s",
2667                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
2668                              sRGB_info[icheck].intent));
2669                         if (image->rendering_intent==UndefinedIntent)
2670                         {
2671                           image->rendering_intent=
2672                           Magick_RenderingIntent_from_PNG_RenderingIntent(
2673                              sRGB_info[icheck].intent);
2674                         }
2675                         break;
2676                      }
2677                    }
2678                  }
2679                  if (sRGB_info[icheck].len == 0)
2680                  {
2681                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2682                         "    Got a %lu-byte ICC profile not recognized as sRGB",
2683                         (unsigned long) length);
2684                     (void) SetImageProfile(image,"icc",profile,exception);
2685                  }
2686             }
2687             else /* Preserve-iCCP */
2688             {
2689                     (void) SetImageProfile(image,"icc",profile,exception);
2690             }
2691
2692             profile=DestroyStringInfo(profile);
2693           }
2694       }
2695     }
2696 #endif
2697
2698 #if defined(PNG_READ_sRGB_SUPPORTED)
2699   {
2700     if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2701         PNG_INFO_sRGB))
2702     {
2703       if (png_get_sRGB(ping,ping_info,&intent))
2704       {
2705         if (image->rendering_intent == UndefinedIntent)
2706           image->rendering_intent=
2707              Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2708
2709         if (logging != MagickFalse)
2710           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2711             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2712       }
2713     }
2714
2715     else if (mng_info->have_global_srgb)
2716       {
2717         if (image->rendering_intent == UndefinedIntent)
2718           image->rendering_intent=
2719             Magick_RenderingIntent_from_PNG_RenderingIntent
2720             (mng_info->global_srgb_intent);
2721       }
2722   }
2723 #endif
2724
2725
2726   {
2727      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2728        if (mng_info->have_global_gama)
2729          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2730
2731      if (png_get_gAMA(ping,ping_info,&file_gamma))
2732        {
2733          image->gamma=(float) file_gamma;
2734          if (logging != MagickFalse)
2735            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2736              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2737        }
2738   }
2739
2740   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2741     {
2742       if (mng_info->have_global_chrm != MagickFalse)
2743         {
2744           (void) png_set_cHRM(ping,ping_info,
2745             mng_info->global_chrm.white_point.x,
2746             mng_info->global_chrm.white_point.y,
2747             mng_info->global_chrm.red_primary.x,
2748             mng_info->global_chrm.red_primary.y,
2749             mng_info->global_chrm.green_primary.x,
2750             mng_info->global_chrm.green_primary.y,
2751             mng_info->global_chrm.blue_primary.x,
2752             mng_info->global_chrm.blue_primary.y);
2753         }
2754     }
2755
2756   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2757     {
2758       (void) png_get_cHRM(ping,ping_info,
2759         &image->chromaticity.white_point.x,
2760         &image->chromaticity.white_point.y,
2761         &image->chromaticity.red_primary.x,
2762         &image->chromaticity.red_primary.y,
2763         &image->chromaticity.green_primary.x,
2764         &image->chromaticity.green_primary.y,
2765         &image->chromaticity.blue_primary.x,
2766         &image->chromaticity.blue_primary.y);
2767
2768        ping_found_cHRM=MagickTrue;
2769
2770        if (image->chromaticity.red_primary.x>0.6399f &&
2771            image->chromaticity.red_primary.x<0.6401f &&
2772            image->chromaticity.red_primary.y>0.3299f &&
2773            image->chromaticity.red_primary.y<0.3301f &&
2774            image->chromaticity.green_primary.x>0.2999f &&
2775            image->chromaticity.green_primary.x<0.3001f &&
2776            image->chromaticity.green_primary.y>0.5999f &&
2777            image->chromaticity.green_primary.y<0.6001f &&
2778            image->chromaticity.blue_primary.x>0.1499f &&
2779            image->chromaticity.blue_primary.x<0.1501f &&
2780            image->chromaticity.blue_primary.y>0.0599f &&
2781            image->chromaticity.blue_primary.y<0.0601f &&
2782            image->chromaticity.white_point.x>0.3126f &&
2783            image->chromaticity.white_point.x<0.3128f &&
2784            image->chromaticity.white_point.y>0.3289f &&
2785            image->chromaticity.white_point.y<0.3291f)
2786           ping_found_sRGB_cHRM=MagickTrue;
2787     }
2788
2789   if (image->rendering_intent != UndefinedIntent)
2790     {
2791       if (ping_found_sRGB != MagickTrue &&
2792           (ping_found_gAMA != MagickTrue ||
2793           (image->gamma > .45 && image->gamma < .46)) &&
2794           (ping_found_cHRM != MagickTrue ||
2795           ping_found_sRGB_cHRM != MagickFalse) &&
2796           ping_found_iCCP != MagickTrue)
2797       {
2798          png_set_sRGB(ping,ping_info,
2799             Magick_RenderingIntent_to_PNG_RenderingIntent
2800             (image->rendering_intent));
2801          file_gamma=1.000f/2.200f;
2802          ping_found_sRGB=MagickTrue;
2803          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2804            "    Setting sRGB as if in input");
2805       }
2806     }
2807
2808 #if defined(PNG_oFFs_SUPPORTED)
2809   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2810     {
2811       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2812       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2813
2814       if (logging != MagickFalse)
2815         if (image->page.x || image->page.y)
2816           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2817             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2818             image->page.x,(double) image->page.y);
2819     }
2820 #endif
2821 #if defined(PNG_pHYs_SUPPORTED)
2822   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2823     {
2824       if (mng_info->have_global_phys)
2825         {
2826           png_set_pHYs(ping,ping_info,
2827                        mng_info->global_x_pixels_per_unit,
2828                        mng_info->global_y_pixels_per_unit,
2829                        mng_info->global_phys_unit_type);
2830         }
2831     }
2832
2833   x_resolution=0;
2834   y_resolution=0;
2835   unit_type=0;
2836   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2837     {
2838       /*
2839         Set image resolution.
2840       */
2841       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2842         &unit_type);
2843       image->resolution.x=(double) x_resolution;
2844       image->resolution.y=(double) y_resolution;
2845
2846       if (unit_type == PNG_RESOLUTION_METER)
2847         {
2848           image->units=PixelsPerCentimeterResolution;
2849           image->resolution.x=(double) x_resolution/100.0;
2850           image->resolution.y=(double) y_resolution/100.0;
2851         }
2852
2853       if (logging != MagickFalse)
2854         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2855           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2856           (double) x_resolution,(double) y_resolution,unit_type);
2857     }
2858 #endif
2859
2860   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2861     {
2862       png_colorp
2863         palette;
2864
2865       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2866
2867       if ((number_colors == 0) &&
2868           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2869         {
2870           if (mng_info->global_plte_length)
2871             {
2872               png_set_PLTE(ping,ping_info,mng_info->global_plte,
2873                 (int) mng_info->global_plte_length);
2874
2875               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2876               {
2877                 if (mng_info->global_trns_length)
2878                   {
2879                     png_warning(ping,
2880                       "global tRNS has more entries than global PLTE");
2881                   }
2882                 else
2883                   {
2884                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
2885                        (int) mng_info->global_trns_length,NULL);
2886                   }
2887                }
2888 #ifdef PNG_READ_bKGD_SUPPORTED
2889               if (
2890 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2891                    mng_info->have_saved_bkgd_index ||
2892 #endif
2893                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2894                     {
2895                       png_color_16
2896                          background;
2897
2898 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2899                       if (mng_info->have_saved_bkgd_index)
2900                         background.index=mng_info->saved_bkgd_index;
2901 #endif
2902                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2903                         background.index=ping_background->index;
2904
2905                       background.red=(png_uint_16)
2906                         mng_info->global_plte[background.index].red;
2907
2908                       background.green=(png_uint_16)
2909                         mng_info->global_plte[background.index].green;
2910
2911                       background.blue=(png_uint_16)
2912                         mng_info->global_plte[background.index].blue;
2913
2914                       background.gray=(png_uint_16)
2915                         mng_info->global_plte[background.index].green;
2916
2917                       png_set_bKGD(ping,ping_info,&background);
2918                     }
2919 #endif
2920                 }
2921               else
2922                 png_error(ping,"No global PLTE in file");
2923             }
2924         }
2925
2926 #ifdef PNG_READ_bKGD_SUPPORTED
2927   if (mng_info->have_global_bkgd &&
2928           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2929       image->background_color=mng_info->mng_global_bkgd;
2930
2931   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2932     {
2933       unsigned int
2934         bkgd_scale;
2935
2936       /* Set image background color.
2937        * Scale background components to 16-bit, then scale
2938        * to quantum depth
2939        */
2940
2941         bkgd_scale = 1;
2942
2943         if (ping_file_depth == 1)
2944            bkgd_scale = 255;
2945
2946         else if (ping_file_depth == 2)
2947            bkgd_scale = 85;
2948
2949         else if (ping_file_depth == 4)
2950            bkgd_scale = 17;
2951
2952         if (ping_file_depth <= 8)
2953            bkgd_scale *= 257;
2954
2955         ping_background->red *= bkgd_scale;
2956         ping_background->green *= bkgd_scale;
2957         ping_background->blue *= bkgd_scale;
2958
2959         if (logging != MagickFalse)
2960           {
2961             if (logging != MagickFalse)
2962               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2963                  "    Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d).\n"
2964                  "    bkgd_scale=%d.  ping_background=(%d,%d,%d).",
2965                  ping_background->red,ping_background->green,
2966                  ping_background->blue,
2967                  bkgd_scale,ping_background->red,
2968                  ping_background->green,ping_background->blue);
2969           }
2970
2971         image->background_color.red=
2972             ScaleShortToQuantum(ping_background->red);
2973
2974         image->background_color.green=
2975             ScaleShortToQuantum(ping_background->green);
2976
2977         image->background_color.blue=
2978           ScaleShortToQuantum(ping_background->blue);
2979
2980         image->background_color.alpha=OpaqueAlpha;
2981
2982         if (logging != MagickFalse)
2983           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2984             "    image->background_color=(%.20g,%.20g,%.20g).",
2985             (double) image->background_color.red,
2986             (double) image->background_color.green,
2987             (double) image->background_color.blue);
2988     }
2989 #endif /* PNG_READ_bKGD_SUPPORTED */
2990
2991   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2992     {
2993       /*
2994         Image has a tRNS chunk.
2995       */
2996       int
2997         max_sample;
2998
2999       size_t
3000         one=1;
3001
3002       if (logging != MagickFalse)
3003         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3004           "    Reading PNG tRNS chunk.");
3005
3006       max_sample = (int) ((one << ping_file_depth) - 1);
3007
3008       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3009           (int)ping_trans_color->gray > max_sample) ||
3010           (ping_color_type == PNG_COLOR_TYPE_RGB &&
3011           ((int)ping_trans_color->red > max_sample ||
3012           (int)ping_trans_color->green > max_sample ||
3013           (int)ping_trans_color->blue > max_sample)))
3014         {
3015           if (logging != MagickFalse)
3016             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3017               "    Ignoring PNG tRNS chunk with out-of-range sample.");
3018           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3019           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3020           image->alpha_trait=UndefinedPixelTrait;
3021         }
3022       else
3023         {
3024           int
3025              scale_to_short;
3026
3027           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3028
3029           /* Scale transparent_color to short */
3030           transparent_color.red= scale_to_short*ping_trans_color->red;
3031           transparent_color.green= scale_to_short*ping_trans_color->green;
3032           transparent_color.blue= scale_to_short*ping_trans_color->blue;
3033           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3034
3035           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3036             {
3037               if (logging != MagickFalse)
3038               {
3039                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3040                   "    Raw tRNS graylevel = %d, scaled graylevel = %d.",
3041                   (int) ping_trans_color->gray,(int) transparent_color.alpha);
3042
3043               }
3044               transparent_color.red=transparent_color.alpha;
3045               transparent_color.green=transparent_color.alpha;
3046               transparent_color.blue=transparent_color.alpha;
3047             }
3048         }
3049     }
3050 #if defined(PNG_READ_sBIT_SUPPORTED)
3051   if (mng_info->have_global_sbit)
3052     {
3053       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3054         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3055     }
3056 #endif
3057   num_passes=png_set_interlace_handling(ping);
3058
3059   png_read_update_info(ping,ping_info);
3060
3061   ping_rowbytes=png_get_rowbytes(ping,ping_info);
3062
3063   /*
3064     Initialize image structure.
3065   */
3066   mng_info->image_box.left=0;
3067   mng_info->image_box.right=(ssize_t) ping_width;
3068   mng_info->image_box.top=0;
3069   mng_info->image_box.bottom=(ssize_t) ping_height;
3070   if (mng_info->mng_type == 0)
3071     {
3072       mng_info->mng_width=ping_width;
3073       mng_info->mng_height=ping_height;
3074       mng_info->frame=mng_info->image_box;
3075       mng_info->clip=mng_info->image_box;
3076     }
3077
3078   else
3079     {
3080       image->page.y=mng_info->y_off[mng_info->object_id];
3081     }
3082
3083   image->compression=ZipCompression;
3084   image->columns=ping_width;
3085   image->rows=ping_height;
3086
3087   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3088       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3089     {
3090       double
3091         image_gamma = image->gamma;
3092
3093       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3094          "    image->gamma=%f",(float) image_gamma);
3095
3096       if (image_gamma > 0.75)
3097         {
3098           /* Set image->rendering_intent to Undefined,
3099            * image->colorspace to GRAY, and reset image->chromaticity.
3100            */
3101           image->intensity = Rec709LuminancePixelIntensityMethod;
3102           SetImageColorspace(image,GRAYColorspace,exception);
3103           image->gamma = image_gamma;
3104         }
3105     }
3106   
3107   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3108       "    image->colorspace=%d",(int) image->colorspace);
3109
3110   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3111       ((int) ping_bit_depth < 16 &&
3112       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3113     {
3114       size_t
3115         one;
3116
3117       image->storage_class=PseudoClass;
3118       one=1;
3119       image->colors=one << ping_file_depth;
3120 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3121       if (image->colors > 256)
3122         image->colors=256;
3123 #else
3124       if (image->colors > 65536L)
3125         image->colors=65536L;
3126 #endif
3127       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3128         {
3129           png_colorp
3130             palette;
3131
3132           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3133           image->colors=(size_t) number_colors;
3134
3135           if (logging != MagickFalse)
3136             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3137               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3138         }
3139     }
3140
3141   if (image->storage_class == PseudoClass)
3142     {
3143       /*
3144         Initialize image colormap.
3145       */
3146       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3147         png_error(ping,"Memory allocation failed");
3148
3149       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3150         {
3151           png_colorp
3152             palette;
3153
3154           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3155
3156           for (i=0; i < (ssize_t) number_colors; i++)
3157           {
3158             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3159             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3160             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3161           }
3162
3163           for ( ; i < (ssize_t) image->colors; i++)
3164           {
3165             image->colormap[i].red=0;
3166             image->colormap[i].green=0;
3167             image->colormap[i].blue=0;
3168           }
3169         }
3170
3171       else
3172         {
3173           size_t
3174             scale;
3175
3176           scale=(QuantumRange/((1UL << ping_file_depth)-1));
3177
3178           if (scale < 1)
3179              scale=1;
3180
3181           for (i=0; i < (ssize_t) image->colors; i++)
3182           {
3183             image->colormap[i].red=(Quantum) (i*scale);
3184             image->colormap[i].green=(Quantum) (i*scale);
3185             image->colormap[i].blue=(Quantum) (i*scale);
3186           }
3187        }
3188     }
3189
3190    /* Set some properties for reporting by "identify" */
3191     {
3192       char
3193         msg[MaxTextExtent];
3194
3195      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3196         ping_interlace_method in value */
3197
3198      (void) FormatLocaleString(msg,MaxTextExtent,
3199          "%d, %d",(int) ping_width, (int) ping_height);
3200      (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3201
3202      (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
3203      (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3204
3205      (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
3206          (int) ping_color_type,
3207          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3208      (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3209
3210      if (ping_interlace_method == 0)
3211        {
3212          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
3213             (int) ping_interlace_method);
3214        }
3215      else if (ping_interlace_method == 1)
3216        {
3217          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
3218             (int) ping_interlace_method);
3219        }
3220      else
3221        {
3222          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
3223             (int) ping_interlace_method);
3224        }
3225        (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3226
3227      if (number_colors != 0)
3228        {
3229          (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3230             (int) number_colors);
3231          (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3232             exception);
3233        }
3234    }
3235
3236   /*
3237     Read image scanlines.
3238   */
3239   if (image->delay != 0)
3240     mng_info->scenes_found++;
3241
3242   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3243       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3244       (image_info->first_scene+image_info->number_scenes))))
3245     {
3246       /* This happens later in non-ping decodes */
3247       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3248         image->storage_class=DirectClass;
3249
3250       if (logging != MagickFalse)
3251         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3252           "    Skipping PNG image data for scene %.20g",(double)
3253           mng_info->scenes_found-1);
3254       png_destroy_read_struct(&ping,&ping_info,&end_info);
3255
3256 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3257       UnlockSemaphoreInfo(ping_semaphore);
3258 #endif
3259
3260       if (logging != MagickFalse)
3261         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3262           "  exit ReadOnePNGImage().");
3263
3264       return(image);
3265     }
3266
3267   if (logging != MagickFalse)
3268     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3269       "    Reading PNG IDAT chunk(s)");
3270
3271   if (num_passes > 1)
3272     pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3273       sizeof(*ping_pixels));
3274   else
3275     pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3276
3277   if (pixel_info == (MemoryInfo *) NULL)
3278     png_error(ping,"Memory allocation failed");
3279   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3280
3281   if (logging != MagickFalse)
3282     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3283       "    Converting PNG pixels to pixel packets");
3284   /*
3285     Convert PNG pixels to pixel packets.
3286   */
3287   quantum_info=AcquireQuantumInfo(image_info,image);
3288
3289   if (quantum_info == (QuantumInfo *) NULL)
3290      png_error(ping,"Failed to allocate quantum_info");
3291
3292   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3293
3294   {
3295
3296    MagickBooleanType
3297      found_transparent_pixel;
3298
3299   found_transparent_pixel=MagickFalse;
3300
3301   if (image->storage_class == DirectClass)
3302     {
3303       for (pass=0; pass < num_passes; pass++)
3304       {
3305         /*
3306           Convert image to DirectClass pixel packets.
3307         */
3308         image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3309             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3310             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3311             BlendPixelTrait : UndefinedPixelTrait;
3312
3313         for (y=0; y < (ssize_t) image->rows; y++)
3314         {
3315           if (num_passes > 1)
3316             row_offset=ping_rowbytes*y;
3317
3318           else
3319             row_offset=0;
3320
3321           png_read_row(ping,ping_pixels+row_offset,NULL);
3322
3323           if (pass < num_passes-1)
3324             continue;
3325
3326           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3327
3328           if (q == (Quantum *) NULL)
3329             break;
3330
3331           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3332             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3333               GrayQuantum,ping_pixels+row_offset,exception);
3334
3335           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3336             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3337               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3338
3339           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3340             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3341               RGBAQuantum,ping_pixels+row_offset,exception);
3342
3343           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3344             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3345               IndexQuantum,ping_pixels+row_offset,exception);
3346
3347           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3348             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3349               RGBQuantum,ping_pixels+row_offset,exception);
3350
3351           if (found_transparent_pixel == MagickFalse)
3352             {
3353               /* Is there a transparent pixel in the row? */
3354               if (y== 0 && logging != MagickFalse)
3355                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3356                    "    Looking for cheap transparent pixel");
3357
3358               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3359               {
3360                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3361                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3362                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3363                   {
3364                     if (logging != MagickFalse)
3365                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3366                         "    ...got one.");
3367
3368                     found_transparent_pixel = MagickTrue;
3369                     break;
3370                   }
3371                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3372                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3373                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3374                     transparent_color.red &&
3375                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3376                     transparent_color.green &&
3377                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3378                     transparent_color.blue))
3379                   {
3380                     if (logging != MagickFalse)
3381                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3382                         "    ...got one.");
3383                     found_transparent_pixel = MagickTrue;
3384                     break;
3385                   }
3386                 q+=GetPixelChannels(image);
3387               }
3388             }
3389
3390           if ((image->previous == (Image *) NULL) && (num_passes == 1))
3391             {
3392               status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3393                   image->rows);
3394
3395               if (status == MagickFalse)
3396                 break;
3397             }
3398           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3399             break;
3400         }
3401
3402         if ((image->previous == (Image *) NULL) && (num_passes != 1))
3403           {
3404             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3405             if (status == MagickFalse)
3406               break;
3407           }
3408       }
3409     }
3410
3411   else /* image->storage_class != DirectClass */
3412
3413     for (pass=0; pass < num_passes; pass++)
3414     {
3415       Quantum
3416         *quantum_scanline;
3417
3418       register Quantum
3419         *r;
3420
3421       /*
3422         Convert grayscale image to PseudoClass pixel packets.
3423       */
3424       if (logging != MagickFalse)
3425         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3426           "    Converting grayscale pixels to pixel packets");
3427
3428       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3429         BlendPixelTrait : UndefinedPixelTrait;
3430
3431       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3432         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3433         sizeof(*quantum_scanline));
3434
3435       if (quantum_scanline == (Quantum *) NULL)
3436         png_error(ping,"Memory allocation failed");
3437
3438       for (y=0; y < (ssize_t) image->rows; y++)
3439       {
3440         Quantum
3441            alpha;
3442
3443         if (num_passes > 1)
3444           row_offset=ping_rowbytes*y;
3445
3446         else
3447           row_offset=0;
3448
3449         png_read_row(ping,ping_pixels+row_offset,NULL);
3450
3451         if (pass < num_passes-1)
3452           continue;
3453
3454         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3455
3456         if (q == (Quantum *) NULL)
3457           break;
3458
3459         p=ping_pixels+row_offset;
3460         r=quantum_scanline;
3461
3462         switch (ping_bit_depth)
3463         {
3464           case 8:
3465           {
3466
3467             if (ping_color_type == 4)
3468               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3469               {
3470                 *r++=*p++;
3471
3472                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3473
3474                 SetPixelAlpha(image,alpha,q);
3475
3476                 if (alpha != OpaqueAlpha)
3477                   found_transparent_pixel = MagickTrue;
3478
3479                 q+=GetPixelChannels(image);
3480               }
3481
3482             else
3483               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3484                 *r++=*p++;
3485
3486             break;
3487           }
3488
3489           case 16:
3490           {
3491             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3492             {
3493 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3494               unsigned short
3495                 quantum;
3496
3497               if (image->colors > 256)
3498                 quantum=((*p++) << 8);
3499
3500               else
3501                 quantum=0;
3502
3503               quantum|=(*p++);
3504               *r=ScaleShortToQuantum(quantum);
3505               r++;
3506
3507               if (ping_color_type == 4)
3508                 {
3509                   if (image->colors > 256)
3510                     quantum=((*p++) << 8);
3511                   else
3512                     quantum=0;
3513
3514                   quantum|=(*p++);
3515
3516                   alpha=ScaleShortToQuantum(quantum);
3517                   SetPixelAlpha(image,alpha,q);
3518
3519                   if (alpha != OpaqueAlpha)
3520                     found_transparent_pixel = MagickTrue;
3521
3522                   q+=GetPixelChannels(image);
3523                 }
3524
3525 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3526               *r++=(*p++);
3527               p++; /* strip low byte */
3528
3529               if (ping_color_type == 4)
3530                 {
3531                   SetPixelAlpha(image,*p++,q);
3532
3533                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3534                     found_transparent_pixel = MagickTrue;
3535
3536                   p++;
3537                   q+=GetPixelChannels(image);
3538                 }
3539 #endif
3540             }
3541
3542             break;
3543           }
3544
3545           default:
3546             break;
3547         }
3548
3549         /*
3550           Transfer image scanline.
3551         */
3552         r=quantum_scanline;
3553
3554         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3555
3556         if (q == (Quantum *) NULL)
3557           break;
3558         for (x=0; x < (ssize_t) image->columns; x++)
3559         {
3560           SetPixelIndex(image,*r++,q);
3561           q+=GetPixelChannels(image);
3562         }
3563
3564         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3565           break;
3566
3567         if ((image->previous == (Image *) NULL) && (num_passes == 1))
3568           {
3569             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3570               image->rows);
3571
3572             if (status == MagickFalse)
3573               break;
3574           }
3575       }
3576
3577       if ((image->previous == (Image *) NULL) && (num_passes != 1))
3578         {
3579           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3580
3581           if (status == MagickFalse)
3582             break;
3583         }
3584
3585       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3586     }
3587
3588     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3589       UndefinedPixelTrait;
3590
3591     if (logging != MagickFalse)
3592       {
3593         if (found_transparent_pixel != MagickFalse)
3594           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3595             "    Found transparent pixel");
3596         else
3597           {
3598             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3599               "    No transparent pixel was found");
3600
3601             ping_color_type&=0x03;
3602           }
3603       }
3604     }
3605
3606   if (quantum_info != (QuantumInfo *) NULL)
3607     quantum_info=DestroyQuantumInfo(quantum_info);
3608
3609   if (image->storage_class == PseudoClass)
3610     {
3611       PixelTrait
3612         alpha_trait;
3613
3614       alpha_trait=image->alpha_trait;
3615       image->alpha_trait=UndefinedPixelTrait;
3616       (void) SyncImage(image,exception);
3617       image->alpha_trait=alpha_trait;
3618     }
3619
3620   png_read_end(ping,end_info);
3621
3622   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3623       (ssize_t) image_info->first_scene && image->delay != 0)
3624     {
3625       png_destroy_read_struct(&ping,&ping_info,&end_info);
3626       pixel_info=RelinquishVirtualMemory(pixel_info);
3627       image->colors=2;
3628       (void) SetImageBackgroundColor(image,exception);
3629 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3630       UnlockSemaphoreInfo(ping_semaphore);
3631 #endif
3632       if (logging != MagickFalse)
3633         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3634           "  exit ReadOnePNGImage() early.");
3635       return(image);
3636     }
3637
3638   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3639     {
3640       ClassType
3641         storage_class;
3642
3643       /*
3644         Image has a transparent background.
3645       */
3646       storage_class=image->storage_class;
3647       image->alpha_trait=BlendPixelTrait;
3648
3649 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3650
3651       if (storage_class == PseudoClass)
3652         {
3653           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3654             {
3655               for (x=0; x < ping_num_trans; x++)
3656               {
3657                  image->colormap[x].alpha_trait=BlendPixelTrait;
3658                  image->colormap[x].alpha =
3659                    ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3660               }
3661             }
3662
3663           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3664             {
3665               for (x=0; x < (int) image->colors; x++)
3666               {
3667                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3668                      transparent_color.alpha)
3669                  {
3670                     image->colormap[x].alpha_trait=BlendPixelTrait;
3671                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3672                  }
3673               }
3674             }
3675           (void) SyncImage(image,exception);
3676         }
3677
3678 #if 1 /* Should have already been done above, but glennrp problem P10
3679        * needs this.
3680        */
3681       else
3682         {
3683           for (y=0; y < (ssize_t) image->rows; y++)
3684           {
3685             image->storage_class=storage_class;
3686             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3687
3688             if (q == (Quantum *) NULL)
3689               break;
3690
3691
3692             /* Caution: on a Q8 build, this does not distinguish between
3693              * 16-bit colors that differ only in the low byte
3694              */
3695             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3696             {
3697               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3698                   transparent_color.red &&
3699                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3700                   transparent_color.green &&
3701                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3702                   transparent_color.blue)
3703                 {
3704                   SetPixelAlpha(image,TransparentAlpha,q);
3705                 }
3706
3707 #if 0 /* I have not found a case where this is needed. */
3708               else
3709                 {
3710                   SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3711                 }
3712 #endif
3713
3714               q+=GetPixelChannels(image);
3715             }
3716
3717             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3718                break;
3719           }
3720         }
3721 #endif
3722
3723       image->storage_class=DirectClass;
3724     }
3725
3726   for (j = 0; j < 2; j++)
3727   {
3728     if (j == 0)
3729       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3730           MagickTrue : MagickFalse;
3731     else
3732       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3733           MagickTrue : MagickFalse;
3734
3735     if (status != MagickFalse)
3736       for (i=0; i < (ssize_t) num_text; i++)
3737       {
3738         /* Check for a profile */
3739
3740         if (logging != MagickFalse)
3741           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3742             "    Reading PNG text chunk");
3743
3744         if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3745           {
3746             const char
3747               *value;
3748
3749             value=GetImageOption(image_info,"profile:skip");
3750
3751             if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3752             {
3753                (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3754                   (int) i,exception);
3755                num_raw_profiles++;
3756                if (logging != MagickFalse)
3757                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3758                    "    Read raw profile %s",text[i].key+17);
3759             }
3760             else
3761             {
3762                if (logging != MagickFalse)
3763                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3764                    "    Skipping raw profile %s",text[i].key+17);
3765             }
3766           }
3767
3768         else
3769           {
3770             char
3771               *value;
3772
3773             length=text[i].text_length;
3774             value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3775               sizeof(*value));
3776             if (value == (char *) NULL)
3777               {
3778                 png_error(ping,"Memory allocation failed");
3779                 break;
3780               }
3781             *value='\0';
3782             (void) ConcatenateMagickString(value,text[i].text,length+2);
3783
3784             /* Don't save "density" or "units" property if we have a pHYs
3785              * chunk
3786              */
3787             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3788                 (LocaleCompare(text[i].key,"density") != 0 &&
3789                 LocaleCompare(text[i].key,"units") != 0))
3790                (void) SetImageProperty(image,text[i].key,value,exception);
3791
3792             if (logging != MagickFalse)
3793             {
3794               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3795                 "      length: %lu\n"
3796                 "      Keyword: %s",
3797                 (unsigned long) length,
3798                 text[i].key);
3799             }
3800
3801             value=DestroyString(value);
3802           }
3803       }
3804       num_text_total += num_text;
3805     }
3806
3807 #ifdef MNG_OBJECT_BUFFERS
3808   /*
3809     Store the object if necessary.
3810   */
3811   if (object_id && !mng_info->frozen[object_id])
3812     {
3813       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3814         {
3815           /*
3816             create a new object buffer.
3817           */
3818           mng_info->ob[object_id]=(MngBuffer *)
3819             AcquireMagickMemory(sizeof(MngBuffer));
3820
3821           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3822             {
3823               mng_info->ob[object_id]->image=(Image *) NULL;
3824               mng_info->ob[object_id]->reference_count=1;
3825             }
3826         }
3827
3828       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3829           mng_info->ob[object_id]->frozen)
3830         {
3831           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3832              png_error(ping,"Memory allocation failed");
3833
3834           if (mng_info->ob[object_id]->frozen)
3835             png_error(ping,"Cannot overwrite frozen MNG object buffer");
3836         }
3837
3838       else
3839         {
3840
3841           if (mng_info->ob[object_id]->image != (Image *) NULL)
3842             mng_info->ob[object_id]->image=DestroyImage
3843                 (mng_info->ob[object_id]->image);
3844
3845           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3846             exception);
3847
3848           if (mng_info->ob[object_id]->image != (Image *) NULL)
3849             mng_info->ob[object_id]->image->file=(FILE *) NULL;
3850
3851           else
3852             png_error(ping, "Cloning image for object buffer failed");
3853
3854           if (ping_width > 250000L || ping_height > 250000L)
3855              png_error(ping,"PNG Image dimensions are too large.");
3856
3857           mng_info->ob[object_id]->width=ping_width;
3858           mng_info->ob[object_id]->height=ping_height;
3859           mng_info->ob[object_id]->color_type=ping_color_type;
3860           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3861           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3862           mng_info->ob[object_id]->compression_method=
3863              ping_compression_method;
3864           mng_info->ob[object_id]->filter_method=ping_filter_method;
3865
3866           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3867             {
3868               png_colorp
3869                 plte;
3870
3871               /*
3872                 Copy the PLTE to the object buffer.
3873               */
3874               png_get_PLTE(ping,ping_info,&plte,&number_colors);
3875               mng_info->ob[object_id]->plte_length=number_colors;
3876
3877               for (i=0; i < number_colors; i++)
3878               {
3879                 mng_info->ob[object_id]->plte[i]=plte[i];
3880               }
3881             }
3882
3883           else
3884               mng_info->ob[object_id]->plte_length=0;
3885         }
3886     }
3887 #endif
3888
3889    /* Set image->alpha_trait to MagickTrue if the input colortype supports
3890     * alpha or if a valid tRNS chunk is present, no matter whether there
3891     * is actual transparency present.
3892     */
3893     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3894         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3895         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3896         BlendPixelTrait : UndefinedPixelTrait;
3897
3898 #if 0  /* I'm not sure what's wrong here but it does not work. */
3899     if (image->alpha_trait == BlendPixelTrait)
3900     {
3901       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3902         (void) SetImageType(image,GrayscaleMatteType,exception);
3903
3904       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3905         (void) SetImageType(image,PaletteMatteType,exception);
3906
3907       else
3908         (void) SetImageType(image,TrueColorMatteType,exception);
3909     }
3910
3911     else
3912     {
3913       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3914         (void) SetImageType(image,GrayscaleType,exception);
3915
3916       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3917         (void) SetImageType(image,PaletteType,exception);
3918
3919       else
3920         (void) SetImageType(image,TrueColorType,exception);
3921     }
3922 #endif
3923
3924    /* Set more properties for identify to retrieve */
3925    {
3926      char
3927        msg[MaxTextExtent];
3928
3929      if (num_text_total != 0)
3930        {
3931          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3932          (void) FormatLocaleString(msg,MaxTextExtent,
3933             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3934          (void) SetImageProperty(image,"png:text",msg,
3935                 exception);
3936        }
3937
3938      if (num_raw_profiles != 0)
3939        {
3940          (void) FormatLocaleString(msg,MaxTextExtent,
3941             "%d were found", num_raw_profiles);
3942          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3943                 exception);
3944        }
3945
3946      if (ping_found_cHRM != MagickFalse)
3947        {
3948          (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3949             "chunk was found (see Chromaticity, above)");
3950          (void) SetImageProperty(image,"png:cHRM",msg,
3951                 exception);
3952        }
3953
3954      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3955        {
3956          (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3957             "chunk was found (see Background color, above)");
3958          (void) SetImageProperty(image,"png:bKGD",msg,
3959                 exception);
3960        }
3961
3962      (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3963         "chunk was found");
3964
3965 #if defined(PNG_iCCP_SUPPORTED)
3966      if (ping_found_iCCP != MagickFalse)
3967         (void) SetImageProperty(image,"png:iCCP",msg,
3968                 exception);
3969 #endif
3970
3971      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3972         (void) SetImageProperty(image,"png:tRNS",msg,
3973                 exception);
3974
3975 #if defined(PNG_sRGB_SUPPORTED)
3976      if (ping_found_sRGB != MagickFalse)
3977        {
3978          (void) FormatLocaleString(msg,MaxTextExtent,
3979             "intent=%d (%s)",
3980             (int) intent,
3981             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3982          (void) SetImageProperty(image,"png:sRGB",msg,
3983                  exception);
3984        }
3985 #endif
3986
3987      if (ping_found_gAMA != MagickFalse)
3988        {
3989          (void) FormatLocaleString(msg,MaxTextExtent,
3990             "gamma=%.8g (See Gamma, above)",
3991             file_gamma);
3992          (void) SetImageProperty(image,"png:gAMA",msg,
3993                 exception);
3994        }
3995
3996 #if defined(PNG_pHYs_SUPPORTED)
3997      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3998        {
3999          (void) FormatLocaleString(msg,MaxTextExtent,
4000             "x_res=%.10g, y_res=%.10g, units=%d",
4001             (double) x_resolution,(double) y_resolution, unit_type);
4002          (void) SetImageProperty(image,"png:pHYs",msg,
4003                 exception);
4004        }
4005 #endif
4006
4007 #if defined(PNG_oFFs_SUPPORTED)
4008      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4009        {
4010          (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
4011             (double) image->page.x,(double) image->page.y);
4012          (void) SetImageProperty(image,"png:oFFs",msg,
4013                 exception);
4014        }
4015 #endif
4016
4017      if ((image->page.width != 0 && image->page.width != image->columns) ||
4018          (image->page.height != 0 && image->page.height != image->rows))
4019        {
4020          (void) FormatLocaleString(msg,MaxTextExtent,
4021             "width=%.20g, height=%.20g",
4022             (double) image->page.width,(double) image->page.height);
4023          (void) SetImageProperty(image,"png:vpAg",msg,
4024                 exception);
4025        }
4026    }
4027
4028   /*
4029     Relinquish resources.
4030   */
4031   png_destroy_read_struct(&ping,&ping_info,&end_info);
4032
4033   pixel_info=RelinquishVirtualMemory(pixel_info);
4034
4035   if (logging != MagickFalse)
4036     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4037       "  exit ReadOnePNGImage()");
4038
4039 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4040   UnlockSemaphoreInfo(ping_semaphore);
4041 #endif
4042
4043   /* }  for navigation to beginning of SETJMP-protected block, revert to
4044    *    Throwing an Exception when an error occurs.
4045    */
4046
4047   return(image);
4048
4049 /* end of reading one PNG image */
4050 }
4051
4052 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4053 {
4054   Image
4055     *image,
4056     *previous;
4057
4058   MagickBooleanType
4059     have_mng_structure,
4060     logging,
4061     status;
4062
4063   MngInfo
4064     *mng_info;
4065
4066   char
4067     magic_number[MaxTextExtent];
4068
4069   ssize_t
4070     count;
4071
4072   /*
4073     Open image file.
4074   */
4075   assert(image_info != (const ImageInfo *) NULL);
4076   assert(image_info->signature == MagickSignature);
4077
4078   if (image_info->debug != MagickFalse)
4079     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4080       image_info->filename);
4081
4082   assert(exception != (ExceptionInfo *) NULL);
4083   assert(exception->signature == MagickSignature);
4084   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4085   image=AcquireImage(image_info,exception);
4086   mng_info=(MngInfo *) NULL;
4087   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4088
4089   if (status == MagickFalse)
4090     ThrowReaderException(FileOpenError,"UnableToOpenFile");
4091
4092   /*
4093     Verify PNG signature.
4094   */
4095   count=ReadBlob(image,8,(unsigned char *) magic_number);
4096
4097   if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4098     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4099
4100   /*
4101     Allocate a MngInfo structure.
4102   */
4103   have_mng_structure=MagickFalse;
4104   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4105
4106   if (mng_info == (MngInfo *) NULL)
4107     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4108
4109   /*
4110     Initialize members of the MngInfo structure.
4111   */
4112   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4113   mng_info->image=image;
4114   have_mng_structure=MagickTrue;
4115
4116   previous=image;
4117   image=ReadOnePNGImage(mng_info,image_info,exception);
4118   MngInfoFreeStruct(mng_info,&have_mng_structure);
4119
4120   if (image == (Image *) NULL)
4121     {
4122       if (previous != (Image *) NULL)
4123         {
4124           if (previous->signature != MagickSignature)
4125             ThrowReaderException(CorruptImageError,"CorruptImage");
4126
4127           (void) CloseBlob(previous);
4128           (void) DestroyImageList(previous);
4129         }
4130
4131       if (logging != MagickFalse)
4132         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4133           "exit ReadPNGImage() with error");
4134
4135       return((Image *) NULL);
4136     }
4137
4138   (void) CloseBlob(image);
4139
4140   if ((image->columns == 0) || (image->rows == 0))
4141     {
4142       if (logging != MagickFalse)
4143         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4144           "exit ReadPNGImage() with error.");
4145
4146       ThrowReaderException(CorruptImageError,"CorruptImage");
4147     }
4148
4149   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4150       ((image->gamma < .45) || (image->gamma > .46)) &&
4151            !(image->chromaticity.red_primary.x>0.6399f &&
4152            image->chromaticity.red_primary.x<0.6401f &&
4153            image->chromaticity.red_primary.y>0.3299f &&
4154            image->chromaticity.red_primary.y<0.3301f &&
4155            image->chromaticity.green_primary.x>0.2999f &&
4156            image->chromaticity.green_primary.x<0.3001f &&
4157            image->chromaticity.green_primary.y>0.5999f &&
4158            image->chromaticity.green_primary.y<0.6001f &&
4159            image->chromaticity.blue_primary.x>0.1499f &&
4160            image->chromaticity.blue_primary.x<0.1501f &&
4161            image->chromaticity.blue_primary.y>0.0599f &&
4162            image->chromaticity.blue_primary.y<0.0601f &&
4163            image->chromaticity.white_point.x>0.3126f &&
4164            image->chromaticity.white_point.x<0.3128f &&
4165            image->chromaticity.white_point.y>0.3289f &&
4166            image->chromaticity.white_point.y<0.3291f))
4167     SetImageColorspace(image,RGBColorspace,exception);
4168
4169   if (logging != MagickFalse)
4170     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4171         "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4172             (double) image->page.width,(double) image->page.height,
4173             (double) image->page.x,(double) image->page.y);
4174
4175   if (logging != MagickFalse)
4176     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4177
4178   return(image);
4179 }
4180
4181
4182
4183 #if defined(JNG_SUPPORTED)
4184 /*
4185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4186 %                                                                             %
4187 %                                                                             %
4188 %                                                                             %
4189 %   R e a d O n e J N G I m a g e                                             %
4190 %                                                                             %
4191 %                                                                             %
4192 %                                                                             %
4193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4194 %
4195 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4196 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
4197 %  necessary for the new Image structure and returns a pointer to the new
4198 %  image.
4199 %
4200 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4201 %
4202 %  The format of the ReadOneJNGImage method is:
4203 %
4204 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4205 %         ExceptionInfo *exception)
4206 %
4207 %  A description of each parameter follows:
4208 %
4209 %    o mng_info: Specifies a pointer to a MngInfo structure.
4210 %
4211 %    o image_info: the image info.
4212 %
4213 %    o exception: return any errors or warnings in this structure.
4214 %
4215 */
4216 static Image *ReadOneJNGImage(MngInfo *mng_info,
4217     const ImageInfo *image_info, ExceptionInfo *exception)
4218 {
4219   Image
4220     *alpha_image,
4221     *color_image,
4222     *image,
4223     *jng_image;
4224
4225   ImageInfo
4226     *alpha_image_info,
4227     *color_image_info;
4228
4229   MagickBooleanType
4230     logging;
4231
4232   ssize_t
4233     y;
4234
4235   MagickBooleanType
4236     status;
4237
4238   png_uint_32
4239     jng_height,
4240     jng_width;
4241
4242   png_byte
4243     jng_color_type,
4244     jng_image_sample_depth,
4245     jng_image_compression_method,
4246     jng_image_interlace_method,
4247     jng_alpha_sample_depth,
4248     jng_alpha_compression_method,
4249     jng_alpha_filter_method,
4250     jng_alpha_interlace_method;
4251
4252   register const Quantum
4253     *s;
4254
4255   register ssize_t
4256     i,
4257     x;
4258
4259   register Quantum
4260     *q;
4261
4262   register unsigned char
4263     *p;
4264
4265   unsigned int
4266     read_JSEP,
4267     reading_idat;
4268
4269   size_t
4270     length;
4271
4272   jng_alpha_compression_method=0;
4273   jng_alpha_sample_depth=8;
4274   jng_color_type=0;
4275   jng_height=0;
4276   jng_width=0;
4277   alpha_image=(Image *) NULL;
4278   color_image=(Image *) NULL;
4279   alpha_image_info=(ImageInfo *) NULL;
4280   color_image_info=(ImageInfo *) NULL;
4281
4282   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4283     "  Enter ReadOneJNGImage()");
4284
4285   image=mng_info->image;
4286
4287   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4288     {
4289       /*
4290         Allocate next image structure.
4291       */
4292       if (logging != MagickFalse)
4293         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4294            "  AcquireNextImage()");
4295
4296       AcquireNextImage(image_info,image,exception);
4297
4298       if (GetNextImageInList(image) == (Image *) NULL)
4299         return((Image *) NULL);
4300
4301       image=SyncNextImageInList(image);
4302     }
4303   mng_info->image=image;
4304
4305   /*
4306     Signature bytes have already been read.
4307   */
4308
4309   read_JSEP=MagickFalse;
4310   reading_idat=MagickFalse;
4311   for (;;)
4312   {
4313     char
4314       type[MaxTextExtent];
4315
4316     unsigned char
4317       *chunk;
4318
4319     unsigned int
4320       count;
4321
4322     /*
4323       Read a new JNG chunk.
4324     */
4325     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4326       2*GetBlobSize(image));
4327
4328     if (status == MagickFalse)
4329       break;
4330
4331     type[0]='\0';
4332     (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4333     length=ReadBlobMSBLong(image);
4334     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4335
4336     if (logging != MagickFalse)
4337       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4338         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4339         type[0],type[1],type[2],type[3],(double) length);
4340
4341     if (length > PNG_UINT_31_MAX || count == 0)
4342       ThrowReaderException(CorruptImageError,"CorruptImage");
4343
4344     p=NULL;
4345     chunk=(unsigned char *) NULL;
4346
4347     if (length)
4348       {
4349         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4350
4351         if (chunk == (unsigned char *) NULL)
4352           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4353
4354         for (i=0; i < (ssize_t) length; i++)
4355           chunk[i]=(unsigned char) ReadBlobByte(image);
4356
4357         p=chunk;
4358       }
4359
4360     (void) ReadBlobMSBLong(image);  /* read crc word */
4361
4362     if (memcmp(type,mng_JHDR,4) == 0)
4363       {
4364         if (length == 16)
4365           {
4366             jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4367               (p[2] << 8) | p[3]);
4368             jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4369               (p[6] << 8) | p[7]);
4370             if ((jng_width == 0) || (jng_height == 0))
4371               ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
4372             jng_color_type=p[8];
4373             jng_image_sample_depth=p[9];
4374             jng_image_compression_method=p[10];
4375             jng_image_interlace_method=p[11];
4376
4377             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4378               NoInterlace;
4379
4380             jng_alpha_sample_depth=p[12];
4381             jng_alpha_compression_method=p[13];
4382             jng_alpha_filter_method=p[14];
4383             jng_alpha_interlace_method=p[15];
4384
4385             if (logging != MagickFalse)
4386               {
4387                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4388                   "    jng_width:      %16lu,    jng_height:     %16lu\n"
4389                   "    jng_color_type: %16d,     jng_image_sample_depth: %3d\n"
4390                   "    jng_image_compression_method:%3d",
4391                   (unsigned long) jng_width, (unsigned long) jng_height,
4392                   jng_color_type, jng_image_sample_depth,
4393                   jng_image_compression_method);
4394
4395                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4396                   "    jng_image_interlace_method:  %3d"
4397                   "    jng_alpha_sample_depth:      %3d",
4398                   jng_image_interlace_method,
4399                   jng_alpha_sample_depth);
4400
4401                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4402                   "    jng_alpha_compression_method:%3d\n"
4403                   "    jng_alpha_filter_method:     %3d\n"
4404                   "    jng_alpha_interlace_method:  %3d",
4405                   jng_alpha_compression_method,
4406                   jng_alpha_filter_method,
4407                   jng_alpha_interlace_method);
4408               }
4409           }
4410
4411         if (length)
4412           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4413
4414         continue;
4415       }
4416
4417
4418     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4419         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4420          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4421       {
4422         /*
4423            o create color_image
4424            o open color_blob, attached to color_image
4425            o if (color type has alpha)
4426                open alpha_blob, attached to alpha_image
4427         */
4428
4429         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4430
4431         if (color_image_info == (ImageInfo *) NULL)
4432           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4433
4434         GetImageInfo(color_image_info);
4435         color_image=AcquireImage(color_image_info,exception);
4436
4437         if (color_image == (Image *) NULL)
4438           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4439
4440         if (logging != MagickFalse)
4441           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4442             "    Creating color_blob.");
4443
4444         (void) AcquireUniqueFilename(color_image->filename);
4445         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4446           exception);
4447
4448         if (status == MagickFalse)
4449           return((Image *) NULL);
4450
4451         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4452           {
4453             alpha_image_info=(ImageInfo *)
4454               AcquireMagickMemory(sizeof(ImageInfo));
4455
4456             if (alpha_image_info == (ImageInfo *) NULL)
4457               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4458
4459             GetImageInfo(alpha_image_info);
4460             alpha_image=AcquireImage(alpha_image_info,exception);
4461
4462             if (alpha_image == (Image *) NULL)
4463               {
4464                 alpha_image=DestroyImage(alpha_image);
4465                 ThrowReaderException(ResourceLimitError,
4466                   "MemoryAllocationFailed");
4467               }
4468
4469             if (logging != MagickFalse)
4470               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4471                 "    Creating alpha_blob.");
4472
4473             (void) AcquireUniqueFilename(alpha_image->filename);
4474             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4475               exception);
4476
4477             if (status == MagickFalse)
4478               return((Image *) NULL);
4479
4480             if (jng_alpha_compression_method == 0)
4481               {
4482                 unsigned char
4483                   data[18];
4484
4485                 if (logging != MagickFalse)
4486                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4487                     "    Writing IHDR chunk to alpha_blob.");
4488
4489                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4490                   "\211PNG\r\n\032\n");
4491
4492                 (void) WriteBlobMSBULong(alpha_image,13L);
4493                 PNGType(data,mng_IHDR);
4494                 LogPNGChunk(logging,mng_IHDR,13L);
4495                 PNGLong(data+4,jng_width);
4496                 PNGLong(data+8,jng_height);
4497                 data[12]=jng_alpha_sample_depth;
4498                 data[13]=0; /* color_type gray */
4499                 data[14]=0; /* compression method 0 */
4500                 data[15]=0; /* filter_method 0 */
4501                 data[16]=0; /* interlace_method 0 */
4502                 (void) WriteBlob(alpha_image,17,data);
4503                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4504               }
4505           }
4506         reading_idat=MagickTrue;
4507       }
4508
4509     if (memcmp(type,mng_JDAT,4) == 0)
4510       {
4511         /* Copy chunk to color_image->blob */
4512
4513         if (logging != MagickFalse)
4514           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4515             "    Copying JDAT chunk data to color_blob.");
4516
4517         (void) WriteBlob(color_image,length,chunk);
4518
4519         if (length)
4520           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4521
4522         continue;
4523       }
4524
4525     if (memcmp(type,mng_IDAT,4) == 0)
4526       {
4527         png_byte
4528            data[5];
4529
4530         /* Copy IDAT header and chunk data to alpha_image->blob */
4531
4532         if (image_info->ping == MagickFalse)
4533           {
4534             if (logging != MagickFalse)
4535               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4536                 "    Copying IDAT chunk data to alpha_blob.");
4537
4538             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4539             PNGType(data,mng_IDAT);
4540             LogPNGChunk(logging,mng_IDAT,length);
4541             (void) WriteBlob(alpha_image,4,data);
4542             (void) WriteBlob(alpha_image,length,chunk);
4543             (void) WriteBlobMSBULong(alpha_image,
4544               crc32(crc32(0,data,4),chunk,(uInt) length));
4545           }
4546
4547         if (length)
4548           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4549
4550         continue;
4551       }
4552
4553     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4554       {
4555         /* Copy chunk data to alpha_image->blob */
4556
4557         if (image_info->ping == MagickFalse)
4558           {
4559             if (logging != MagickFalse)
4560               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4561                 "    Copying JDAA chunk data to alpha_blob.");
4562
4563             (void) WriteBlob(alpha_image,length,chunk);
4564           }
4565
4566         if (length)
4567           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4568
4569         continue;
4570       }
4571
4572     if (memcmp(type,mng_JSEP,4) == 0)
4573       {
4574         read_JSEP=MagickTrue;
4575
4576         if (length)
4577           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4578
4579         continue;
4580       }
4581
4582     if (memcmp(type,mng_bKGD,4) == 0)
4583       {
4584         if (length == 2)
4585           {
4586             image->background_color.red=ScaleCharToQuantum(p[1]);
4587             image->background_color.green=image->background_color.red;
4588             image->background_color.blue=image->background_color.red;
4589           }
4590
4591         if (length == 6)
4592           {
4593             image->background_color.red=ScaleCharToQuantum(p[1]);
4594             image->background_color.green=ScaleCharToQuantum(p[3]);
4595             image->background_color.blue=ScaleCharToQuantum(p[5]);
4596           }
4597
4598         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4599         continue;
4600       }
4601
4602     if (memcmp(type,mng_gAMA,4) == 0)
4603       {
4604         if (length == 4)
4605           image->gamma=((float) mng_get_long(p))*0.00001;
4606
4607         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4608         continue;
4609       }
4610
4611     if (memcmp(type,mng_cHRM,4) == 0)
4612       {
4613         if (length == 32)
4614           {
4615             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4616             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4617             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4618             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4619             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4620             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4621             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4622             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4623           }
4624
4625         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4626         continue;
4627       }
4628
4629     if (memcmp(type,mng_sRGB,4) == 0)
4630       {
4631         if (length == 1)
4632           {
4633             image->rendering_intent=
4634               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4635             image->gamma=1.000f/2.200f;
4636             image->chromaticity.red_primary.x=0.6400f;
4637             image->chromaticity.red_primary.y=0.3300f;
4638             image->chromaticity.green_primary.x=0.3000f;
4639             image->chromaticity.green_primary.y=0.6000f;
4640             image->chromaticity.blue_primary.x=0.1500f;
4641             image->chromaticity.blue_primary.y=0.0600f;
4642             image->chromaticity.white_point.x=0.3127f;
4643             image->chromaticity.white_point.y=0.3290f;
4644           }
4645
4646         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4647         continue;
4648       }
4649
4650     if (memcmp(type,mng_oFFs,4) == 0)
4651       {
4652         if (length > 8)
4653           {
4654             image->page.x=(ssize_t) mng_get_long(p);
4655             image->page.y=(ssize_t) mng_get_long(&p[4]);
4656
4657             if ((int) p[8] != 0)
4658               {
4659                 image->page.x/=10000;
4660                 image->page.y/=10000;
4661               }
4662           }
4663
4664         if (length)
4665           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4666
4667         continue;
4668       }
4669
4670     if (memcmp(type,mng_pHYs,4) == 0)
4671       {
4672         if (length > 8)
4673           {
4674             image->resolution.x=(double) mng_get_long(p);
4675             image->resolution.y=(double) mng_get_long(&p[4]);
4676             if ((int) p[8] == PNG_RESOLUTION_METER)
4677               {
4678                 image->units=PixelsPerCentimeterResolution;
4679                 image->resolution.x=image->resolution.x/100.0f;
4680                 image->resolution.y=image->resolution.y/100.0f;
4681               }
4682           }
4683
4684         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4685         continue;
4686       }
4687
4688 #if 0
4689     if (memcmp(type,mng_iCCP,4) == 0)
4690       {
4691         /* To do: */
4692         if (length)
4693           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4694
4695         continue;
4696       }
4697 #endif
4698
4699     if (length)
4700       chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4701
4702     if (memcmp(type,mng_IEND,4))
4703       continue;
4704
4705     break;
4706   }
4707
4708
4709   /* IEND found */
4710
4711   /*
4712     Finish up reading image data:
4713
4714        o read main image from color_blob.
4715
4716        o close color_blob.
4717
4718        o if (color_type has alpha)
4719             if alpha_encoding is PNG
4720                read secondary image from alpha_blob via ReadPNG
4721             if alpha_encoding is JPEG
4722                read secondary image from alpha_blob via ReadJPEG
4723
4724        o close alpha_blob.
4725
4726        o copy intensity of secondary image into
4727          alpha samples of main image.
4728
4729        o destroy the secondary image.
4730   */
4731
4732   (void) CloseBlob(color_image);
4733
4734   if (logging != MagickFalse)
4735     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4736       "    Reading jng_image from color_blob.");
4737
4738   assert(color_image_info != (ImageInfo *) NULL);
4739   (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4740     color_image->filename);
4741
4742   color_image_info->ping=MagickFalse;   /* To do: avoid this */
4743   jng_image=ReadImage(color_image_info,exception);
4744
4745   if (jng_image == (Image *) NULL)
4746     return((Image *) NULL);
4747
4748   (void) RelinquishUniqueFileResource(color_image->filename);
4749   color_image=DestroyImage(color_image);
4750   color_image_info=DestroyImageInfo(color_image_info);
4751
4752   if (jng_image == (Image *) NULL)
4753     return((Image *) NULL);
4754
4755   if (logging != MagickFalse)
4756     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4757       "    Copying jng_image pixels to main image.");
4758
4759   image->rows=jng_height;
4760   image->columns=jng_width;
4761
4762   for (y=0; y < (ssize_t) image->rows; y++)
4763   {
4764     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4765     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4766     for (x=(ssize_t) image->columns; x != 0; x--)
4767     {
4768       SetPixelRed(image,GetPixelRed(jng_image,s),q);
4769       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4770       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4771       q+=GetPixelChannels(image);
4772       s+=GetPixelChannels(jng_image);
4773     }
4774
4775     if (SyncAuthenticPixels(image,exception) == MagickFalse)
4776       break;
4777   }
4778
4779   jng_image=DestroyImage(jng_image);
4780
4781   if (image_info->ping == MagickFalse)
4782     {
4783      if (jng_color_type >= 12)
4784        {
4785          if (jng_alpha_compression_method == 0)
4786            {
4787              png_byte
4788                data[5];
4789              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4790              PNGType(data,mng_IEND);
4791              LogPNGChunk(logging,mng_IEND,0L);
4792              (void) WriteBlob(alpha_image,4,data);
4793              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4794            }
4795
4796          (void) CloseBlob(alpha_image);
4797
4798          if (logging != MagickFalse)
4799            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4800              "    Reading alpha from alpha_blob.");
4801
4802          (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4803            "%s",alpha_image->filename);
4804
4805          jng_image=ReadImage(alpha_image_info,exception);
4806
4807          if (jng_image != (Image *) NULL)
4808            for (y=0; y < (ssize_t) image->rows; y++)
4809            {
4810              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4811                exception);
4812              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4813
4814              if (image->alpha_trait == BlendPixelTrait)
4815                for (x=(ssize_t) image->columns; x != 0; x--)
4816                {
4817                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4818                   q+=GetPixelChannels(image);
4819                   s+=GetPixelChannels(jng_image);
4820                }
4821
4822              else
4823                for (x=(ssize_t) image->columns; x != 0; x--)
4824                {
4825                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4826                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
4827                     image->alpha_trait=BlendPixelTrait;
4828                   q+=GetPixelChannels(image);
4829                   s+=GetPixelChannels(jng_image);
4830                }
4831
4832              if (SyncAuthenticPixels(image,exception) == MagickFalse)
4833                break;
4834            }
4835          (void) RelinquishUniqueFileResource(alpha_image->filename);
4836          alpha_image=DestroyImage(alpha_image);
4837          alpha_image_info=DestroyImageInfo(alpha_image_info);
4838          if (jng_image != (Image *) NULL)
4839            jng_image=DestroyImage(jng_image);
4840        }
4841     }
4842
4843   /* Read the JNG image.  */
4844
4845   if (mng_info->mng_type == 0)
4846     {
4847       mng_info->mng_width=jng_width;
4848       mng_info->mng_height=jng_height;
4849     }
4850
4851   if (image->page.width == 0 && image->page.height == 0)
4852     {
4853       image->page.width=jng_width;
4854       image->page.height=jng_height;
4855     }
4856
4857   if (image->page.x == 0 && image->page.y == 0)
4858     {
4859       image->page.x=mng_info->x_off[mng_info->object_id];
4860       image->page.y=mng_info->y_off[mng_info->object_id];
4861     }
4862
4863   else
4864     {
4865       image->page.y=mng_info->y_off[mng_info->object_id];
4866     }
4867
4868   mng_info->image_found++;
4869   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4870     2*GetBlobSize(image));
4871
4872   if (logging != MagickFalse)
4873     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4874       "  exit ReadOneJNGImage()");
4875
4876   return(image);
4877 }
4878
4879 /*
4880 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4881 %                                                                             %
4882 %                                                                             %
4883 %                                                                             %
4884 %   R e a d J N G I m a g e                                                   %
4885 %                                                                             %
4886 %                                                                             %
4887 %                                                                             %
4888 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4889 %
4890 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4891 %  (including the 8-byte signature)  and returns it.  It allocates the memory
4892 %  necessary for the new Image structure and returns a pointer to the new
4893 %  image.
4894 %
4895 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4896 %
4897 %  The format of the ReadJNGImage method is:
4898 %
4899 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4900 %         *exception)
4901 %
4902 %  A description of each parameter follows:
4903 %
4904 %    o image_info: the image info.
4905 %
4906 %    o exception: return any errors or warnings in this structure.
4907 %
4908 */
4909
4910 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4911 {
4912   Image
4913     *image,
4914     *previous;
4915
4916   MagickBooleanType
4917     have_mng_structure,
4918     logging,
4919     status;
4920
4921   MngInfo
4922     *mng_info;
4923
4924   char
4925     magic_number[MaxTextExtent];
4926
4927   size_t
4928     count;
4929
4930   /*
4931     Open image file.
4932   */
4933   assert(image_info != (const ImageInfo *) NULL);
4934   assert(image_info->signature == MagickSignature);
4935   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4936   assert(exception != (ExceptionInfo *) NULL);
4937   assert(exception->signature == MagickSignature);
4938   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4939   image=AcquireImage(image_info,exception);
4940   mng_info=(MngInfo *) NULL;
4941   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4942
4943   if (status == MagickFalse)
4944     return((Image *) NULL);
4945
4946   if (LocaleCompare(image_info->magick,"JNG") != 0)
4947     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4948
4949   /* Verify JNG signature.  */
4950
4951   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4952
4953   if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4954     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4955
4956   /* Allocate a MngInfo structure.  */
4957
4958   have_mng_structure=MagickFalse;
4959   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4960
4961   if (mng_info == (MngInfo *) NULL)
4962     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4963
4964   /* Initialize members of the MngInfo structure.  */
4965
4966   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4967   have_mng_structure=MagickTrue;
4968
4969   mng_info->image=image;
4970   previous=image;
4971   image=ReadOneJNGImage(mng_info,image_info,exception);
4972   MngInfoFreeStruct(mng_info,&have_mng_structure);
4973
4974   if (image == (Image *) NULL)
4975     {
4976       if (IsImageObject(previous) != MagickFalse)
4977         {
4978           (void) CloseBlob(previous);
4979           (void) DestroyImageList(previous);
4980         }
4981
4982       if (logging != MagickFalse)
4983         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4984           "exit ReadJNGImage() with error");
4985
4986       return((Image *) NULL);
4987     }
4988   (void) CloseBlob(image);
4989
4990   if (image->columns == 0 || image->rows == 0)
4991     {
4992       if (logging != MagickFalse)
4993         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4994           "exit ReadJNGImage() with error");
4995
4996       ThrowReaderException(CorruptImageError,"CorruptImage");
4997     }
4998
4999   if (logging != MagickFalse)
5000     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5001
5002   return(image);
5003 }
5004 #endif
5005
5006 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5007 {
5008   char
5009     page_geometry[MaxTextExtent];
5010
5011   Image
5012     *image,
5013     *previous;
5014
5015   MagickBooleanType
5016     logging,
5017     have_mng_structure;
5018
5019   volatile int
5020     first_mng_object,
5021     object_id,
5022     term_chunk_found,
5023     skip_to_iend;
5024
5025   volatile ssize_t
5026     image_count=0;
5027
5028   MagickBooleanType
5029     status;
5030
5031   MagickOffsetType
5032     offset;
5033
5034   MngInfo
5035     *mng_info;
5036
5037   MngBox
5038     default_fb,
5039     fb,
5040     previous_fb;
5041
5042 #if defined(MNG_INSERT_LAYERS)
5043   PixelInfo
5044     mng_background_color;
5045 #endif
5046
5047   register unsigned char
5048     *p;
5049
5050   register ssize_t
5051     i;
5052
5053   size_t
5054     count;
5055
5056   ssize_t
5057     loop_level;
5058
5059   volatile short
5060     skipping_loop;
5061
5062 #if defined(MNG_INSERT_LAYERS)
5063   unsigned int
5064     mandatory_back=0;
5065 #endif
5066
5067   volatile unsigned int
5068 #ifdef MNG_OBJECT_BUFFERS
5069     mng_background_object=0,
5070 #endif
5071     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5072
5073   size_t
5074     default_frame_timeout,
5075     frame_timeout,
5076 #if defined(MNG_INSERT_LAYERS)
5077     image_height,
5078     image_width,
5079 #endif
5080     length;
5081
5082   /* These delays are all measured in image ticks_per_second,
5083    * not in MNG ticks_per_second
5084    */
5085   volatile size_t
5086     default_frame_delay,
5087     final_delay,
5088     final_image_delay,
5089     frame_delay,
5090 #if defined(MNG_INSERT_LAYERS)
5091     insert_layers,
5092 #endif
5093     mng_iterations=1,
5094     simplicity=0,
5095     subframe_height=0,
5096     subframe_width=0;
5097
5098   previous_fb.top=0;
5099   previous_fb.bottom=0;
5100   previous_fb.left=0;
5101   previous_fb.right=0;
5102   default_fb.top=0;
5103   default_fb.bottom=0;
5104   default_fb.left=0;
5105   default_fb.right=0;
5106
5107   /* Open image file.  */
5108
5109   assert(image_info != (const ImageInfo *) NULL);
5110   assert(image_info->signature == MagickSignature);
5111   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
5112   assert(exception != (ExceptionInfo *) NULL);
5113   assert(exception->signature == MagickSignature);
5114   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
5115   image=AcquireImage(image_info,exception);
5116   mng_info=(MngInfo *) NULL;
5117   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5118
5119   if (status == MagickFalse)
5120     return((Image *) NULL);
5121
5122   first_mng_object=MagickFalse;
5123   skipping_loop=(-1);
5124   have_mng_structure=MagickFalse;
5125
5126   /* Allocate a MngInfo structure.  */
5127
5128   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
5129
5130   if (mng_info == (MngInfo *) NULL)
5131     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5132
5133   /* Initialize members of the MngInfo structure.  */
5134
5135   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5136   mng_info->image=image;
5137   have_mng_structure=MagickTrue;
5138
5139   if (LocaleCompare(image_info->magick,"MNG") == 0)
5140     {
5141       char
5142         magic_number[MaxTextExtent];
5143
5144       /* Verify MNG signature.  */
5145       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5146       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5147         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5148
5149       /* Initialize some nonzero members of the MngInfo structure.  */
5150       for (i=0; i < MNG_MAX_OBJECTS; i++)
5151       {
5152         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5153         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5154       }
5155       mng_info->exists[0]=MagickTrue;
5156     }
5157
5158   first_mng_object=MagickTrue;
5159   mng_type=0;
5160 #if defined(MNG_INSERT_LAYERS)
5161   insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5162 #endif
5163   default_frame_delay=0;
5164   default_frame_timeout=0;
5165   frame_delay=0;
5166   final_delay=1;
5167   mng_info->ticks_per_second=1UL*image->ticks_per_second;
5168   object_id=0;
5169   skip_to_iend=MagickFalse;
5170   term_chunk_found=MagickFalse;
5171   mng_info->framing_mode=1;
5172 #if defined(MNG_INSERT_LAYERS)
5173   mandatory_back=MagickFalse;
5174 #endif
5175 #if defined(MNG_INSERT_LAYERS)
5176   mng_background_color=image->background_color;
5177 #endif
5178   default_fb=mng_info->frame;
5179   previous_fb=mng_info->frame;
5180   do
5181   {
5182     char
5183       type[MaxTextExtent];
5184
5185     if (LocaleCompare(image_info->magick,"MNG") == 0)
5186       {
5187         unsigned char
5188           *chunk;
5189
5190         /*
5191           Read a new chunk.
5192         */
5193         type[0]='\0';
5194         (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
5195         length=ReadBlobMSBLong(image);
5196         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5197
5198         if (logging != MagickFalse)
5199           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5200            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
5201            type[0],type[1],type[2],type[3],(double) length);
5202
5203         if (length > PNG_UINT_31_MAX)
5204           status=MagickFalse;
5205
5206         if (count == 0)
5207           ThrowReaderException(CorruptImageError,"CorruptImage");
5208
5209         p=NULL;
5210         chunk=(unsigned char *) NULL;
5211
5212         if (length)
5213           {
5214             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5215
5216             if (chunk == (unsigned char *) NULL)
5217               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5218
5219             for (i=0; i < (ssize_t) length; i++)
5220               chunk[i]=(unsigned char) ReadBlobByte(image);
5221
5222             p=chunk;
5223           }
5224
5225         (void) ReadBlobMSBLong(image);  /* read crc word */
5226
5227 #if !defined(JNG_SUPPORTED)
5228         if (memcmp(type,mng_JHDR,4) == 0)
5229           {
5230             skip_to_iend=MagickTrue;
5231
5232             if (mng_info->jhdr_warning == 0)
5233               (void) ThrowMagickException(exception,GetMagickModule(),
5234                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5235
5236             mng_info->jhdr_warning++;
5237           }
5238 #endif
5239         if (memcmp(type,mng_DHDR,4) == 0)
5240           {
5241             skip_to_iend=MagickTrue;
5242
5243             if (mng_info->dhdr_warning == 0)
5244               (void) ThrowMagickException(exception,GetMagickModule(),
5245                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5246
5247             mng_info->dhdr_warning++;
5248           }
5249         if (memcmp(type,mng_MEND,4) == 0)
5250           break;
5251
5252         if (skip_to_iend)
5253           {
5254             if (memcmp(type,mng_IEND,4) == 0)
5255               skip_to_iend=MagickFalse;
5256
5257             if (length)
5258               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5259
5260             if (logging != MagickFalse)
5261               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5262                 "  Skip to IEND.");
5263
5264             continue;
5265           }
5266
5267         if (memcmp(type,mng_MHDR,4) == 0)
5268           {
5269             mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5270                 (p[2] << 8) | p[3]);
5271
5272             mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5273                 (p[6] << 8) | p[7]);
5274
5275             if (logging != MagickFalse)
5276               {
5277                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5278                   "  MNG width: %.20g",(double) mng_info->mng_width);
5279                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5280                   "  MNG height: %.20g",(double) mng_info->mng_height);
5281               }
5282
5283             p+=8;
5284             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5285
5286             if (mng_info->ticks_per_second == 0)
5287               default_frame_delay=0;
5288
5289             else
5290               default_frame_delay=1UL*image->ticks_per_second/
5291                 mng_info->ticks_per_second;
5292
5293             frame_delay=default_frame_delay;
5294             simplicity=0;
5295
5296             if (length > 16)
5297               {
5298                 p+=16;
5299                 simplicity=(size_t) mng_get_long(p);
5300               }
5301
5302             mng_type=1;    /* Full MNG */
5303
5304             if ((simplicity != 0) && ((simplicity | 11) == 11))
5305               mng_type=2; /* LC */
5306
5307             if ((simplicity != 0) && ((simplicity | 9) == 9))
5308               mng_type=3; /* VLC */
5309
5310 #if defined(MNG_INSERT_LAYERS)
5311             if (mng_type != 3)
5312               insert_layers=MagickTrue;
5313 #endif
5314             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5315               {
5316                 /* Allocate next image structure.  */
5317                 AcquireNextImage(image_info,image,exception);
5318
5319                 if (GetNextImageInList(image) == (Image *) NULL)
5320                   return((Image *) NULL);
5321
5322                 image=SyncNextImageInList(image);
5323                 mng_info->image=image;
5324               }
5325
5326             if ((mng_info->mng_width > 65535L) ||
5327                 (mng_info->mng_height > 65535L))
5328               ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5329
5330             (void) FormatLocaleString(page_geometry,MaxTextExtent,
5331               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5332               mng_info->mng_height);
5333
5334             mng_info->frame.left=0;
5335             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5336             mng_info->frame.top=0;
5337             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5338             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5339
5340             for (i=0; i < MNG_MAX_OBJECTS; i++)
5341               mng_info->object_clip[i]=mng_info->frame;
5342
5343             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5344             continue;
5345           }
5346
5347         if (memcmp(type,mng_TERM,4) == 0)
5348           {
5349             int
5350               repeat=0;
5351
5352
5353             if (length)
5354               repeat=p[0];
5355
5356             if (repeat == 3)
5357               {
5358                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5359                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5360
5361                 if (mng_iterations == PNG_UINT_31_MAX)
5362                   mng_iterations=0;
5363
5364                 image->iterations=mng_iterations;
5365                 term_chunk_found=MagickTrue;
5366               }
5367
5368             if (logging != MagickFalse)
5369               {
5370                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5371                   "    repeat=%d,  final_delay=%.20g,  iterations=%.20g",
5372                   repeat,(double) final_delay, (double) image->iterations);
5373               }
5374
5375             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5376             continue;
5377           }
5378         if (memcmp(type,mng_DEFI,4) == 0)
5379           {
5380             if (mng_type == 3)
5381               (void) ThrowMagickException(exception,GetMagickModule(),
5382                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5383                 image->filename);
5384
5385             object_id=(p[0] << 8) | p[1];
5386
5387             if (mng_type == 2 && object_id != 0)
5388               (void) ThrowMagickException(exception,GetMagickModule(),
5389                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5390                 image->filename);
5391
5392             if (object_id > MNG_MAX_OBJECTS)
5393               {
5394                 /*
5395                   Instead of using a warning we should allocate a larger
5396                   MngInfo structure and continue.
5397                 */
5398                 (void) ThrowMagickException(exception,GetMagickModule(),
5399                   CoderError,"object id too large","`%s'",image->filename);
5400                 object_id=MNG_MAX_OBJECTS;
5401               }
5402
5403             if (mng_info->exists[object_id])
5404               if (mng_info->frozen[object_id])
5405                 {
5406                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5407                   (void) ThrowMagickException(exception,
5408                     GetMagickModule(),CoderError,
5409                     "DEFI cannot redefine a frozen MNG object","`%s'",
5410                     image->filename);
5411                   continue;
5412                 }
5413
5414             mng_info->exists[object_id]=MagickTrue;
5415
5416             if (length > 2)
5417               mng_info->invisible[object_id]=p[2];
5418
5419             /*
5420               Extract object offset info.
5421             */
5422             if (length > 11)
5423               {
5424                 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5425                     (p[5] << 16) | (p[6] << 8) | p[7]);
5426
5427                 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5428                     (p[9] << 16) | (p[10] << 8) | p[11]);
5429
5430                 if (logging != MagickFalse)
5431                   {
5432                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5433                       "  x_off[%d]: %.20g,  y_off[%d]: %.20g",
5434                       object_id,(double) mng_info->x_off[object_id],
5435                       object_id,(double) mng_info->y_off[object_id]);
5436                   }
5437               }
5438
5439             /*
5440               Extract object clipping info.
5441             */
5442             if (length > 27)
5443               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5444                 &p[12]);
5445
5446             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5447             continue;
5448           }
5449         if (memcmp(type,mng_bKGD,4) == 0)
5450           {
5451             mng_info->have_global_bkgd=MagickFalse;
5452
5453             if (length > 5)
5454               {
5455                 mng_info->mng_global_bkgd.red=
5456                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5457
5458                 mng_info->mng_global_bkgd.green=
5459                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5460
5461                 mng_info->mng_global_bkgd.blue=
5462                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5463
5464                 mng_info->have_global_bkgd=MagickTrue;
5465               }
5466
5467             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5468             continue;
5469           }
5470         if (memcmp(type,mng_BACK,4) == 0)
5471           {
5472 #if defined(MNG_INSERT_LAYERS)
5473             if (length > 6)
5474               mandatory_back=p[6];
5475
5476             else
5477               mandatory_back=0;
5478
5479             if (mandatory_back && length > 5)
5480               {
5481                 mng_background_color.red=
5482                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5483
5484                 mng_background_color.green=
5485                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5486
5487                 mng_background_color.blue=
5488                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5489
5490                 mng_background_color.alpha=OpaqueAlpha;
5491               }
5492
5493 #ifdef MNG_OBJECT_BUFFERS
5494             if (length > 8)
5495               mng_background_object=(p[7] << 8) | p[8];
5496 #endif
5497 #endif
5498             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5499             continue;
5500           }
5501
5502         if (memcmp(type,mng_PLTE,4) == 0)
5503           {
5504             /* Read global PLTE.  */
5505
5506             if (length && (length < 769))
5507               {
5508                 if (mng_info->global_plte == (png_colorp) NULL)
5509                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5510                     sizeof(*mng_info->global_plte));
5511
5512                 for (i=0; i < (ssize_t) (length/3); i++)
5513                 {
5514                   mng_info->global_plte[i].red=p[3*i];
5515                   mng_info->global_plte[i].green=p[3*i+1];
5516                   mng_info->global_plte[i].blue=p[3*i+2];
5517                 }
5518
5519                 mng_info->global_plte_length=(unsigned int) (length/3);
5520               }
5521 #ifdef MNG_LOOSE
5522             for ( ; i < 256; i++)
5523             {
5524               mng_info->global_plte[i].red=i;
5525               mng_info->global_plte[i].green=i;
5526               mng_info->global_plte[i].blue=i;
5527             }
5528
5529             if (length)
5530               mng_info->global_plte_length=256;
5531 #endif
5532             else
5533               mng_info->global_plte_length=0;
5534
5535             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5536             continue;
5537           }
5538
5539         if (memcmp(type,mng_tRNS,4) == 0)
5540           {
5541             /* read global tRNS */
5542
5543             if (length < 257)
5544               for (i=0; i < (ssize_t) length; i++)
5545                 mng_info->global_trns[i]=p[i];
5546
5547 #ifdef MNG_LOOSE
5548             for ( ; i < 256; i++)
5549               mng_info->global_trns[i]=255;
5550 #endif
5551             mng_info->global_trns_length=(unsigned int) length;
5552             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5553             continue;
5554           }
5555         if (memcmp(type,mng_gAMA,4) == 0)
5556           {
5557             if (length == 4)
5558               {
5559                 ssize_t
5560                   igamma;
5561
5562                 igamma=mng_get_long(p);
5563                 mng_info->global_gamma=((float) igamma)*0.00001;
5564                 mng_info->have_global_gama=MagickTrue;
5565               }
5566
5567             else
5568               mng_info->have_global_gama=MagickFalse;
5569
5570             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5571             continue;
5572           }
5573
5574         if (memcmp(type,mng_cHRM,4) == 0)
5575           {
5576             /* Read global cHRM */
5577
5578             if (length == 32)
5579               {
5580                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5581                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5582                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5583                 mng_info->global_chrm.red_primary.y=0.00001*
5584                   mng_get_long(&p[12]);
5585                 mng_info->global_chrm.green_primary.x=0.00001*
5586                   mng_get_long(&p[16]);
5587                 mng_info->global_chrm.green_primary.y=0.00001*
5588                   mng_get_long(&p[20]);
5589                 mng_info->global_chrm.blue_primary.x=0.00001*
5590                   mng_get_long(&p[24]);
5591                 mng_info->global_chrm.blue_primary.y=0.00001*
5592                   mng_get_long(&p[28]);
5593                 mng_info->have_global_chrm=MagickTrue;
5594               }
5595             else
5596               mng_info->have_global_chrm=MagickFalse;
5597
5598             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5599             continue;
5600           }
5601
5602         if (memcmp(type,mng_sRGB,4) == 0)
5603           {
5604             /*
5605               Read global sRGB.
5606             */
5607             if (length)
5608               {
5609                 mng_info->global_srgb_intent=
5610                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5611                 mng_info->have_global_srgb=MagickTrue;
5612               }
5613             else
5614               mng_info->have_global_srgb=MagickFalse;
5615
5616             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5617             continue;
5618           }
5619
5620         if (memcmp(type,mng_iCCP,4) == 0)
5621           {
5622             /* To do: */
5623
5624             /*
5625               Read global iCCP.
5626             */
5627             if (length)
5628               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5629
5630             continue;
5631           }
5632
5633         if (memcmp(type,mng_FRAM,4) == 0)
5634           {
5635             if (mng_type == 3)
5636               (void) ThrowMagickException(exception,GetMagickModule(),
5637                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5638                 image->filename);
5639
5640             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5641               image->delay=frame_delay;
5642
5643             frame_delay=default_frame_delay;
5644             frame_timeout=default_frame_timeout;
5645             fb=default_fb;
5646
5647             if (length)
5648               if (p[0])
5649                 mng_info->framing_mode=p[0];
5650
5651             if (logging != MagickFalse)
5652               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5653                 "    Framing_mode=%d",mng_info->framing_mode);
5654
5655             if (length > 6)
5656               {
5657                 /* Note the delay and frame clipping boundaries.  */
5658
5659                 p++; /* framing mode */
5660
5661                 while (*p && ((p-chunk) < (ssize_t) length))
5662                   p++;  /* frame name */
5663
5664                 p++;  /* frame name terminator */
5665
5666                 if ((p-chunk) < (ssize_t) (length-4))
5667                   {
5668                     int
5669                       change_delay,
5670                       change_timeout,
5671                       change_clipping;
5672
5673                     change_delay=(*p++);
5674                     change_timeout=(*p++);
5675                     change_clipping=(*p++);
5676                     p++; /* change_sync */
5677
5678                     if (change_delay)
5679                       {
5680                         frame_delay=1UL*image->ticks_per_second*
5681                           mng_get_long(p);
5682
5683                         if (mng_info->ticks_per_second != 0)
5684                           frame_delay/=mng_info->ticks_per_second;
5685
5686                         else
5687                           frame_delay=PNG_UINT_31_MAX;
5688
5689                         if (change_delay == 2)
5690                           default_frame_delay=frame_delay;
5691
5692                         p+=4;
5693
5694                         if (logging != MagickFalse)
5695                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5696                             "    Framing_delay=%.20g",(double) frame_delay);
5697                       }
5698
5699                     if (change_timeout)
5700                       {
5701                         frame_timeout=1UL*image->ticks_per_second*
5702                           mng_get_long(p);
5703
5704                         if (mng_info->ticks_per_second != 0)
5705                           frame_timeout/=mng_info->ticks_per_second;
5706
5707                         else
5708                           frame_timeout=PNG_UINT_31_MAX;
5709
5710                         if (change_delay == 2)
5711                           default_frame_timeout=frame_timeout;
5712
5713                         p+=4;
5714
5715                         if (logging != MagickFalse)
5716                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5717                             "    Framing_timeout=%.20g",(double) frame_timeout);
5718                       }
5719
5720                     if (change_clipping)
5721                       {
5722                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5723                         p+=17;
5724                         previous_fb=fb;
5725
5726                         if (logging != MagickFalse)
5727                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5728                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5729                             (double) fb.left,(double) fb.right,(double) fb.top,
5730                             (double) fb.bottom);
5731
5732                         if (change_clipping == 2)
5733                           default_fb=fb;
5734                       }
5735                   }
5736               }
5737             mng_info->clip=fb;
5738             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5739
5740             subframe_width=(size_t) (mng_info->clip.right
5741                -mng_info->clip.left);
5742
5743             subframe_height=(size_t) (mng_info->clip.bottom
5744                -mng_info->clip.top);
5745             /*
5746               Insert a background layer behind the frame if framing_mode is 4.
5747             */
5748 #if defined(MNG_INSERT_LAYERS)
5749             if (logging != MagickFalse)
5750               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5751                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
5752                 subframe_width,(double) subframe_height);
5753
5754             if (insert_layers && (mng_info->framing_mode == 4) &&
5755                 (subframe_width) && (subframe_height))
5756               {
5757                 /* Allocate next image structure.  */
5758                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5759                   {
5760                     AcquireNextImage(image_info,image,exception);
5761
5762                     if (GetNextImageInList(image) == (Image *) NULL)
5763                       {
5764                         image=DestroyImageList(image);
5765                         MngInfoFreeStruct(mng_info,&have_mng_structure);
5766                         return((Image *) NULL);
5767                       }
5768
5769                     image=SyncNextImageInList(image);
5770                   }
5771
5772                 mng_info->image=image;
5773
5774                 if (term_chunk_found)
5775                   {
5776                     image->start_loop=MagickTrue;
5777                     image->iterations=mng_iterations;
5778                     term_chunk_found=MagickFalse;
5779                   }
5780
5781                 else
5782                     image->start_loop=MagickFalse;
5783
5784                 image->columns=subframe_width;
5785                 image->rows=subframe_height;
5786                 image->page.width=subframe_width;
5787                 image->page.height=subframe_height;
5788                 image->page.x=mng_info->clip.left;
5789                 image->page.y=mng_info->clip.top;
5790                 image->background_color=mng_background_color;
5791                 image->alpha_trait=UndefinedPixelTrait;
5792                 image->delay=0;
5793                 (void) SetImageBackgroundColor(image,exception);
5794
5795                 if (logging != MagickFalse)
5796                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5797                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5798                     (double) mng_info->clip.left,(double) mng_info->clip.right,
5799                     (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5800               }
5801 #endif
5802             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5803             continue;
5804           }
5805         if (memcmp(type,mng_CLIP,4) == 0)
5806           {
5807             unsigned int
5808               first_object,
5809               last_object;
5810
5811             /*
5812               Read CLIP.
5813             */
5814             first_object=(p[0] << 8) | p[1];
5815             last_object=(p[2] << 8) | p[3];
5816
5817             for (i=(int) first_object; i <= (int) last_object; i++)
5818             {
5819               if (mng_info->exists[i] && !mng_info->frozen[i])
5820                 {
5821                   MngBox
5822                     box;
5823
5824                   box=mng_info->object_clip[i];
5825                   mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5826                 }
5827             }
5828
5829             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5830             continue;
5831           }
5832         if (memcmp(type,mng_SAVE,4) == 0)
5833           {
5834             for (i=1; i < MNG_MAX_OBJECTS; i++)
5835               if (mng_info->exists[i])
5836                 {
5837                  mng_info->frozen[i]=MagickTrue;
5838 #ifdef MNG_OBJECT_BUFFERS
5839                  if (mng_info->ob[i] != (MngBuffer *) NULL)
5840                     mng_info->ob[i]->frozen=MagickTrue;
5841 #endif
5842                 }
5843
5844             if (length)
5845               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5846
5847             continue;
5848           }
5849
5850         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5851           {
5852             /* Read DISC or SEEK.  */
5853
5854             if ((length == 0) || !memcmp(type,mng_SEEK,4))
5855               {
5856                 for (i=1; i < MNG_MAX_OBJECTS; i++)
5857                   MngInfoDiscardObject(mng_info,i);
5858               }
5859
5860             else
5861               {
5862                 register ssize_t
5863                   j;
5864
5865                 for (j=0; j < (ssize_t) length; j+=2)
5866                 {
5867                   i=p[j] << 8 | p[j+1];
5868                   MngInfoDiscardObject(mng_info,i);
5869                 }
5870               }
5871
5872             if (length)
5873               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5874
5875             continue;
5876           }
5877
5878         if (memcmp(type,mng_MOVE,4) == 0)
5879           {
5880             size_t
5881               first_object,
5882               last_object;
5883
5884             /* read MOVE */
5885
5886             first_object=(p[0] << 8) | p[1];
5887             last_object=(p[2] << 8) | p[3];
5888             for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5889             {
5890               if (mng_info->exists[i] && !mng_info->frozen[i])
5891                 {
5892                   MngPair
5893                     new_pair;
5894
5895                   MngPair
5896                     old_pair;
5897
5898                   old_pair.a=mng_info->x_off[i];
5899                   old_pair.b=mng_info->y_off[i];
5900                   new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5901                   mng_info->x_off[i]=new_pair.a;
5902                   mng_info->y_off[i]=new_pair.b;
5903                 }
5904             }
5905
5906             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5907             continue;
5908           }
5909
5910         if (memcmp(type,mng_LOOP,4) == 0)
5911           {
5912             ssize_t loop_iters=1;
5913             loop_level=chunk[0];
5914             mng_info->loop_active[loop_level]=1;  /* mark loop active */
5915
5916             /* Record starting point.  */
5917             loop_iters=mng_get_long(&chunk[1]);
5918
5919             if (logging != MagickFalse)
5920               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5921                 "  LOOP level %.20g has %.20g iterations ",(double) loop_level,
5922                 (double) loop_iters);
5923
5924             if (loop_iters == 0)
5925               skipping_loop=loop_level;
5926
5927             else
5928               {
5929                 mng_info->loop_jump[loop_level]=TellBlob(image);
5930                 mng_info->loop_count[loop_level]=loop_iters;
5931               }
5932
5933             mng_info->loop_iteration[loop_level]=0;
5934             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5935             continue;
5936           }
5937
5938         if (memcmp(type,mng_ENDL,4) == 0)
5939           {
5940             loop_level=chunk[0];
5941
5942             if (skipping_loop > 0)
5943               {
5944                 if (skipping_loop == loop_level)
5945                   {
5946                     /*
5947                       Found end of zero-iteration loop.
5948                     */
5949                     skipping_loop=(-1);
5950                     mng_info->loop_active[loop_level]=0;
5951                   }
5952               }
5953
5954             else
5955               {
5956                 if (mng_info->loop_active[loop_level] == 1)
5957                   {
5958                     mng_info->loop_count[loop_level]--;
5959                     mng_info->loop_iteration[loop_level]++;
5960
5961                     if (logging != MagickFalse)
5962                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5963                         "  ENDL: LOOP level %.20g has %.20g remaining iters ",
5964                         (double) loop_level,(double)
5965                         mng_info->loop_count[loop_level]);
5966
5967                     if (mng_info->loop_count[loop_level] != 0)
5968                       {
5969                         offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5970                           SEEK_SET);
5971
5972                         if (offset < 0)
5973                           ThrowReaderException(CorruptImageError,
5974                             "ImproperImageHeader");
5975                       }
5976
5977                     else
5978                       {
5979                         short
5980                           last_level;
5981
5982                         /*
5983                           Finished loop.
5984                         */
5985                         mng_info->loop_active[loop_level]=0;
5986                         last_level=(-1);
5987                         for (i=0; i < loop_level; i++)
5988                           if (mng_info->loop_active[i] == 1)
5989                             last_level=(short) i;
5990                         loop_level=last_level;
5991                       }
5992                   }
5993               }
5994
5995             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5996             continue;
5997           }
5998
5999         if (memcmp(type,mng_CLON,4) == 0)
6000           {
6001             if (mng_info->clon_warning == 0)
6002               (void) ThrowMagickException(exception,GetMagickModule(),
6003                 CoderError,"CLON is not implemented yet","`%s'",
6004                 image->filename);
6005
6006             mng_info->clon_warning++;
6007           }
6008
6009         if (memcmp(type,mng_MAGN,4) == 0)
6010           {
6011             png_uint_16
6012               magn_first,
6013               magn_last,
6014               magn_mb,
6015               magn_ml,
6016               magn_mr,
6017               magn_mt,
6018               magn_mx,
6019               magn_my,
6020               magn_methx,
6021               magn_methy;
6022
6023             if (length > 1)
6024               magn_first=(p[0] << 8) | p[1];
6025
6026             else
6027               magn_first=0;
6028
6029             if (length > 3)
6030               magn_last=(p[2] << 8) | p[3];
6031
6032             else
6033               magn_last=magn_first;
6034 #ifndef MNG_OBJECT_BUFFERS
6035             if (magn_first || magn_last)
6036               if (mng_info->magn_warning == 0)
6037                 {
6038                   (void) ThrowMagickException(exception,
6039                      GetMagickModule(),CoderError,
6040                      "MAGN is not implemented yet for nonzero objects",
6041                      "`%s'",image->filename);
6042
6043                    mng_info->magn_warning++;
6044                 }
6045 #endif
6046             if (length > 4)
6047               magn_methx=p[4];
6048
6049             else
6050               magn_methx=0;
6051
6052             if (length > 6)
6053               magn_mx=(p[5] << 8) | p[6];
6054
6055             else
6056               magn_mx=1;
6057
6058             if (magn_mx == 0)
6059               magn_mx=1;
6060
6061             if (length > 8)
6062               magn_my=(p[7] << 8) | p[8];
6063
6064             else
6065               magn_my=magn_mx;
6066
6067             if (magn_my == 0)
6068               magn_my=1;
6069
6070             if (length > 10)
6071               magn_ml=(p[9] << 8) | p[10];
6072
6073             else
6074               magn_ml=magn_mx;
6075
6076             if (magn_ml == 0)
6077               magn_ml=1;
6078
6079             if (length > 12)
6080               magn_mr=(p[11] << 8) | p[12];
6081
6082             else
6083               magn_mr=magn_mx;
6084
6085             if (magn_mr == 0)
6086               magn_mr=1;
6087
6088             if (length > 14)
6089               magn_mt=(p[13] << 8) | p[14];
6090
6091             else
6092               magn_mt=magn_my;
6093
6094             if (magn_mt == 0)
6095               magn_mt=1;
6096
6097             if (length > 16)
6098               magn_mb=(p[15] << 8) | p[16];
6099
6100             else
6101               magn_mb=magn_my;
6102
6103             if (magn_mb == 0)
6104               magn_mb=1;
6105
6106             if (length > 17)
6107               magn_methy=p[17];
6108
6109             else
6110               magn_methy=magn_methx;
6111
6112
6113             if (magn_methx > 5 || magn_methy > 5)
6114               if (mng_info->magn_warning == 0)
6115                 {
6116                   (void) ThrowMagickException(exception,
6117                      GetMagickModule(),CoderError,
6118                      "Unknown MAGN method in MNG datastream","`%s'",
6119                      image->filename);
6120
6121                    mng_info->magn_warning++;
6122                 }
6123 #ifdef MNG_OBJECT_BUFFERS
6124           /* Magnify existing objects in the range magn_first to magn_last */
6125 #endif
6126             if (magn_first == 0 || magn_last == 0)
6127               {
6128                 /* Save the magnification factors for object 0 */
6129                 mng_info->magn_mb=magn_mb;
6130                 mng_info->magn_ml=magn_ml;
6131                 mng_info->magn_mr=magn_mr;
6132                 mng_info->magn_mt=magn_mt;
6133                 mng_info->magn_mx=magn_mx;
6134                 mng_info->magn_my=magn_my;
6135                 mng_info->magn_methx=magn_methx;
6136                 mng_info->magn_methy=magn_methy;
6137               }
6138           }
6139
6140         if (memcmp(type,mng_PAST,4) == 0)
6141           {
6142             if (mng_info->past_warning == 0)
6143               (void) ThrowMagickException(exception,GetMagickModule(),
6144                 CoderError,"PAST is not implemented yet","`%s'",
6145                 image->filename);
6146
6147             mng_info->past_warning++;
6148           }
6149
6150         if (memcmp(type,mng_SHOW,4) == 0)
6151           {
6152             if (mng_info->show_warning == 0)
6153               (void) ThrowMagickException(exception,GetMagickModule(),
6154                 CoderError,"SHOW is not implemented yet","`%s'",
6155                 image->filename);
6156
6157             mng_info->show_warning++;
6158           }
6159
6160         if (memcmp(type,mng_sBIT,4) == 0)
6161           {
6162             if (length < 4)
6163               mng_info->have_global_sbit=MagickFalse;
6164
6165             else
6166               {
6167                 mng_info->global_sbit.gray=p[0];
6168                 mng_info->global_sbit.red=p[0];
6169                 mng_info->global_sbit.green=p[1];
6170                 mng_info->global_sbit.blue=p[2];
6171                 mng_info->global_sbit.alpha=p[3];
6172                 mng_info->have_global_sbit=MagickTrue;
6173              }
6174           }
6175         if (memcmp(type,mng_pHYs,4) == 0)
6176           {
6177             if (length > 8)
6178               {
6179                 mng_info->global_x_pixels_per_unit=
6180                     (size_t) mng_get_long(p);
6181                 mng_info->global_y_pixels_per_unit=
6182                     (size_t) mng_get_long(&p[4]);
6183                 mng_info->global_phys_unit_type=p[8];
6184                 mng_info->have_global_phys=MagickTrue;
6185               }
6186
6187             else
6188               mng_info->have_global_phys=MagickFalse;
6189           }
6190         if (memcmp(type,mng_pHYg,4) == 0)
6191           {
6192             if (mng_info->phyg_warning == 0)
6193               (void) ThrowMagickException(exception,GetMagickModule(),
6194                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6195
6196             mng_info->phyg_warning++;
6197           }
6198         if (memcmp(type,mng_BASI,4) == 0)
6199           {
6200             skip_to_iend=MagickTrue;
6201
6202             if (mng_info->basi_warning == 0)
6203               (void) ThrowMagickException(exception,GetMagickModule(),
6204                 CoderError,"BASI is not implemented yet","`%s'",
6205                 image->filename);
6206
6207             mng_info->basi_warning++;
6208 #ifdef MNG_BASI_SUPPORTED
6209             basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6210                (p[2] << 8) | p[3]);
6211             basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6212                (p[6] << 8) | p[7]);
6213             basi_color_type=p[8];
6214             basi_compression_method=p[9];
6215             basi_filter_type=p[10];
6216             basi_interlace_method=p[11];
6217             if (length > 11)
6218               basi_red=(p[12] << 8) & p[13];
6219
6220             else
6221               basi_red=0;
6222
6223             if (length > 13)
6224               basi_green=(p[14] << 8) & p[15];
6225
6226             else
6227               basi_green=0;
6228
6229             if (length > 15)
6230               basi_blue=(p[16] << 8) & p[17];
6231
6232             else
6233               basi_blue=0;
6234
6235             if (length > 17)
6236               basi_alpha=(p[18] << 8) & p[19];
6237
6238             else
6239               {
6240                 if (basi_sample_depth == 16)
6241                   basi_alpha=65535L;
6242                 else
6243                   basi_alpha=255;
6244               }
6245
6246             if (length > 19)
6247               basi_viewable=p[20];
6248
6249             else
6250               basi_viewable=0;
6251
6252 #endif
6253             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6254             continue;
6255           }
6256
6257         if (memcmp(type,mng_IHDR,4)
6258 #if defined(JNG_SUPPORTED)
6259             && memcmp(type,mng_JHDR,4)
6260 #endif
6261             )
6262           {
6263             /* Not an IHDR or JHDR chunk */
6264             if (length)
6265               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6266
6267             continue;
6268           }
6269 /* Process IHDR */
6270         if (logging != MagickFalse)
6271           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6272             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6273
6274         mng_info->exists[object_id]=MagickTrue;
6275         mng_info->viewable[object_id]=MagickTrue;
6276
6277         if (mng_info->invisible[object_id])
6278           {
6279             if (logging != MagickFalse)
6280               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6281                 "  Skipping invisible object");
6282
6283             skip_to_iend=MagickTrue;
6284             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6285             continue;
6286           }
6287 #if defined(MNG_INSERT_LAYERS)
6288         if (length < 8)
6289           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6290
6291         image_width=(size_t) mng_get_long(p);
6292         image_height=(size_t) mng_get_long(&p[4]);
6293 #endif
6294         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6295
6296         /*
6297           Insert a transparent background layer behind the entire animation
6298           if it is not full screen.
6299         */
6300 #if defined(MNG_INSERT_LAYERS)
6301         if (insert_layers && mng_type && first_mng_object)
6302           {
6303             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6304                 (image_width < mng_info->mng_width) ||
6305                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6306                 (image_height < mng_info->mng_height) ||
6307                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6308               {
6309                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6310                   {
6311                     /*
6312                       Allocate next image structure.
6313                     */
6314                     AcquireNextImage(image_info,image,exception);
6315
6316                     if (GetNextImageInList(image) == (Image *) NULL)
6317                       {
6318                         image=DestroyImageList(image);
6319                         MngInfoFreeStruct(mng_info,&have_mng_structure);
6320                         return((Image *) NULL);
6321                       }
6322
6323                     image=SyncNextImageInList(image);
6324                   }
6325                 mng_info->image=image;
6326
6327                 if (term_chunk_found)
6328                   {
6329                     image->start_loop=MagickTrue;
6330                     image->iterations=mng_iterations;
6331                     term_chunk_found=MagickFalse;
6332                   }
6333
6334                 else
6335                     image->start_loop=MagickFalse;
6336
6337                 /* Make a background rectangle.  */
6338
6339                 image->delay=0;
6340                 image->columns=mng_info->mng_width;
6341                 image->rows=mng_info->mng_height;
6342                 image->page.width=mng_info->mng_width;
6343                 image->page.height=mng_info->mng_height;
6344                 image->page.x=0;
6345                 image->page.y=0;
6346                 image->background_color=mng_background_color;
6347                 (void) SetImageBackgroundColor(image,exception);
6348                 if (logging != MagickFalse)
6349                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6350                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6351                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6352               }
6353           }
6354         /*
6355           Insert a background layer behind the upcoming image if
6356           framing_mode is 3, and we haven't already inserted one.
6357         */
6358         if (insert_layers && (mng_info->framing_mode == 3) &&
6359                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6360                 (simplicity & 0x08)))
6361           {
6362             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6363             {
6364               /*
6365                 Allocate next image structure.
6366               */
6367               AcquireNextImage(image_info,image,exception);
6368
6369               if (GetNextImageInList(image) == (Image *) NULL)
6370                 {
6371                   image=DestroyImageList(image);
6372                   MngInfoFreeStruct(mng_info,&have_mng_structure);
6373                   return((Image *) NULL);
6374                 }
6375
6376               image=SyncNextImageInList(image);
6377             }
6378
6379             mng_info->image=image;
6380
6381             if (term_chunk_found)
6382               {
6383                 image->start_loop=MagickTrue;
6384                 image->iterations=mng_iterations;
6385                 term_chunk_found=MagickFalse;
6386               }
6387
6388             else
6389                 image->start_loop=MagickFalse;
6390
6391             image->delay=0;
6392             image->columns=subframe_width;
6393             image->rows=subframe_height;
6394             image->page.width=subframe_width;
6395             image->page.height=subframe_height;
6396             image->page.x=mng_info->clip.left;
6397             image->page.y=mng_info->clip.top;
6398             image->background_color=mng_background_color;
6399             image->alpha_trait=UndefinedPixelTrait;
6400             (void) SetImageBackgroundColor(image,exception);
6401
6402             if (logging != MagickFalse)
6403               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6404                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6405                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6406                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6407           }
6408 #endif /* MNG_INSERT_LAYERS */
6409         first_mng_object=MagickFalse;
6410
6411         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6412           {
6413             /*
6414               Allocate next image structure.
6415             */
6416             AcquireNextImage(image_info,image,exception);
6417
6418             if (GetNextImageInList(image) == (Image *) NULL)
6419               {
6420                 image=DestroyImageList(image);
6421                 MngInfoFreeStruct(mng_info,&have_mng_structure);
6422                 return((Image *) NULL);
6423               }
6424
6425             image=SyncNextImageInList(image);
6426           }
6427         mng_info->image=image;
6428         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6429           GetBlobSize(image));
6430
6431         if (status == MagickFalse)
6432           break;
6433
6434         if (term_chunk_found)
6435           {
6436             image->start_loop=MagickTrue;
6437             term_chunk_found=MagickFalse;
6438           }
6439
6440         else
6441             image->start_loop=MagickFalse;
6442
6443         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6444           {
6445             image->delay=frame_delay;
6446             frame_delay=default_frame_delay;
6447           }
6448
6449         else
6450           image->delay=0;
6451
6452         image->page.width=mng_info->mng_width;
6453         image->page.height=mng_info->mng_height;
6454         image->page.x=mng_info->x_off[object_id];
6455         image->page.y=mng_info->y_off[object_id];
6456         image->iterations=mng_iterations;
6457
6458         /*
6459           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6460         */
6461
6462         if (logging != MagickFalse)
6463           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6464             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6465             type[2],type[3]);
6466
6467         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6468
6469         if (offset < 0)
6470           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6471       }
6472
6473     previous=image;
6474     mng_info->image=image;
6475     mng_info->mng_type=mng_type;
6476     mng_info->object_id=object_id;
6477
6478     if (memcmp(type,mng_IHDR,4) == 0)
6479       image=ReadOnePNGImage(mng_info,image_info,exception);
6480
6481 #if defined(JNG_SUPPORTED)
6482     else
6483       image=ReadOneJNGImage(mng_info,image_info,exception);
6484 #endif
6485
6486     if (image == (Image *) NULL)
6487       {
6488         if (IsImageObject(previous) != MagickFalse)
6489           {
6490             (void) DestroyImageList(previous);
6491             (void) CloseBlob(previous);
6492           }
6493
6494         MngInfoFreeStruct(mng_info,&have_mng_structure);
6495         return((Image *) NULL);
6496       }
6497
6498     if (image->columns == 0 || image->rows == 0)
6499       {
6500         (void) CloseBlob(image);
6501         image=DestroyImageList(image);
6502         MngInfoFreeStruct(mng_info,&have_mng_structure);
6503         return((Image *) NULL);
6504       }
6505
6506     mng_info->image=image;
6507
6508     if (mng_type)
6509       {
6510         MngBox
6511           crop_box;
6512
6513         if (mng_info->magn_methx || mng_info->magn_methy)
6514           {
6515             png_uint_32
6516                magnified_height,
6517                magnified_width;
6518
6519             if (logging != MagickFalse)
6520               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6521                 "  Processing MNG MAGN chunk");
6522
6523             if (mng_info->magn_methx == 1)
6524               {
6525                 magnified_width=mng_info->magn_ml;
6526
6527                 if (image->columns > 1)
6528                    magnified_width += mng_info->magn_mr;
6529
6530                 if (image->columns > 2)
6531                    magnified_width += (png_uint_32)
6532                       ((image->columns-2)*(mng_info->magn_mx));
6533               }
6534
6535             else
6536               {
6537                 magnified_width=(png_uint_32) image->columns;
6538
6539                 if (image->columns > 1)
6540                    magnified_width += mng_info->magn_ml-1;
6541
6542                 if (image->columns > 2)
6543                    magnified_width += mng_info->magn_mr-1;
6544
6545                 if (image->columns > 3)
6546                    magnified_width += (png_uint_32)
6547                       ((image->columns-3)*(mng_info->magn_mx-1));
6548               }
6549
6550             if (mng_info->magn_methy == 1)
6551               {
6552                 magnified_height=mng_info->magn_mt;
6553
6554                 if (image->rows > 1)
6555                    magnified_height += mng_info->magn_mb;
6556
6557                 if (image->rows > 2)
6558                    magnified_height += (png_uint_32)
6559                       ((image->rows-2)*(mng_info->magn_my));
6560               }
6561
6562             else
6563               {
6564                 magnified_height=(png_uint_32) image->rows;
6565
6566                 if (image->rows > 1)
6567                    magnified_height += mng_info->magn_mt-1;
6568
6569                 if (image->rows > 2)
6570                    magnified_height += mng_info->magn_mb-1;
6571
6572                 if (image->rows > 3)
6573                    magnified_height += (png_uint_32)
6574                       ((image->rows-3)*(mng_info->magn_my-1));
6575               }
6576
6577             if (magnified_height > image->rows ||
6578                 magnified_width > image->columns)
6579               {
6580                 Image
6581                   *large_image;
6582
6583                 int
6584                   yy;
6585
6586                 Quantum
6587                   *next,
6588                   *prev;
6589
6590                 png_uint_16
6591                   magn_methx,
6592                   magn_methy;
6593
6594                 ssize_t
6595                   m,
6596                   y;
6597
6598                 register Quantum
6599                   *n,
6600                   *q;
6601
6602                 register ssize_t
6603                   x;
6604
6605                 /* Allocate next image structure.  */
6606
6607                 if (logging != MagickFalse)
6608                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6609                     "    Allocate magnified image");
6610
6611                 AcquireNextImage(image_info,image,exception);
6612
6613                 if (GetNextImageInList(image) == (Image *) NULL)
6614                   {
6615                     image=DestroyImageList(image);
6616                     MngInfoFreeStruct(mng_info,&have_mng_structure);
6617                     return((Image *) NULL);
6618                   }
6619
6620                 large_image=SyncNextImageInList(image);
6621
6622                 large_image->columns=magnified_width;
6623                 large_image->rows=magnified_height;
6624
6625                 magn_methx=mng_info->magn_methx;
6626                 magn_methy=mng_info->magn_methy;
6627
6628 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6629 #define QM unsigned short
6630                 if (magn_methx != 1 || magn_methy != 1)
6631                   {
6632                   /*
6633                      Scale pixels to unsigned shorts to prevent
6634                      overflow of intermediate values of interpolations
6635                   */
6636                      for (y=0; y < (ssize_t) image->rows; y++)
6637                      {
6638                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6639                           exception);
6640
6641                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6642                        {
6643                           SetPixelRed(image,ScaleQuantumToShort(
6644                             GetPixelRed(image,q)),q);
6645                           SetPixelGreen(image,ScaleQuantumToShort(
6646                             GetPixelGreen(image,q)),q);
6647                           SetPixelBlue(image,ScaleQuantumToShort(
6648                             GetPixelBlue(image,q)),q);
6649                           SetPixelAlpha(image,ScaleQuantumToShort(
6650                             GetPixelAlpha(image,q)),q);
6651                           q+=GetPixelChannels(image);
6652                        }
6653
6654                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6655                          break;
6656                      }
6657                   }
6658 #else
6659 #define QM Quantum
6660 #endif
6661
6662                 if (image->alpha_trait == BlendPixelTrait)
6663                    (void) SetImageBackgroundColor(large_image,exception);
6664
6665                 else
6666                   {
6667                     large_image->background_color.alpha=OpaqueAlpha;
6668                     (void) SetImageBackgroundColor(large_image,exception);
6669
6670                     if (magn_methx == 4)
6671                       magn_methx=2;
6672
6673                     if (magn_methx == 5)
6674                       magn_methx=3;
6675
6676                     if (magn_methy == 4)
6677                       magn_methy=2;
6678
6679                     if (magn_methy == 5)
6680                       magn_methy=3;
6681                   }
6682
6683                 /* magnify the rows into the right side of the large image */
6684
6685                 if (logging != MagickFalse)
6686                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6687                     "    Magnify the rows to %.20g",(double) large_image->rows);
6688                 m=(ssize_t) mng_info->magn_mt;
6689                 yy=0;
6690                 length=(size_t) image->columns*GetPixelChannels(image);
6691                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6692                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6693
6694                 if ((prev == (Quantum *) NULL) ||
6695                     (next == (Quantum *) NULL))
6696                   {
6697                      image=DestroyImageList(image);
6698                      MngInfoFreeStruct(mng_info,&have_mng_structure);
6699                      ThrowReaderException(ResourceLimitError,
6700                        "MemoryAllocationFailed");
6701                   }
6702
6703                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6704                 (void) CopyMagickMemory(next,n,length);
6705
6706                 for (y=0; y < (ssize_t) image->rows; y++)
6707                 {
6708                   if (y == 0)
6709                     m=(ssize_t) mng_info->magn_mt;
6710
6711                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6712                     m=(ssize_t) mng_info->magn_mb;
6713
6714                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6715                     m=(ssize_t) mng_info->magn_mb;
6716
6717                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6718                     m=1;
6719
6720                   else
6721                     m=(ssize_t) mng_info->magn_my;
6722
6723                   n=prev;
6724                   prev=next;
6725                   next=n;
6726
6727                   if (y < (ssize_t) image->rows-1)
6728                     {
6729                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6730                           exception);
6731                       (void) CopyMagickMemory(next,n,length);
6732                     }
6733
6734                   for (i=0; i < m; i++, yy++)
6735                   {
6736                     register Quantum
6737                       *pixels;
6738
6739                     assert(yy < (ssize_t) large_image->rows);
6740                     pixels=prev;
6741                     n=next;
6742                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6743                       1,exception);
6744                     q+=(large_image->columns-image->columns)*
6745                       GetPixelChannels(large_image);
6746
6747                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
6748                     {
6749                       /* To do: get color as function of indexes[x] */
6750                       /*
6751                       if (image->storage_class == PseudoClass)
6752                         {
6753                         }
6754                       */
6755
6756                       if (magn_methy <= 1)
6757                         {
6758                           /* replicate previous */
6759                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6760                           SetPixelGreen(large_image,GetPixelGreen(image,
6761                              pixels),q);
6762                           SetPixelBlue(large_image,GetPixelBlue(image,
6763                              pixels),q);
6764                           SetPixelAlpha(large_image,GetPixelAlpha(image,
6765                              pixels),q);
6766                         }
6767
6768                       else if (magn_methy == 2 || magn_methy == 4)
6769                         {
6770                           if (i == 0)
6771                             {
6772                               SetPixelRed(large_image,GetPixelRed(image,
6773                                  pixels),q);
6774                               SetPixelGreen(large_image,GetPixelGreen(image,
6775                                  pixels),q);
6776                               SetPixelBlue(large_image,GetPixelBlue(image,
6777                                  pixels),q);
6778                               SetPixelAlpha(large_image,GetPixelAlpha(image,
6779                                  pixels),q);
6780                             }
6781
6782                           else
6783                             {
6784                               /* Interpolate */
6785                               SetPixelRed(large_image,((QM) (((ssize_t)
6786                                  (2*i*(GetPixelRed(image,n)
6787                                  -GetPixelRed(image,pixels)+m))/
6788                                  ((ssize_t) (m*2))
6789                                  +GetPixelRed(image,pixels)))),q);
6790                               SetPixelGreen(large_image,((QM) (((ssize_t)
6791                                  (2*i*(GetPixelGreen(image,n)
6792                                  -GetPixelGreen(image,pixels)+m))/
6793                                  ((ssize_t) (m*2))
6794                                  +GetPixelGreen(image,pixels)))),q);
6795                               SetPixelBlue(large_image,((QM) (((ssize_t)
6796                                  (2*i*(GetPixelBlue(image,n)
6797                                  -GetPixelBlue(image,pixels)+m))/
6798                                  ((ssize_t) (m*2))
6799                                  +GetPixelBlue(image,pixels)))),q);
6800
6801                               if (image->alpha_trait == BlendPixelTrait)
6802                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
6803                                     (2*i*(GetPixelAlpha(image,n)
6804                                     -GetPixelAlpha(image,pixels)+m))
6805                                     /((ssize_t) (m*2))+
6806                                    GetPixelAlpha(image,pixels)))),q);
6807                             }
6808
6809                           if (magn_methy == 4)
6810                             {
6811                               /* Replicate nearest */
6812                               if (i <= ((m+1) << 1))
6813                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6814                                     pixels),q);
6815                               else
6816                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6817                                     n),q);
6818                             }
6819                         }
6820
6821                       else /* if (magn_methy == 3 || magn_methy == 5) */
6822                         {
6823                           /* Replicate nearest */
6824                           if (i <= ((m+1) << 1))
6825                           {
6826                              SetPixelRed(large_image,GetPixelRed(image,
6827                                     pixels),q);
6828                              SetPixelGreen(large_image,GetPixelGreen(image,
6829                                     pixels),q);
6830                              SetPixelBlue(large_image,GetPixelBlue(image,
6831                                     pixels),q);
6832                              SetPixelAlpha(large_image,GetPixelAlpha(image,
6833                                     pixels),q);
6834                           }
6835
6836                           else
6837                           {
6838                              SetPixelRed(large_image,GetPixelRed(image,n),q);
6839                              SetPixelGreen(large_image,GetPixelGreen(image,n),
6840                                     q);
6841                              SetPixelBlue(large_image,GetPixelBlue(image,n),
6842                                     q);
6843                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6844                                     q);
6845                           }
6846
6847                           if (magn_methy == 5)
6848                             {
6849                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6850                                  (GetPixelAlpha(image,n)
6851                                  -GetPixelAlpha(image,pixels))
6852                                  +m))/((ssize_t) (m*2))
6853                                  +GetPixelAlpha(image,pixels)),q);
6854                             }
6855                         }
6856                       n+=GetPixelChannels(image);
6857                       q+=GetPixelChannels(large_image);
6858                       pixels+=GetPixelChannels(image);
6859                     } /* x */
6860
6861                     if (SyncAuthenticPixels(large_image,exception) == 0)
6862                       break;
6863
6864                   } /* i */
6865                 } /* y */
6866
6867                 prev=(Quantum *) RelinquishMagickMemory(prev);
6868                 next=(Quantum *) RelinquishMagickMemory(next);
6869
6870                 length=image->columns;
6871
6872                 if (logging != MagickFalse)
6873                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6874                     "    Delete original image");
6875
6876                 DeleteImageFromList(&image);
6877
6878                 image=large_image;
6879
6880                 mng_info->image=image;
6881
6882                 /* magnify the columns */
6883                 if (logging != MagickFalse)
6884                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6885                     "    Magnify the columns to %.20g",(double) image->columns);
6886
6887                 for (y=0; y < (ssize_t) image->rows; y++)
6888                 {
6889                   register Quantum
6890                     *pixels;
6891
6892                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6893                   pixels=q+(image->columns-length)*GetPixelChannels(image);
6894                   n=pixels+GetPixelChannels(image);
6895
6896                   for (x=(ssize_t) (image->columns-length);
6897                     x < (ssize_t) image->columns; x++)
6898                   {
6899                     /* To do: Rewrite using Get/Set***PixelChannel() */
6900
6901                     if (x == (ssize_t) (image->columns-length))
6902                       m=(ssize_t) mng_info->magn_ml;
6903
6904                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6905                       m=(ssize_t) mng_info->magn_mr;
6906
6907                     else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6908                       m=(ssize_t) mng_info->magn_mr;
6909
6910                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6911                       m=1;
6912
6913                     else
6914                       m=(ssize_t) mng_info->magn_mx;
6915
6916                     for (i=0; i < m; i++)
6917                     {
6918                       if (magn_methx <= 1)
6919                         {
6920                           /* replicate previous */
6921                           SetPixelRed(image,GetPixelRed(image,pixels),q);
6922                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6923                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6924                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6925                         }
6926
6927                       else if (magn_methx == 2 || magn_methx == 4)
6928                         {
6929                           if (i == 0)
6930                           {
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                           /* To do: Rewrite using Get/Set***PixelChannel() */
6938                           else
6939                             {
6940                               /* Interpolate */
6941                               SetPixelRed(image,(QM) ((2*i*(
6942                                  GetPixelRed(image,n)
6943                                  -GetPixelRed(image,pixels))+m)
6944                                  /((ssize_t) (m*2))+
6945                                  GetPixelRed(image,pixels)),q);
6946
6947                               SetPixelGreen(image,(QM) ((2*i*(
6948                                  GetPixelGreen(image,n)
6949                                  -GetPixelGreen(image,pixels))+m)
6950                                  /((ssize_t) (m*2))+
6951                                  GetPixelGreen(image,pixels)),q);
6952
6953                               SetPixelBlue(image,(QM) ((2*i*(
6954                                  GetPixelBlue(image,n)
6955                                  -GetPixelBlue(image,pixels))+m)
6956                                  /((ssize_t) (m*2))+
6957                                  GetPixelBlue(image,pixels)),q);
6958                               if (image->alpha_trait == BlendPixelTrait)
6959                                  SetPixelAlpha(image,(QM) ((2*i*(
6960                                    GetPixelAlpha(image,n)
6961                                    -GetPixelAlpha(image,pixels))+m)
6962                                    /((ssize_t) (m*2))+
6963                                    GetPixelAlpha(image,pixels)),q);
6964                             }
6965
6966                           if (magn_methx == 4)
6967                             {
6968                               /* Replicate nearest */
6969                               if (i <= ((m+1) << 1))
6970                               {
6971                                  SetPixelAlpha(image,
6972                                    GetPixelAlpha(image,pixels)+0,q);
6973                               }
6974                               else
6975                               {
6976                                  SetPixelAlpha(image,
6977                                    GetPixelAlpha(image,n)+0,q);
6978                               }
6979                             }
6980                         }
6981
6982                       else /* if (magn_methx == 3 || magn_methx == 5) */
6983                         {
6984                           /* Replicate nearest */
6985                           if (i <= ((m+1) << 1))
6986                           {
6987                              SetPixelRed(image,GetPixelRed(image,pixels),q);
6988                              SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6989                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6990                              SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6991                           }
6992
6993                           else
6994                           {
6995                              SetPixelRed(image,GetPixelRed(image,n),q);
6996                              SetPixelGreen(image,GetPixelGreen(image,n),q);
6997                              SetPixelBlue(image,GetPixelBlue(image,n),q);
6998                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6999                           }
7000
7001                           if (magn_methx == 5)
7002                             {
7003                               /* Interpolate */
7004                               SetPixelAlpha(image,
7005                                  (QM) ((2*i*( GetPixelAlpha(image,n)
7006                                  -GetPixelAlpha(image,pixels))+m)/
7007                                  ((ssize_t) (m*2))
7008                                  +GetPixelAlpha(image,pixels)),q);
7009                             }
7010                         }
7011                       q+=GetPixelChannels(image);
7012                     }
7013                     n+=GetPixelChannels(image);
7014                   }
7015
7016                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
7017                     break;
7018                 }
7019 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7020               if (magn_methx != 1 || magn_methy != 1)
7021                 {
7022                 /*
7023                    Rescale pixels to Quantum
7024                 */
7025                    for (y=0; y < (ssize_t) image->rows; y++)
7026                    {
7027                      q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7028
7029                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
7030                      {
7031                         SetPixelRed(image,ScaleShortToQuantum(
7032                           GetPixelRed(image,q)),q);
7033                         SetPixelGreen(image,ScaleShortToQuantum(
7034                           GetPixelGreen(image,q)),q);
7035                         SetPixelBlue(image,ScaleShortToQuantum(
7036                           GetPixelBlue(image,q)),q);
7037                         SetPixelAlpha(image,ScaleShortToQuantum(
7038                           GetPixelAlpha(image,q)),q);
7039                         q+=GetPixelChannels(image);
7040                      }
7041
7042                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
7043                        break;
7044                    }
7045                 }
7046 #endif
7047                 if (logging != MagickFalse)
7048                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7049                     "  Finished MAGN processing");
7050               }
7051           }
7052
7053         /*
7054           Crop_box is with respect to the upper left corner of the MNG.
7055         */
7056         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7057         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7058         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7059         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7060         crop_box=mng_minimum_box(crop_box,mng_info->clip);
7061         crop_box=mng_minimum_box(crop_box,mng_info->frame);
7062         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7063         if ((crop_box.left != (mng_info->image_box.left
7064             +mng_info->x_off[object_id])) ||
7065             (crop_box.right != (mng_info->image_box.right
7066             +mng_info->x_off[object_id])) ||
7067             (crop_box.top != (mng_info->image_box.top
7068             +mng_info->y_off[object_id])) ||
7069             (crop_box.bottom != (mng_info->image_box.bottom
7070             +mng_info->y_off[object_id])))
7071           {
7072             if (logging != MagickFalse)
7073               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7074                 "  Crop the PNG image");
7075
7076             if ((crop_box.left < crop_box.right) &&
7077                 (crop_box.top < crop_box.bottom))
7078               {
7079                 Image
7080                   *im;
7081
7082                 RectangleInfo
7083                   crop_info;
7084
7085                 /*
7086                   Crop_info is with respect to the upper left corner of
7087                   the image.
7088                 */
7089                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7090                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7091                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7092                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7093                 image->page.width=image->columns;
7094                 image->page.height=image->rows;
7095                 image->page.x=0;
7096                 image->page.y=0;
7097                 im=CropImage(image,&crop_info,exception);
7098
7099                 if (im != (Image *) NULL)
7100                   {
7101                     image->columns=im->columns;
7102                     image->rows=im->rows;
7103                     im=DestroyImage(im);
7104                     image->page.width=image->columns;
7105                     image->page.height=image->rows;
7106                     image->page.x=crop_box.left;
7107                     image->page.y=crop_box.top;
7108                   }
7109               }
7110
7111             else
7112               {
7113                 /*
7114                   No pixels in crop area.  The MNG spec still requires
7115                   a layer, though, so make a single transparent pixel in
7116                   the top left corner.
7117                 */
7118                 image->columns=1;
7119                 image->rows=1;
7120                 image->colors=2;
7121                 (void) SetImageBackgroundColor(image,exception);
7122                 image->page.width=1;
7123                 image->page.height=1;
7124                 image->page.x=0;
7125                 image->page.y=0;
7126               }
7127           }
7128 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7129         image=mng_info->image;
7130 #endif
7131       }
7132
7133 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7134       /* PNG does not handle depths greater than 16 so reduce it even
7135        * if lossy.
7136        */
7137       if (image->depth > 16)
7138          image->depth=16;
7139 #endif
7140
7141 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7142       if (image->depth > 8)
7143         {
7144           /* To do: fill low byte properly */
7145           image->depth=16;
7146         }
7147
7148       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7149          image->depth = 8;
7150 #endif
7151
7152       if (image_info->number_scenes != 0)
7153         {
7154           if (mng_info->scenes_found >
7155              (ssize_t) (image_info->first_scene+image_info->number_scenes))
7156             break;
7157         }
7158
7159       if (logging != MagickFalse)
7160         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7161           "  Finished reading image datastream.");
7162
7163   } while (LocaleCompare(image_info->magick,"MNG") == 0);
7164
7165   (void) CloseBlob(image);
7166
7167   if (logging != MagickFalse)
7168     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7169       "  Finished reading all image datastreams.");
7170
7171 #if defined(MNG_INSERT_LAYERS)
7172   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7173        (mng_info->mng_height))
7174     {
7175       /*
7176         Insert a background layer if nothing else was found.
7177       */
7178       if (logging != MagickFalse)
7179         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7180           "  No images found.  Inserting a background layer.");
7181
7182       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7183         {
7184           /*
7185             Allocate next image structure.
7186           */
7187           AcquireNextImage(image_info,image,exception);
7188           if (GetNextImageInList(image) == (Image *) NULL)
7189             {
7190               image=DestroyImageList(image);
7191               MngInfoFreeStruct(mng_info,&have_mng_structure);
7192
7193               if (logging != MagickFalse)
7194                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7195                   "  Allocation failed, returning NULL.");
7196
7197               return((Image *) NULL);
7198             }
7199           image=SyncNextImageInList(image);
7200         }
7201       image->columns=mng_info->mng_width;
7202       image->rows=mng_info->mng_height;
7203       image->page.width=mng_info->mng_width;
7204       image->page.height=mng_info->mng_height;
7205       image->page.x=0;
7206       image->page.y=0;
7207       image->background_color=mng_background_color;
7208       image->alpha_trait=UndefinedPixelTrait;
7209
7210       if (image_info->ping == MagickFalse)
7211         (void) SetImageBackgroundColor(image,exception);
7212
7213       mng_info->image_found++;
7214     }
7215 #endif
7216   image->iterations=mng_iterations;
7217
7218   if (mng_iterations == 1)
7219     image->start_loop=MagickTrue;
7220
7221   while (GetPreviousImageInList(image) != (Image *) NULL)
7222   {
7223     image_count++;
7224     if (image_count > 10*mng_info->image_found)
7225       {
7226         if (logging != MagickFalse)
7227           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7228
7229         (void) ThrowMagickException(exception,GetMagickModule(),
7230           CoderError,"Linked list is corrupted, beginning of list not found",
7231           "`%s'",image_info->filename);
7232
7233         return((Image *) NULL);
7234       }
7235
7236     image=GetPreviousImageInList(image);
7237
7238     if (GetNextImageInList(image) == (Image *) NULL)
7239       {
7240         if (logging != MagickFalse)
7241           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7242
7243         (void) ThrowMagickException(exception,GetMagickModule(),
7244           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7245           image_info->filename);
7246       }
7247   }
7248
7249   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7250              GetNextImageInList(image) ==
7251      (Image *) NULL)
7252     {
7253       if (logging != MagickFalse)
7254         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7255             "  First image null");
7256
7257       (void) ThrowMagickException(exception,GetMagickModule(),
7258         CoderError,"image->next for first image is NULL but shouldn't be.",
7259         "`%s'",image_info->filename);
7260     }
7261
7262   if (mng_info->image_found == 0)
7263     {
7264       if (logging != MagickFalse)
7265         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7266           "  No visible images found.");
7267
7268       (void) ThrowMagickException(exception,GetMagickModule(),
7269         CoderError,"No visible images in file","`%s'",image_info->filename);
7270
7271       if (image != (Image *) NULL)
7272         image=DestroyImageList(image);
7273
7274       MngInfoFreeStruct(mng_info,&have_mng_structure);
7275       return((Image *) NULL);
7276     }
7277
7278   if (mng_info->ticks_per_second)
7279     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7280             final_delay/mng_info->ticks_per_second;
7281
7282   else
7283     image->start_loop=MagickTrue;
7284
7285   /* Find final nonzero image delay */
7286   final_image_delay=0;
7287
7288   while (GetNextImageInList(image) != (Image *) NULL)
7289     {
7290       if (image->delay)
7291         final_image_delay=image->delay;
7292
7293       image=GetNextImageInList(image);
7294     }
7295
7296   if (final_delay < final_image_delay)
7297     final_delay=final_image_delay;
7298
7299   image->delay=final_delay;
7300
7301   if (logging != MagickFalse)
7302       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7303         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7304         (double) final_delay);
7305
7306   if (logging != MagickFalse)
7307     {
7308       int
7309         scene;
7310
7311       scene=0;
7312       image=GetFirstImageInList(image);
7313
7314       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7315         "  Before coalesce:");
7316
7317       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7318         "    scene 0 delay=%.20g",(double) image->delay);
7319
7320       while (GetNextImageInList(image) != (Image *) NULL)
7321       {
7322         image=GetNextImageInList(image);
7323         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7324           "    scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7325       }
7326     }
7327
7328   image=GetFirstImageInList(image);
7329 #ifdef MNG_COALESCE_LAYERS
7330   if (insert_layers)
7331     {
7332       Image
7333         *next_image,
7334         *next;
7335
7336       size_t
7337         scene;
7338
7339       if (logging != MagickFalse)
7340         (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Coalesce Images");
7341
7342       scene=image->scene;
7343       next_image=CoalesceImages(image,exception);
7344
7345       if (next_image == (Image *) NULL)
7346         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7347
7348       image=DestroyImageList(image);
7349       image=next_image;
7350
7351       for (next=image; next != (Image *) NULL; next=next_image)
7352       {
7353          next->page.width=mng_info->mng_width;
7354          next->page.height=mng_info->mng_height;
7355          next->page.x=0;
7356          next->page.y=0;
7357          next->scene=scene++;
7358          next_image=GetNextImageInList(next);
7359
7360          if (next_image == (Image *) NULL)
7361            break;
7362
7363          if (next->delay == 0)
7364            {
7365              scene--;
7366              next_image->previous=GetPreviousImageInList(next);
7367              if (GetPreviousImageInList(next) == (Image *) NULL)
7368                image=next_image;
7369              else
7370                next->previous->next=next_image;
7371              next=DestroyImage(next);
7372            }
7373       }
7374     }
7375 #endif
7376
7377   while (GetNextImageInList(image) != (Image *) NULL)
7378       image=GetNextImageInList(image);
7379
7380   image->dispose=BackgroundDispose;
7381
7382   if (logging != MagickFalse)
7383     {
7384       int
7385         scene;
7386
7387       scene=0;
7388       image=GetFirstImageInList(image);
7389
7390       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7391         "  After coalesce:");
7392
7393       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7394         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7395         (double) image->dispose);
7396
7397       while (GetNextImageInList(image) != (Image *) NULL)
7398       {
7399         image=GetNextImageInList(image);
7400
7401         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7402           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7403           (double) image->delay,(double) image->dispose);
7404       }
7405    }
7406
7407   image=GetFirstImageInList(image);
7408   MngInfoFreeStruct(mng_info,&have_mng_structure);
7409   have_mng_structure=MagickFalse;
7410
7411   if (logging != MagickFalse)
7412     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7413
7414   return(GetFirstImageInList(image));
7415 }
7416 #else /* PNG_LIBPNG_VER > 10011 */
7417 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7418 {
7419   printf("Your PNG library is too old: You have libpng-%s\n",
7420      PNG_LIBPNG_VER_STRING);
7421
7422   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7423     "PNG library is too old","`%s'",image_info->filename);
7424
7425   return(Image *) NULL;
7426 }
7427
7428 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7429 {
7430   return(ReadPNGImage(image_info,exception));
7431 }
7432 #endif /* PNG_LIBPNG_VER > 10011 */
7433 #endif
7434 \f
7435 /*
7436 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7437 %                                                                             %
7438 %                                                                             %
7439 %                                                                             %
7440 %   R e g i s t e r P N G I m a g e                                           %
7441 %                                                                             %
7442 %                                                                             %
7443 %                                                                             %
7444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7445 %
7446 %  RegisterPNGImage() adds properties for the PNG image format to
7447 %  the list of supported formats.  The properties include the image format
7448 %  tag, a method to read and/or write the format, whether the format
7449 %  supports the saving of more than one frame to the same file or blob,
7450 %  whether the format supports native in-memory I/O, and a brief
7451 %  description of the format.
7452 %
7453 %  The format of the RegisterPNGImage method is:
7454 %
7455 %      size_t RegisterPNGImage(void)
7456 %
7457 */
7458 ModuleExport size_t RegisterPNGImage(void)
7459 {
7460   char
7461     version[MaxTextExtent];
7462
7463   MagickInfo
7464     *entry;
7465
7466   static const char
7467     *PNGNote=
7468     {
7469       "See http://www.libpng.org/ for details about the PNG format."
7470     },
7471
7472     *JNGNote=
7473     {
7474       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7475       "format."
7476     },
7477
7478     *MNGNote=
7479     {
7480       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7481       "format."
7482     };
7483
7484   *version='\0';
7485
7486 #if defined(PNG_LIBPNG_VER_STRING)
7487   (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7488   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7489
7490   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7491     {
7492       (void) ConcatenateMagickString(version,",",MaxTextExtent);
7493       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7494             MaxTextExtent);
7495     }
7496 #endif
7497
7498   entry=SetMagickInfo("MNG");
7499   entry->seekable_stream=MagickTrue;  /* To do: eliminate this. */
7500
7501 #if defined(MAGICKCORE_PNG_DELEGATE)
7502   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7503   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7504 #endif
7505
7506   entry->magick=(IsImageFormatHandler *) IsMNG;
7507   entry->description=ConstantString("Multiple-image Network Graphics");
7508
7509   if (*version != '\0')
7510     entry->version=ConstantString(version);
7511
7512   entry->mime_type=ConstantString("video/x-mng");
7513   entry->module=ConstantString("PNG");
7514   entry->note=ConstantString(MNGNote);
7515   (void) RegisterMagickInfo(entry);
7516
7517   entry=SetMagickInfo("PNG");
7518
7519 #if defined(MAGICKCORE_PNG_DELEGATE)
7520   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7521   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7522 #endif
7523
7524   entry->magick=(IsImageFormatHandler *) IsPNG;
7525   entry->adjoin=MagickFalse;
7526   entry->description=ConstantString("Portable Network Graphics");
7527   entry->mime_type=ConstantString("image/png");
7528   entry->module=ConstantString("PNG");
7529
7530   if (*version != '\0')
7531     entry->version=ConstantString(version);
7532
7533   entry->note=ConstantString(PNGNote);
7534   (void) RegisterMagickInfo(entry);
7535
7536   entry=SetMagickInfo("PNG8");
7537
7538 #if defined(MAGICKCORE_PNG_DELEGATE)
7539   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7540   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7541 #endif
7542
7543   entry->magick=(IsImageFormatHandler *) IsPNG;
7544   entry->adjoin=MagickFalse;
7545   entry->description=ConstantString(
7546             "8-bit indexed with optional binary transparency");
7547   entry->mime_type=ConstantString("image/png");
7548   entry->module=ConstantString("PNG");
7549   (void) RegisterMagickInfo(entry);
7550
7551   entry=SetMagickInfo("PNG24");
7552   *version='\0';
7553
7554 #if defined(ZLIB_VERSION)
7555   (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7556   (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7557
7558   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7559     {
7560       (void) ConcatenateMagickString(version,",",MaxTextExtent);
7561       (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7562     }
7563 #endif
7564
7565   if (*version != '\0')
7566     entry->version=ConstantString(version);
7567
7568 #if defined(MAGICKCORE_PNG_DELEGATE)
7569   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7570   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7571 #endif
7572
7573   entry->magick=(IsImageFormatHandler *) IsPNG;
7574   entry->adjoin=MagickFalse;
7575   entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
7576   entry->mime_type=ConstantString("image/png");
7577   entry->module=ConstantString("PNG");
7578   (void) RegisterMagickInfo(entry);
7579
7580   entry=SetMagickInfo("PNG32");
7581
7582 #if defined(MAGICKCORE_PNG_DELEGATE)
7583   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7584   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7585 #endif
7586
7587   entry->magick=(IsImageFormatHandler *) IsPNG;
7588   entry->adjoin=MagickFalse;
7589   entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7590   entry->mime_type=ConstantString("image/png");
7591   entry->module=ConstantString("PNG");
7592   (void) RegisterMagickInfo(entry);
7593
7594   entry=SetMagickInfo("PNG48");
7595
7596 #if defined(MAGICKCORE_PNG_DELEGATE)
7597   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7598   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7599 #endif
7600
7601   entry->magick=(IsImageFormatHandler *) IsPNG;
7602   entry->adjoin=MagickFalse;
7603   entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7604   entry->mime_type=ConstantString("image/png");
7605   entry->module=ConstantString("PNG");
7606   (void) RegisterMagickInfo(entry);
7607
7608   entry=SetMagickInfo("PNG64");
7609
7610 #if defined(MAGICKCORE_PNG_DELEGATE)
7611   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7612   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7613 #endif
7614
7615   entry->magick=(IsImageFormatHandler *) IsPNG;
7616   entry->adjoin=MagickFalse;
7617   entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7618   entry->mime_type=ConstantString("image/png");
7619   entry->module=ConstantString("PNG");
7620   (void) RegisterMagickInfo(entry);
7621
7622   entry=SetMagickInfo("PNG00");
7623
7624 #if defined(MAGICKCORE_PNG_DELEGATE)
7625   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7626   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7627 #endif
7628
7629   entry->magick=(IsImageFormatHandler *) IsPNG;
7630   entry->adjoin=MagickFalse;
7631   entry->description=ConstantString(
7632     "PNG inheriting bit-depth and color-type from original");
7633   entry->mime_type=ConstantString("image/png");
7634   entry->module=ConstantString("PNG");
7635   (void) RegisterMagickInfo(entry);
7636
7637   entry=SetMagickInfo("JNG");
7638
7639 #if defined(JNG_SUPPORTED)
7640 #if defined(MAGICKCORE_PNG_DELEGATE)
7641   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7642   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7643 #endif
7644 #endif
7645
7646   entry->magick=(IsImageFormatHandler *) IsJNG;
7647   entry->adjoin=MagickFalse;
7648   entry->description=ConstantString("JPEG Network Graphics");
7649   entry->mime_type=ConstantString("image/x-jng");
7650   entry->module=ConstantString("PNG");
7651   entry->note=ConstantString(JNGNote);
7652   (void) RegisterMagickInfo(entry);
7653
7654 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7655   ping_semaphore=AcquireSemaphoreInfo();
7656 #endif
7657
7658   return(MagickImageCoderSignature);
7659 }
7660 \f
7661 /*
7662 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7663 %                                                                             %
7664 %                                                                             %
7665 %                                                                             %
7666 %   U n r e g i s t e r P N G I m a g e                                       %
7667 %                                                                             %
7668 %                                                                             %
7669 %                                                                             %
7670 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7671 %
7672 %  UnregisterPNGImage() removes format registrations made by the
7673 %  PNG module from the list of supported formats.
7674 %
7675 %  The format of the UnregisterPNGImage method is:
7676 %
7677 %      UnregisterPNGImage(void)
7678 %
7679 */
7680 ModuleExport void UnregisterPNGImage(void)
7681 {
7682   (void) UnregisterMagickInfo("MNG");
7683   (void) UnregisterMagickInfo("PNG");
7684   (void) UnregisterMagickInfo("PNG8");
7685   (void) UnregisterMagickInfo("PNG24");
7686   (void) UnregisterMagickInfo("PNG32");
7687   (void) UnregisterMagickInfo("PNG48");
7688   (void) UnregisterMagickInfo("PNG64");
7689   (void) UnregisterMagickInfo("PNG00");
7690   (void) UnregisterMagickInfo("JNG");
7691
7692 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7693   if (ping_semaphore != (SemaphoreInfo *) NULL)
7694     RelinquishSemaphoreInfo(&ping_semaphore);
7695 #endif
7696 }
7697 \f
7698 #if defined(MAGICKCORE_PNG_DELEGATE)
7699 #if PNG_LIBPNG_VER > 10011
7700 /*
7701 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7702 %                                                                             %
7703 %                                                                             %
7704 %                                                                             %
7705 %   W r i t e M N G I m a g e                                                 %
7706 %                                                                             %
7707 %                                                                             %
7708 %                                                                             %
7709 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7710 %
7711 %  WriteMNGImage() writes an image in the Portable Network Graphics
7712 %  Group's "Multiple-image Network Graphics" encoded image format.
7713 %
7714 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
7715 %
7716 %  The format of the WriteMNGImage method is:
7717 %
7718 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7719 %        Image *image,ExceptionInfo *exception)
7720 %
7721 %  A description of each parameter follows.
7722 %
7723 %    o image_info: the image info.
7724 %
7725 %    o image:  The image.
7726 %
7727 %    o exception: return any errors or warnings in this structure.
7728 %
7729 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7730 %    "To do" under ReadPNGImage):
7731 %
7732 %    Preserve all unknown and not-yet-handled known chunks found in input
7733 %    PNG file and copy them  into output PNG files according to the PNG
7734 %    copying rules.
7735 %
7736 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
7737 %
7738 %    Improve selection of color type (use indexed-colour or indexed-colour
7739 %    with tRNS when 256 or fewer unique RGBA values are present).
7740 %
7741 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7742 %    This will be complicated if we limit ourselves to generating MNG-LC
7743 %    files.  For now we ignore disposal method 3 and simply overlay the next
7744 %    image on it.
7745 %
7746 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
7747 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
7748 %    [mostly done 15 June 1999 but still need to take care of tRNS]
7749 %
7750 %    Check for identical sRGB and replace with a global sRGB (and remove
7751 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7752 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7753 %    local gAMA/cHRM with local sRGB if appropriate).
7754 %
7755 %    Check for identical sBIT chunks and write global ones.
7756 %
7757 %    Provide option to skip writing the signature tEXt chunks.
7758 %
7759 %    Use signatures to detect identical objects and reuse the first
7760 %    instance of such objects instead of writing duplicate objects.
7761 %
7762 %    Use a smaller-than-32k value of compression window size when
7763 %    appropriate.
7764 %
7765 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
7766 %    ancillary text chunks and save profiles.
7767 %
7768 %    Provide an option to force LC files (to ensure exact framing rate)
7769 %    instead of VLC.
7770 %
7771 %    Provide an option to force VLC files instead of LC, even when offsets
7772 %    are present.  This will involve expanding the embedded images with a
7773 %    transparent region at the top and/or left.
7774 */
7775
7776 static void
7777 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7778    png_info *ping_info, unsigned char *profile_type, unsigned char
7779    *profile_description, unsigned char *profile_data, png_uint_32 length)
7780 {
7781    png_textp
7782      text;
7783
7784    register ssize_t
7785      i;
7786
7787    unsigned char
7788      *sp;
7789
7790    png_charp
7791      dp;
7792
7793    png_uint_32
7794      allocated_length,
7795      description_length;
7796
7797    unsigned char
7798      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7799
7800    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7801       return;
7802
7803    if (image_info->verbose)
7804      {
7805        (void) printf("writing raw profile: type=%s, length=%.20g\n",
7806          (char *) profile_type, (double) length);
7807      }
7808
7809 #if PNG_LIBPNG_VER >= 10400
7810    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7811 #else
7812    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7813 #endif
7814    description_length=(png_uint_32) strlen((const char *) profile_description);
7815    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7816       + description_length);
7817 #if PNG_LIBPNG_VER >= 10400
7818    text[0].text=(png_charp) png_malloc(ping,
7819       (png_alloc_size_t) allocated_length);
7820    text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7821 #else
7822    text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7823    text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7824 #endif
7825    text[0].key[0]='\0';
7826    (void) ConcatenateMagickString(text[0].key,
7827       "Raw profile type ",MaxTextExtent);
7828    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7829    sp=profile_data;
7830    dp=text[0].text;
7831    *dp++='\n';
7832    (void) CopyMagickString(dp,(const char *) profile_description,
7833      allocated_length);
7834    dp+=description_length;
7835    *dp++='\n';
7836    (void) FormatLocaleString(dp,allocated_length-
7837      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7838    dp+=8;
7839
7840    for (i=0; i < (ssize_t) length; i++)
7841    {
7842      if (i%36 == 0)
7843        *dp++='\n';
7844      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7845      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7846    }
7847
7848    *dp++='\n';
7849    *dp='\0';
7850    text[0].text_length=(png_size_t) (dp-text[0].text);
7851    text[0].compression=image_info->compression == NoCompression ||
7852      (image_info->compression == UndefinedCompression &&
7853      text[0].text_length < 128) ? -1 : 0;
7854
7855    if (text[0].text_length <= allocated_length)
7856      png_set_text(ping,ping_info,text,1);
7857
7858    png_free(ping,text[0].text);
7859    png_free(ping,text[0].key);
7860    png_free(ping,text);
7861 }
7862
7863 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7864   const char *string, MagickBooleanType logging)
7865 {
7866   char
7867     *name;
7868
7869   const StringInfo
7870     *profile;
7871
7872   unsigned char
7873     *data;
7874
7875   png_uint_32 length;
7876
7877   ResetImageProfileIterator(image);
7878
7879   for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7880   {
7881     profile=GetImageProfile(image,name);
7882
7883     if (profile != (const StringInfo *) NULL)
7884       {
7885         StringInfo
7886           *ping_profile;
7887
7888         if (LocaleNCompare(name,string,11) == 0)
7889           {
7890             if (logging != MagickFalse)
7891                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7892                    "  Found %s profile",name);
7893
7894             ping_profile=CloneStringInfo(profile);
7895             data=GetStringInfoDatum(ping_profile),
7896             length=(png_uint_32) GetStringInfoLength(ping_profile);
7897             data[4]=data[3];
7898             data[3]=data[2];
7899             data[2]=data[1];
7900             data[1]=data[0];
7901             (void) WriteBlobMSBULong(image,length-5);  /* data length */
7902             (void) WriteBlob(image,length-1,data+1);
7903             (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7904             ping_profile=DestroyStringInfo(ping_profile);
7905           }
7906       }
7907
7908       name=GetNextImageProfile(image);
7909    }
7910
7911    return(MagickTrue);
7912 }
7913
7914
7915 /* Write one PNG image */
7916 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7917   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7918 {
7919   char
7920     im_vers[32],
7921     libpng_runv[32],
7922     libpng_vers[32],
7923     zlib_runv[32],
7924     zlib_vers[32];
7925
7926   Image
7927     *image;
7928
7929   ImageInfo
7930     *image_info;
7931
7932   char
7933     s[2];
7934
7935   const char
7936     *name,
7937     *property,
7938     *value;
7939
7940   const StringInfo
7941     *profile;
7942
7943   int
7944     num_passes,
7945     pass;
7946
7947   png_byte
7948      ping_trans_alpha[256];
7949
7950   png_color
7951      palette[257];
7952
7953   png_color_16
7954     ping_background,
7955     ping_trans_color;
7956
7957   png_info
7958     *ping_info;
7959
7960   png_struct
7961     *ping;
7962
7963   png_uint_32
7964     ping_height,
7965     ping_width;
7966
7967   ssize_t
7968     y;
7969
7970   MagickBooleanType
7971     image_matte,
7972     logging,
7973     matte,
7974
7975     ping_have_blob,
7976     ping_have_cheap_transparency,
7977     ping_have_color,
7978     ping_have_non_bw,
7979     ping_have_PLTE,
7980     ping_have_bKGD,
7981     ping_have_iCCP,
7982     ping_have_pHYs,
7983     ping_have_sRGB,
7984     ping_have_tRNS,
7985
7986     ping_exclude_bKGD,
7987     ping_exclude_cHRM,
7988     ping_exclude_date,
7989     /* ping_exclude_EXIF, */
7990     ping_exclude_gAMA,
7991     ping_exclude_iCCP,
7992     /* ping_exclude_iTXt, */
7993     ping_exclude_oFFs,
7994     ping_exclude_pHYs,
7995     ping_exclude_sRGB,
7996     ping_exclude_tEXt,
7997     /* ping_exclude_tRNS, */
7998     ping_exclude_vpAg,
7999     ping_exclude_zCCP, /* hex-encoded iCCP */
8000     ping_exclude_zTXt,
8001
8002     ping_preserve_colormap,
8003     ping_preserve_iCCP,
8004     ping_need_colortype_warning,
8005
8006     status,
8007     tried_332,
8008     tried_333,
8009     tried_444;
8010
8011   MemoryInfo
8012     *volatile pixel_info;
8013
8014   QuantumInfo
8015     *quantum_info;
8016
8017   PNGErrorInfo
8018     error_info;
8019
8020   register ssize_t
8021     i,
8022     x;
8023
8024   unsigned char
8025     *ping_pixels;
8026
8027   volatile int
8028     image_colors,
8029     ping_bit_depth,
8030     ping_color_type,
8031     ping_interlace_method,
8032     ping_compression_method,
8033     ping_filter_method,
8034     ping_num_trans;
8035
8036   volatile size_t
8037     image_depth,
8038     old_bit_depth;
8039
8040   size_t
8041     quality,
8042     rowbytes,
8043     save_image_depth;
8044
8045   int
8046     j,
8047     number_colors,
8048     number_opaque,
8049     number_semitransparent,
8050     number_transparent,
8051     ping_pHYs_unit_type;
8052
8053   png_uint_32
8054     ping_pHYs_x_resolution,
8055     ping_pHYs_y_resolution;
8056
8057   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8058     "  Enter WriteOnePNGImage()");
8059
8060   image = CloneImage(IMimage,0,0,MagickFalse,exception);
8061   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8062   if (image_info == (ImageInfo *) NULL)
8063      ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8064
8065   /* Define these outside of the following "if logging()" block so they will
8066    * show in debuggers.
8067    */
8068   *im_vers='\0';
8069   (void) ConcatenateMagickString(im_vers,
8070          MagickLibVersionText,MaxTextExtent);
8071   (void) ConcatenateMagickString(im_vers,
8072          MagickLibAddendum,MaxTextExtent);
8073
8074   *libpng_vers='\0';
8075   (void) ConcatenateMagickString(libpng_vers,
8076          PNG_LIBPNG_VER_STRING,32);
8077   *libpng_runv='\0';
8078   (void) ConcatenateMagickString(libpng_runv,
8079          png_get_libpng_ver(NULL),32);
8080
8081   *zlib_vers='\0';
8082   (void) ConcatenateMagickString(zlib_vers,
8083          ZLIB_VERSION,32);
8084   *zlib_runv='\0';
8085   (void) ConcatenateMagickString(zlib_runv,
8086          zlib_version,32);
8087
8088   if (logging)
8089     {
8090        LogMagickEvent(CoderEvent,GetMagickModule(),"    IM version     = %s",
8091            im_vers);
8092        LogMagickEvent(CoderEvent,GetMagickModule(),"    Libpng version = %s",
8093            libpng_vers);
8094        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8095        {
8096        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8097            libpng_runv);
8098        }
8099        LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
8100            zlib_vers);
8101        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8102        {
8103        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8104            zlib_runv);
8105        }
8106     }
8107
8108   /* Initialize some stuff */
8109   ping_bit_depth=0,
8110   ping_color_type=0,
8111   ping_interlace_method=0,
8112   ping_compression_method=0,
8113   ping_filter_method=0,
8114   ping_num_trans = 0;
8115
8116   ping_background.red = 0;
8117   ping_background.green = 0;
8118   ping_background.blue = 0;
8119   ping_background.gray = 0;
8120   ping_background.index = 0;
8121
8122   ping_trans_color.red=0;
8123   ping_trans_color.green=0;
8124   ping_trans_color.blue=0;
8125   ping_trans_color.gray=0;
8126
8127   ping_pHYs_unit_type = 0;
8128   ping_pHYs_x_resolution = 0;
8129   ping_pHYs_y_resolution = 0;
8130
8131   ping_have_blob=MagickFalse;
8132   ping_have_cheap_transparency=MagickFalse;
8133   ping_have_color=MagickTrue;
8134   ping_have_non_bw=MagickTrue;
8135   ping_have_PLTE=MagickFalse;
8136   ping_have_bKGD=MagickFalse;
8137   ping_have_iCCP=MagickFalse;
8138   ping_have_pHYs=MagickFalse;
8139   ping_have_sRGB=MagickFalse;
8140   ping_have_tRNS=MagickFalse;
8141
8142   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8143   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8144   ping_exclude_date=mng_info->ping_exclude_date;
8145   /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8146   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8147   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8148   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8149   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8150   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8151   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8152   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8153   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8154   ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8155   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8156   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8157
8158   ping_preserve_colormap = mng_info->ping_preserve_colormap;
8159   ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8160   ping_need_colortype_warning = MagickFalse;
8161
8162   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8163    * i.e., eliminate the ICC profile and set image->rendering_intent.
8164    * Note that this will not involve any changes to the actual pixels
8165    * but merely passes information to applications that read the resulting
8166    * PNG image.
8167    *
8168    * To do: recognize other variants of the sRGB profile, using the CRC to
8169    * verify all recognized variants including the 7 already known.
8170    *
8171    * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8172    *
8173    * Use something other than image->rendering_intent to record the fact
8174    * that the sRGB profile was found.
8175    *
8176    * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8177    * profile.  Record the Blackpoint Compensation, if any.
8178    */
8179    if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8180    {
8181       char
8182         *name;
8183
8184       const StringInfo
8185         *profile;
8186
8187       ResetImageProfileIterator(image);
8188       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8189       {
8190         profile=GetImageProfile(image,name);
8191
8192         if (profile != (StringInfo *) NULL)
8193           {
8194             if ((LocaleCompare(name,"ICC") == 0) ||
8195                 (LocaleCompare(name,"ICM") == 0))
8196
8197              {
8198                  int
8199                    icheck,
8200                    got_crc=0;
8201
8202
8203                  png_uint_32
8204                    length,
8205                    profile_crc=0;
8206
8207                  unsigned char
8208                    *data;
8209
8210                  length=(png_uint_32) GetStringInfoLength(profile);
8211
8212                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8213                  {
8214                    if (length == sRGB_info[icheck].len)
8215                    {
8216                      if (got_crc == 0)
8217                      {
8218                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8219                          "    Got a %lu-byte ICC profile (potentially sRGB)",
8220                          (unsigned long) length);
8221
8222                        data=GetStringInfoDatum(profile);
8223                        profile_crc=crc32(0,data,length);
8224
8225                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8226                            "      with crc=%8x",(unsigned int) profile_crc);
8227                        got_crc++;
8228                      }
8229
8230                      if (profile_crc == sRGB_info[icheck].crc)
8231                      {
8232                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8233                             "      It is sRGB with rendering intent = %s",
8234                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
8235                              sRGB_info[icheck].intent));
8236                         if (image->rendering_intent==UndefinedIntent)
8237                         {
8238                           image->rendering_intent=
8239                           Magick_RenderingIntent_from_PNG_RenderingIntent(
8240                              sRGB_info[icheck].intent);
8241                         }
8242                         ping_exclude_iCCP = MagickTrue;
8243                         ping_exclude_zCCP = MagickTrue;
8244                         ping_have_sRGB = MagickTrue;
8245                         break;
8246                      }
8247                    }
8248                  }
8249                  if (sRGB_info[icheck].len == 0)
8250                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8251                         "    Got a %lu-byte ICC profile not recognized as sRGB",
8252                         (unsigned long) length);
8253               }
8254           }
8255         name=GetNextImageProfile(image);
8256       }
8257   }
8258
8259   number_opaque = 0;
8260   number_semitransparent = 0;
8261   number_transparent = 0;
8262
8263   if (logging != MagickFalse)
8264     {
8265       if (image->storage_class == UndefinedClass)
8266           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8267           "    storage_class=UndefinedClass");
8268       if (image->storage_class == DirectClass)
8269           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8270           "    storage_class=DirectClass");
8271       if (image->storage_class == PseudoClass)
8272           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8273           "    storage_class=PseudoClass");
8274     }
8275
8276   if (image->storage_class == PseudoClass &&
8277      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8278      mng_info->write_png48 || mng_info->write_png64 ||
8279      (mng_info->write_png_colortype != 1 &&
8280      mng_info->write_png_colortype != 5)))
8281     {
8282       (void) SyncImage(image,exception);
8283       image->storage_class = DirectClass;
8284     }
8285
8286   if (ping_preserve_colormap == MagickFalse)
8287     {
8288       if (image->storage_class != PseudoClass && image->colormap != NULL)
8289         {
8290           /* Free the bogus colormap; it can cause trouble later */
8291            if (logging != MagickFalse)
8292               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8293               "    Freeing bogus colormap");
8294            (void) RelinquishMagickMemory(image->colormap);
8295            image->colormap=NULL;
8296         }
8297     }
8298
8299   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8300     (void) TransformImageColorspace(image,sRGBColorspace,exception);
8301
8302   /*
8303     Sometimes we get PseudoClass images whose RGB values don't match
8304     the colors in the colormap.  This code syncs the RGB values.
8305   */
8306   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8307      (void) SyncImage(image,exception);
8308
8309 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8310   if (image->depth > 8)
8311     {
8312       if (logging != MagickFalse)
8313         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8314           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8315
8316       image->depth=8;
8317     }
8318 #endif
8319
8320   /* Respect the -depth option */
8321   if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
8322     {
8323        register Quantum
8324          *r;
8325
8326        if (image->depth > 8)
8327          {
8328 #if MAGICKCORE_QUANTUM_DEPTH > 16
8329            /* Scale to 16-bit */
8330            LBR16PacketRGBO(image->background_color);
8331
8332            for (y=0; y < (ssize_t) image->rows; y++)
8333            {
8334              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8335
8336              if (r == (Quantum *) NULL)
8337                break;
8338
8339              for (x=0; x < (ssize_t) image->columns; x++)
8340              {
8341                 LBR16PixelRGBA(r);
8342                 r+=GetPixelChannels(image);
8343              }
8344
8345              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8346                 break;
8347            }
8348
8349            if (image->storage_class == PseudoClass && image->colormap != NULL)
8350            {
8351              for (i=0; i < (ssize_t) image->colors; i++)
8352              {
8353                LBR16PacketRGBO(image->colormap[i]);
8354              }
8355            }
8356 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
8357          }
8358
8359        else if (image->depth > 4)
8360          {
8361 #if MAGICKCORE_QUANTUM_DEPTH > 8
8362            /* Scale to 8-bit */
8363            LBR08PacketRGBO(image->background_color);
8364
8365            for (y=0; y < (ssize_t) image->rows; y++)
8366            {
8367              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8368
8369              if (r == (Quantum *) NULL)
8370                break;
8371
8372              for (x=0; x < (ssize_t) image->columns; x++)
8373              {
8374                 LBR08PixelRGBA(r);
8375                 r+=GetPixelChannels(image);
8376              }
8377
8378              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8379                 break;
8380            }
8381
8382            if (image->storage_class == PseudoClass && image->colormap != NULL)
8383            {
8384              for (i=0; i < (ssize_t) image->colors; i++)
8385              {
8386                LBR08PacketRGBO(image->colormap[i]);
8387              }
8388            }
8389 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
8390          }
8391        else
8392          if (image->depth > 2)
8393          {
8394            /* Scale to 4-bit */
8395            LBR04PacketRGBO(image->background_color);
8396
8397            for (y=0; y < (ssize_t) image->rows; y++)
8398            {
8399              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8400
8401              if (r == (Quantum *) NULL)
8402                break;
8403
8404              for (x=0; x < (ssize_t) image->columns; x++)
8405              {
8406                 LBR04PixelRGBA(r);
8407                 r+=GetPixelChannels(image);
8408              }
8409
8410              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8411                 break;
8412            }
8413
8414            if (image->storage_class == PseudoClass && image->colormap != NULL)
8415            {
8416              for (i=0; i < (ssize_t) image->colors; i++)
8417              {
8418                LBR04PacketRGBO(image->colormap[i]);
8419              }
8420            }
8421          }
8422
8423        else if (image->depth > 1)
8424          {
8425            /* Scale to 2-bit */
8426            LBR02PacketRGBO(image->background_color);
8427
8428            for (y=0; y < (ssize_t) image->rows; y++)
8429            {
8430              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8431
8432              if (r == (Quantum *) NULL)
8433                break;
8434
8435              for (x=0; x < (ssize_t) image->columns; x++)
8436              {
8437                 LBR02PixelRGBA(r);
8438                 r+=GetPixelChannels(image);
8439              }
8440
8441              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8442                 break;
8443            }
8444
8445            if (image->storage_class == PseudoClass && image->colormap != NULL)
8446            {
8447              for (i=0; i < (ssize_t) image->colors; i++)
8448              {
8449                LBR02PacketRGBO(image->colormap[i]);
8450              }
8451            }
8452          }
8453        else
8454          {
8455            /* Scale to 1-bit */
8456            LBR01PacketRGBO(image->background_color);
8457
8458            for (y=0; y < (ssize_t) image->rows; y++)
8459            {
8460              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8461
8462              if (r == (Quantum *) NULL)
8463                break;
8464
8465              for (x=0; x < (ssize_t) image->columns; x++)
8466              {
8467                 LBR01PixelRGBA(r);
8468                 r+=GetPixelChannels(image);
8469              }
8470
8471              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8472                 break;
8473            }
8474
8475            if (image->storage_class == PseudoClass && image->colormap != NULL)
8476            {
8477              for (i=0; i < (ssize_t) image->colors; i++)
8478              {
8479                LBR01PacketRGBO(image->colormap[i]);
8480              }
8481            }
8482          }
8483     }
8484
8485   /* To do: set to next higher multiple of 8 */
8486   if (image->depth < 8)
8487      image->depth=8;
8488
8489 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8490   /* PNG does not handle depths greater than 16 so reduce it even
8491    * if lossy
8492    */
8493   if (image->depth > 8)
8494       image->depth=16;
8495 #endif
8496
8497 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8498   if (image->depth > 8)
8499     {
8500       /* To do: fill low byte properly */
8501       image->depth=16;
8502     }
8503
8504   if (image->depth == 16 && mng_info->write_png_depth != 16)
8505     if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8506       image->depth = 8;
8507 #endif
8508
8509   image_colors = (int) image->colors;
8510   number_opaque = (int) image->colors;
8511   number_transparent = 0;
8512   number_semitransparent = 0;
8513
8514   if (mng_info->write_png_colortype &&
8515      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8516      mng_info->write_png_colortype < 4 &&
8517      image->alpha_trait != BlendPixelTrait)))
8518   {
8519      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8520       * are not going to need the result.
8521       */
8522      if (mng_info->write_png_colortype == 1 ||
8523         mng_info->write_png_colortype == 5)
8524        ping_have_color=MagickFalse;
8525
8526      if (image->alpha_trait == BlendPixelTrait)
8527        {
8528          number_transparent = 2;
8529          number_semitransparent = 1;
8530        }
8531   }
8532
8533   if (mng_info->write_png_colortype < 7)
8534   {
8535   /* BUILD_PALETTE
8536    *
8537    * Normally we run this just once, but in the case of writing PNG8
8538    * we reduce the transparency to binary and run again, then if there
8539    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8540    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8541    * palette.  Then (To do) we take care of a final reduction that is only
8542    * needed if there are still 256 colors present and one of them has both
8543    * transparent and opaque instances.
8544    */
8545
8546   tried_332 = MagickFalse;
8547   tried_333 = MagickFalse;
8548   tried_444 = MagickFalse;
8549
8550   for (j=0; j<6; j++)
8551   {
8552     /*
8553      * Sometimes we get DirectClass images that have 256 colors or fewer.
8554      * This code will build a colormap.
8555      *
8556      * Also, sometimes we get PseudoClass images with an out-of-date
8557      * colormap.  This code will replace the colormap with a new one.
8558      * Sometimes we get PseudoClass images that have more than 256 colors.
8559      * This code will delete the colormap and change the image to
8560      * DirectClass.
8561      *
8562      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8563      * even though it sometimes contains left-over non-opaque values.
8564      *
8565      * Also we gather some information (number of opaque, transparent,
8566      * and semitransparent pixels, and whether the image has any non-gray
8567      * pixels or only black-and-white pixels) that we might need later.
8568      *
8569      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8570      * we need to check for bogus non-opaque values, at least.
8571      */
8572
8573    int
8574      n;
8575
8576    PixelInfo
8577      opaque[260],
8578      semitransparent[260],
8579      transparent[260];
8580
8581    register const Quantum
8582      *s;
8583
8584    register Quantum
8585      *q,
8586      *r;
8587
8588    if (logging != MagickFalse)
8589      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8590          "    Enter BUILD_PALETTE:");
8591
8592    if (logging != MagickFalse)
8593      {
8594        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8595              "      image->columns=%.20g",(double) image->columns);
8596        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8597              "      image->rows=%.20g",(double) image->rows);
8598        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8599              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
8600        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8601              "      image->depth=%.20g",(double) image->depth);
8602
8603        if (image->storage_class == PseudoClass && image->colormap != NULL)
8604        {
8605          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8606              "      Original colormap:");
8607          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8608              "        i    (red,green,blue,alpha)");
8609
8610          for (i=0; i < 256; i++)
8611          {
8612                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8613                    "        %d    (%d,%d,%d,%d)",
8614                     (int) i,
8615                     (int) image->colormap[i].red,
8616                     (int) image->colormap[i].green,
8617                     (int) image->colormap[i].blue,
8618                     (int) image->colormap[i].alpha);
8619          }
8620
8621          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8622          {
8623            if (i > 255)
8624              {
8625                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8626                    "        %d    (%d,%d,%d,%d)",
8627                     (int) i,
8628                     (int) image->colormap[i].red,
8629                     (int) image->colormap[i].green,
8630                     (int) image->colormap[i].blue,
8631                     (int) image->colormap[i].alpha);
8632              }
8633          }
8634        }
8635
8636        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8637            "      image->colors=%d",(int) image->colors);
8638
8639        if (image->colors == 0)
8640          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8641              "        (zero means unknown)");
8642
8643        if (ping_preserve_colormap == MagickFalse)
8644          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8645               "      Regenerate the colormap");
8646      }
8647
8648      image_colors=0;
8649      number_opaque = 0;
8650      number_semitransparent = 0;
8651      number_transparent = 0;
8652
8653      for (y=0; y < (ssize_t) image->rows; y++)
8654      {
8655        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8656
8657        if (q == (Quantum *) NULL)
8658          break;
8659
8660        for (x=0; x < (ssize_t) image->columns; x++)
8661        {
8662            if (image->alpha_trait != BlendPixelTrait ||
8663               GetPixelAlpha(image,q) == OpaqueAlpha)
8664              {
8665                if (number_opaque < 259)
8666                  {
8667                    if (number_opaque == 0)
8668                      {
8669                        GetPixelInfoPixel(image, q, opaque);
8670                        opaque[0].alpha=OpaqueAlpha;
8671                        number_opaque=1;
8672                      }
8673
8674                    for (i=0; i< (ssize_t) number_opaque; i++)
8675                      {
8676                        if (IsPixelEquivalent(image,q, opaque+i))
8677                          break;
8678                      }
8679
8680                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
8681                      {
8682                        number_opaque++;
8683                        GetPixelInfoPixel(image, q, opaque+i);
8684                        opaque[i].alpha=OpaqueAlpha;
8685                      }
8686                  }
8687              }
8688            else if (GetPixelAlpha(image,q) == TransparentAlpha)
8689              {
8690                if (number_transparent < 259)
8691                  {
8692                    if (number_transparent == 0)
8693                      {
8694                        GetPixelInfoPixel(image, q, transparent);
8695                        ping_trans_color.red=(unsigned short)
8696                          GetPixelRed(image,q);
8697                        ping_trans_color.green=(unsigned short)
8698                          GetPixelGreen(image,q);
8699                        ping_trans_color.blue=(unsigned short)
8700                          GetPixelBlue(image,q);
8701                        ping_trans_color.gray=(unsigned short)
8702                          GetPixelGray(image,q);
8703                        number_transparent = 1;
8704                      }
8705
8706                    for (i=0; i< (ssize_t) number_transparent; i++)
8707                      {
8708                        if (IsPixelEquivalent(image,q, transparent+i))
8709                          break;
8710                      }
8711
8712                    if (i ==  (ssize_t) number_transparent &&
8713                        number_transparent < 259)
8714                      {
8715                        number_transparent++;
8716                        GetPixelInfoPixel(image,q,transparent+i);
8717                      }
8718                  }
8719              }
8720            else
8721              {
8722                if (number_semitransparent < 259)
8723                  {
8724                    if (number_semitransparent == 0)
8725                      {
8726                        GetPixelInfoPixel(image,q,semitransparent);
8727                        number_semitransparent = 1;
8728                      }
8729
8730                    for (i=0; i< (ssize_t) number_semitransparent; i++)
8731                      {
8732                        if (IsPixelEquivalent(image,q, semitransparent+i)
8733                            && GetPixelAlpha(image,q) ==
8734                            semitransparent[i].alpha)
8735                          break;
8736                      }
8737
8738                    if (i ==  (ssize_t) number_semitransparent &&
8739                        number_semitransparent < 259)
8740                      {
8741                        number_semitransparent++;
8742                        GetPixelInfoPixel(image, q, semitransparent+i);
8743                      }
8744                  }
8745              }
8746            q+=GetPixelChannels(image);
8747         }
8748      }
8749
8750      if (mng_info->write_png8 == MagickFalse &&
8751          ping_exclude_bKGD == MagickFalse)
8752        {
8753          /* Add the background color to the palette, if it
8754           * isn't already there.
8755           */
8756           if (logging != MagickFalse)
8757             {
8758               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8759                   "      Check colormap for background (%d,%d,%d)",
8760                   (int) image->background_color.red,
8761                   (int) image->background_color.green,
8762                   (int) image->background_color.blue);
8763             }
8764           for (i=0; i<number_opaque; i++)
8765           {
8766              if (opaque[i].red == image->background_color.red &&
8767                  opaque[i].green == image->background_color.green &&
8768                  opaque[i].blue == image->background_color.blue)
8769                break;
8770           }
8771           if (number_opaque < 259 && i == number_opaque)
8772             {
8773                opaque[i] = image->background_color;
8774                ping_background.index = i;
8775                number_opaque++;
8776                if (logging != MagickFalse)
8777                  {
8778                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8779                        "      background_color index is %d",(int) i);
8780                  }
8781
8782             }
8783           else if (logging != MagickFalse)
8784               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8785                   "      No room in the colormap to add background color");
8786        }
8787
8788      image_colors=number_opaque+number_transparent+number_semitransparent;
8789
8790      if (logging != MagickFalse)
8791        {
8792          if (image_colors > 256)
8793             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8794                   "      image has more than 256 colors");
8795
8796          else
8797             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8798                   "      image has %d colors",image_colors);
8799        }
8800
8801      if (ping_preserve_colormap != MagickFalse)
8802        break;
8803
8804      if (mng_info->write_png_colortype != 7) /* We won't need this info */
8805        {
8806          ping_have_color=MagickFalse;
8807          ping_have_non_bw=MagickFalse;
8808
8809          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8810          {
8811            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8812               "incompatible colorspace");
8813            ping_have_color=MagickTrue;
8814            ping_have_non_bw=MagickTrue;
8815          }
8816
8817          if(image_colors > 256)
8818            {
8819              for (y=0; y < (ssize_t) image->rows; y++)
8820              {
8821                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8822
8823                if (q == (Quantum *) NULL)
8824                  break;
8825
8826                s=q;
8827                for (x=0; x < (ssize_t) image->columns; x++)
8828                {
8829                  if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8830                      GetPixelRed(image,s) != GetPixelBlue(image,s))
8831                    {
8832                       ping_have_color=MagickTrue;
8833                       ping_have_non_bw=MagickTrue;
8834                       break;
8835                    }
8836                  s+=GetPixelChannels(image);
8837                }
8838
8839                if (ping_have_color != MagickFalse)
8840                  break;
8841
8842                /* Worst case is black-and-white; we are looking at every
8843                 * pixel twice.
8844                 */
8845
8846                if (ping_have_non_bw == MagickFalse)
8847                  {
8848                    s=q;
8849                    for (x=0; x < (ssize_t) image->columns; x++)
8850                    {
8851                      if (GetPixelRed(image,s) != 0 &&
8852                          GetPixelRed(image,s) != QuantumRange)
8853                        {
8854                          ping_have_non_bw=MagickTrue;
8855                          break;
8856                        }
8857                      s+=GetPixelChannels(image);
8858                    }
8859                }
8860              }
8861            }
8862        }
8863
8864      if (image_colors < 257)
8865        {
8866          PixelInfo
8867            colormap[260];
8868
8869          /*
8870           * Initialize image colormap.
8871           */
8872
8873          if (logging != MagickFalse)
8874             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8875                   "      Sort the new colormap");
8876
8877         /* Sort palette, transparent first */;
8878
8879          n = 0;
8880
8881          for (i=0; i<number_transparent; i++)
8882             colormap[n++] = transparent[i];
8883
8884          for (i=0; i<number_semitransparent; i++)
8885             colormap[n++] = semitransparent[i];
8886
8887          for (i=0; i<number_opaque; i++)
8888             colormap[n++] = opaque[i];
8889
8890          ping_background.index +=
8891            (number_transparent + number_semitransparent);
8892
8893          /* image_colors < 257; search the colormap instead of the pixels
8894           * to get ping_have_color and ping_have_non_bw
8895           */
8896          for (i=0; i<n; i++)
8897          {
8898            if (ping_have_color == MagickFalse)
8899              {
8900                 if (colormap[i].red != colormap[i].green ||
8901                     colormap[i].red != colormap[i].blue)
8902                   {
8903                      ping_have_color=MagickTrue;
8904                      ping_have_non_bw=MagickTrue;
8905                      break;
8906                   }
8907               }
8908
8909            if (ping_have_non_bw == MagickFalse)
8910              {
8911                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8912                    ping_have_non_bw=MagickTrue;
8913              }
8914           }
8915
8916         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8917             (number_transparent == 0 && number_semitransparent == 0)) &&
8918             (((mng_info->write_png_colortype-1) ==
8919             PNG_COLOR_TYPE_PALETTE) ||
8920             (mng_info->write_png_colortype == 0)))
8921           {
8922             if (logging != MagickFalse)
8923               {
8924                 if (n !=  (ssize_t) image_colors)
8925                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8926                    "   image_colors (%d) and n (%d)  don't match",
8927                    image_colors, n);
8928
8929                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8930                    "      AcquireImageColormap");
8931               }
8932
8933             image->colors = image_colors;
8934
8935             if (AcquireImageColormap(image,image_colors,exception) ==
8936                 MagickFalse)
8937                ThrowWriterException(ResourceLimitError,
8938                    "MemoryAllocationFailed");
8939
8940             for (i=0; i< (ssize_t) image_colors; i++)
8941                image->colormap[i] = colormap[i];
8942
8943             if (logging != MagickFalse)
8944               {
8945                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8946                       "      image->colors=%d (%d)",
8947                       (int) image->colors, image_colors);
8948
8949                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8950                       "      Update the pixel indexes");
8951               }
8952
8953             /* Sync the pixel indices with the new colormap */
8954
8955             for (y=0; y < (ssize_t) image->rows; y++)
8956             {
8957               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8958
8959               if (q == (Quantum *) NULL)
8960                 break;
8961
8962               for (x=0; x < (ssize_t) image->columns; x++)
8963               {
8964                 for (i=0; i< (ssize_t) image_colors; i++)
8965                 {
8966                   if ((image->alpha_trait != BlendPixelTrait ||
8967                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8968                       image->colormap[i].red == GetPixelRed(image,q) &&
8969                       image->colormap[i].green == GetPixelGreen(image,q) &&
8970                       image->colormap[i].blue == GetPixelBlue(image,q))
8971                   {
8972                     SetPixelIndex(image,i,q);
8973                     break;
8974                   }
8975                 }
8976                 q+=GetPixelChannels(image);
8977               }
8978
8979               if (SyncAuthenticPixels(image,exception) == MagickFalse)
8980                  break;
8981             }
8982           }
8983        }
8984
8985      if (logging != MagickFalse)
8986        {
8987          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8988             "      image->colors=%d", (int) image->colors);
8989
8990          if (image->colormap != NULL)
8991            {
8992              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8993                  "       i     (red,green,blue,alpha)");
8994
8995              for (i=0; i < (ssize_t) image->colors; i++)
8996              {
8997                if (i < 300 || i >= (ssize_t) image->colors - 10)
8998                  {
8999                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9000                        "       %d     (%d,%d,%d,%d)",
9001                         (int) i,
9002                         (int) image->colormap[i].red,
9003                         (int) image->colormap[i].green,
9004                         (int) image->colormap[i].blue,
9005                         (int) image->colormap[i].alpha);
9006                  }
9007              }
9008            }
9009
9010            if (number_transparent < 257)
9011              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9012                    "      number_transparent     = %d",
9013                    number_transparent);
9014            else
9015
9016              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9017                    "      number_transparent     > 256");
9018
9019            if (number_opaque < 257)
9020              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9021                    "      number_opaque          = %d",
9022                    number_opaque);
9023
9024            else
9025              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9026                    "      number_opaque          > 256");
9027
9028            if (number_semitransparent < 257)
9029              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9030                    "      number_semitransparent = %d",
9031                    number_semitransparent);
9032
9033            else
9034              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9035                    "      number_semitransparent > 256");
9036
9037            if (ping_have_non_bw == MagickFalse)
9038               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9039                     "      All pixels and the background are black or white");
9040
9041            else if (ping_have_color == MagickFalse)
9042               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9043                     "      All pixels and the background are gray");
9044
9045            else
9046               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9047                     "      At least one pixel or the background is non-gray");
9048
9049            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9050                "    Exit BUILD_PALETTE:");
9051        }
9052
9053    if (mng_info->write_png8 == MagickFalse)
9054       break;
9055
9056    /* Make any reductions necessary for the PNG8 format */
9057     if (image_colors <= 256 &&
9058         image_colors != 0 && image->colormap != NULL &&
9059         number_semitransparent == 0 &&
9060         number_transparent <= 1)
9061       break;
9062
9063     /* PNG8 can't have semitransparent colors so we threshold the
9064      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9065      * transparent color so if more than one is transparent we merge
9066      * them into image->background_color.
9067      */
9068     if (number_semitransparent != 0 || number_transparent > 1)
9069       {
9070         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9071             "    Thresholding the alpha channel to binary");
9072
9073         for (y=0; y < (ssize_t) image->rows; y++)
9074         {
9075           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9076
9077           if (r == (Quantum *) NULL)
9078             break;
9079
9080           for (x=0; x < (ssize_t) image->columns; x++)
9081           {
9082               if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9083                 {
9084                   SetPixelInfoPixel(image,&image->background_color,r);
9085                   SetPixelAlpha(image,TransparentAlpha,r);
9086                 }
9087               else
9088                   SetPixelAlpha(image,OpaqueAlpha,r);
9089               r+=GetPixelChannels(image);
9090           }
9091
9092           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9093              break;
9094
9095           if (image_colors != 0 && image_colors <= 256 &&
9096              image->colormap != NULL)
9097             for (i=0; i<image_colors; i++)
9098                 image->colormap[i].alpha =
9099                     (image->colormap[i].alpha > TransparentAlpha/2 ?
9100                     TransparentAlpha : OpaqueAlpha);
9101         }
9102       continue;
9103     }
9104
9105     /* PNG8 can't have more than 256 colors so we quantize the pixels and
9106      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
9107      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9108      * colors or less.
9109      */
9110     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9111       {
9112         if (logging != MagickFalse)
9113            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9114                "    Quantizing the background color to 4-4-4");
9115
9116         tried_444 = MagickTrue;
9117
9118         LBR04PacketRGB(image->background_color);
9119
9120         if (logging != MagickFalse)
9121           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9122               "    Quantizing the pixel colors to 4-4-4");
9123
9124         if (image->colormap == NULL)
9125         {
9126           for (y=0; y < (ssize_t) image->rows; y++)
9127           {
9128             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9129
9130             if (r == (Quantum *) NULL)
9131               break;
9132
9133             for (x=0; x < (ssize_t) image->columns; x++)
9134             {
9135               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9136                   LBR04PixelRGB(r);
9137               r+=GetPixelChannels(image);
9138             }
9139
9140             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9141                break;
9142           }
9143         }
9144
9145         else /* Should not reach this; colormap already exists and
9146                 must be <= 256 */
9147         {
9148           if (logging != MagickFalse)
9149               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9150               "    Quantizing the colormap to 4-4-4");
9151
9152           for (i=0; i<image_colors; i++)
9153           {
9154             LBR04PacketRGB(image->colormap[i]);
9155           }
9156         }
9157         continue;
9158       }
9159
9160     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9161       {
9162         if (logging != MagickFalse)
9163            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9164                "    Quantizing the background color to 3-3-3");
9165
9166         tried_333 = MagickTrue;
9167
9168         LBR03PacketRGB(image->background_color);
9169
9170         if (logging != MagickFalse)
9171           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9172               "    Quantizing the pixel colors to 3-3-3-1");
9173
9174         if (image->colormap == NULL)
9175         {
9176           for (y=0; y < (ssize_t) image->rows; y++)
9177           {
9178             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9179
9180             if (r == (Quantum *) NULL)
9181               break;
9182
9183             for (x=0; x < (ssize_t) image->columns; x++)
9184             {
9185               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9186                   LBR03RGB(r);
9187               r+=GetPixelChannels(image);
9188             }
9189
9190             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9191                break;
9192           }
9193         }
9194
9195         else /* Should not reach this; colormap already exists and
9196                 must be <= 256 */
9197         {
9198           if (logging != MagickFalse)
9199               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9200               "    Quantizing the colormap to 3-3-3-1");
9201           for (i=0; i<image_colors; i++)
9202           {
9203               LBR03PacketRGB(image->colormap[i]);
9204           }
9205         }
9206         continue;
9207       }
9208
9209     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9210       {
9211         if (logging != MagickFalse)
9212            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9213                "    Quantizing the background color to 3-3-2");
9214
9215         tried_332 = MagickTrue;
9216
9217         /* Red and green were already done so we only quantize the blue
9218          * channel
9219          */
9220
9221         LBR02PacketBlue(image->background_color);
9222
9223         if (logging != MagickFalse)
9224           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9225               "    Quantizing the pixel colors to 3-3-2-1");
9226
9227         if (image->colormap == NULL)
9228         {
9229           for (y=0; y < (ssize_t) image->rows; y++)
9230           {
9231             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9232
9233             if (r == (Quantum *) NULL)
9234               break;
9235
9236             for (x=0; x < (ssize_t) image->columns; x++)
9237             {
9238               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9239                   LBR02PixelBlue(r);
9240               r+=GetPixelChannels(image);
9241             }
9242
9243             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9244                break;
9245           }
9246         }
9247
9248         else /* Should not reach this; colormap already exists and
9249                 must be <= 256 */
9250         {
9251           if (logging != MagickFalse)
9252               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9253               "    Quantizing the colormap to 3-3-2-1");
9254           for (i=0; i<image_colors; i++)
9255           {
9256               LBR02PacketBlue(image->colormap[i]);
9257           }
9258       }
9259       continue;
9260     }
9261
9262     if (image_colors == 0 || image_colors > 256)
9263     {
9264       /* Take care of special case with 256 opaque colors + 1 transparent
9265        * color.  We don't need to quantize to 2-3-2-1; we only need to
9266        * eliminate one color, so we'll merge the two darkest red
9267        * colors (0x49, 0, 0) -> (0x24, 0, 0).
9268        */
9269       if (logging != MagickFalse)
9270         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9271             "    Merging two dark red background colors to 3-3-2-1");
9272
9273       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9274           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9275           ScaleQuantumToChar(image->background_color.blue) == 0x00)
9276       {
9277          image->background_color.red=ScaleCharToQuantum(0x24);
9278       }
9279
9280       if (logging != MagickFalse)
9281         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9282             "    Merging two dark red pixel colors to 3-3-2-1");
9283
9284       if (image->colormap == NULL)
9285       {
9286         for (y=0; y < (ssize_t) image->rows; y++)
9287         {
9288           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9289
9290           if (r == (Quantum *) NULL)
9291             break;
9292
9293           for (x=0; x < (ssize_t) image->columns; x++)
9294           {
9295             if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9296                 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9297                 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9298                 GetPixelAlpha(image,r) == OpaqueAlpha)
9299               {
9300                 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9301               }
9302             r+=GetPixelChannels(image);
9303           }
9304
9305           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9306              break;
9307
9308         }
9309       }
9310
9311       else
9312       {
9313          for (i=0; i<image_colors; i++)
9314          {
9315             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9316                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9317                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9318             {
9319                image->colormap[i].red=ScaleCharToQuantum(0x24);
9320             }
9321          }
9322       }
9323     }
9324   }
9325   }
9326   /* END OF BUILD_PALETTE */
9327
9328   /* If we are excluding the tRNS chunk and there is transparency,
9329    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9330    * PNG.
9331    */
9332   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9333      (number_transparent != 0 || number_semitransparent != 0))
9334     {
9335       unsigned int colortype=mng_info->write_png_colortype;
9336
9337       if (ping_have_color == MagickFalse)
9338         mng_info->write_png_colortype = 5;
9339
9340       else
9341         mng_info->write_png_colortype = 7;
9342
9343       if (colortype != 0 &&
9344          mng_info->write_png_colortype != colortype)
9345         ping_need_colortype_warning=MagickTrue;
9346
9347     }
9348
9349   /* See if cheap transparency is possible.  It is only possible
9350    * when there is a single transparent color, no semitransparent
9351    * color, and no opaque color that has the same RGB components
9352    * as the transparent color.  We only need this information if
9353    * we are writing a PNG with colortype 0 or 2, and we have not
9354    * excluded the tRNS chunk.
9355    */
9356   if (number_transparent == 1 &&
9357       mng_info->write_png_colortype < 4)
9358     {
9359        ping_have_cheap_transparency = MagickTrue;
9360
9361        if (number_semitransparent != 0)
9362          ping_have_cheap_transparency = MagickFalse;
9363
9364        else if (image_colors == 0 || image_colors > 256 ||
9365            image->colormap == NULL)
9366          {
9367            register const Quantum
9368              *q;
9369
9370            for (y=0; y < (ssize_t) image->rows; y++)
9371            {
9372              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9373
9374              if (q == (Quantum *) NULL)
9375                break;
9376
9377              for (x=0; x < (ssize_t) image->columns; x++)
9378              {
9379                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9380                      (unsigned short) GetPixelRed(image,q) ==
9381                                      ping_trans_color.red &&
9382                      (unsigned short) GetPixelGreen(image,q) ==
9383                                      ping_trans_color.green &&
9384                      (unsigned short) GetPixelBlue(image,q) ==
9385                                      ping_trans_color.blue)
9386                    {
9387                      ping_have_cheap_transparency = MagickFalse;
9388                      break;
9389                    }
9390
9391                  q+=GetPixelChannels(image);
9392              }
9393
9394              if (ping_have_cheap_transparency == MagickFalse)
9395                 break;
9396            }
9397          }
9398        else
9399          {
9400             /* Assuming that image->colormap[0] is the one transparent color
9401              * and that all others are opaque.
9402              */
9403             if (image_colors > 1)
9404               for (i=1; i<image_colors; i++)
9405                 if (image->colormap[i].red == image->colormap[0].red &&
9406                     image->colormap[i].green == image->colormap[0].green &&
9407                     image->colormap[i].blue == image->colormap[0].blue)
9408                   {
9409                      ping_have_cheap_transparency = MagickFalse;
9410                      break;
9411                   }
9412          }
9413
9414        if (logging != MagickFalse)
9415          {
9416            if (ping_have_cheap_transparency == MagickFalse)
9417              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9418                  "   Cheap transparency is not possible.");
9419
9420            else
9421              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9422                  "   Cheap transparency is possible.");
9423          }
9424      }
9425   else
9426     ping_have_cheap_transparency = MagickFalse;
9427
9428   image_depth=image->depth;
9429
9430   quantum_info = (QuantumInfo *) NULL;
9431   number_colors=0;
9432   image_colors=(int) image->colors;
9433   image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
9434
9435   if (mng_info->write_png_colortype < 5)
9436     mng_info->IsPalette=image->storage_class == PseudoClass &&
9437       image_colors <= 256 && image->colormap != NULL;
9438   else
9439     mng_info->IsPalette = MagickFalse;
9440
9441   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9442      (image->colors == 0 || image->colormap == NULL))
9443     {
9444       image_info=DestroyImageInfo(image_info);
9445       image=DestroyImage(image);
9446       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9447           "Cannot write PNG8 or color-type 3; colormap is NULL",
9448           "`%s'",IMimage->filename);
9449       return(MagickFalse);
9450     }
9451
9452   /*
9453     Allocate the PNG structures
9454   */
9455 #ifdef PNG_USER_MEM_SUPPORTED
9456  error_info.image=image;
9457  error_info.exception=exception;
9458   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9459     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9460     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9461
9462 #else
9463   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9464     MagickPNGErrorHandler,MagickPNGWarningHandler);
9465
9466 #endif
9467   if (ping == (png_struct *) NULL)
9468     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9469
9470   ping_info=png_create_info_struct(ping);
9471
9472   if (ping_info == (png_info *) NULL)
9473     {
9474       png_destroy_write_struct(&ping,(png_info **) NULL);
9475       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9476     }
9477
9478   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9479   pixel_info=(MemoryInfo *) NULL;
9480
9481   if (setjmp(png_jmpbuf(ping)))
9482     {
9483       /*
9484         PNG write failed.
9485       */
9486 #ifdef PNG_DEBUG
9487      if (image_info->verbose)
9488         (void) printf("PNG write has failed.\n");
9489 #endif
9490       png_destroy_write_struct(&ping,&ping_info);
9491 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9492       UnlockSemaphoreInfo(ping_semaphore);
9493 #endif
9494
9495       if (pixel_info != (MemoryInfo *) NULL)
9496         pixel_info=RelinquishVirtualMemory(pixel_info);
9497
9498       if (quantum_info != (QuantumInfo *) NULL)
9499         quantum_info=DestroyQuantumInfo(quantum_info);
9500
9501       if (ping_have_blob != MagickFalse)
9502           (void) CloseBlob(image);
9503       image_info=DestroyImageInfo(image_info);
9504       image=DestroyImage(image);
9505       return(MagickFalse);
9506     }
9507
9508   /* {  For navigation to end of SETJMP-protected block.  Within this
9509    *    block, use png_error() instead of Throwing an Exception, to ensure
9510    *    that libpng is able to clean up, and that the semaphore is unlocked.
9511    */
9512
9513 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9514   LockSemaphoreInfo(ping_semaphore);
9515 #endif
9516
9517 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9518   /* Allow benign errors */
9519   png_set_benign_errors(ping, 1);
9520 #endif
9521
9522   /*
9523     Prepare PNG for writing.
9524   */
9525
9526 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9527   if (mng_info->write_mng)
9528   {
9529      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9530 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9531      /* Disable new libpng-1.5.10 feature when writing a MNG because
9532       * zero-length PLTE is OK
9533       */
9534      png_set_check_for_invalid_index (ping, 0);
9535 # endif
9536   }
9537
9538 #else
9539 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9540   if (mng_info->write_mng)
9541      png_permit_empty_plte(ping,MagickTrue);
9542
9543 # endif
9544 #endif
9545
9546   x=0;
9547
9548   ping_width=(png_uint_32) image->columns;
9549   ping_height=(png_uint_32) image->rows;
9550
9551   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9552      image_depth=8;
9553
9554   if (mng_info->write_png48 || mng_info->write_png64)
9555      image_depth=16;
9556
9557   if (mng_info->write_png_depth != 0)
9558      image_depth=mng_info->write_png_depth;
9559
9560   /* Adjust requested depth to next higher valid depth if necessary */
9561   if (image_depth > 8)
9562      image_depth=16;
9563
9564   if ((image_depth > 4) && (image_depth < 8))
9565      image_depth=8;
9566
9567   if (image_depth == 3)
9568      image_depth=4;
9569
9570   if (logging != MagickFalse)
9571     {
9572      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9573         "    width=%.20g",(double) ping_width);
9574      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9575         "    height=%.20g",(double) ping_height);
9576      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9577         "    image_matte=%.20g",(double) image->alpha_trait);
9578      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9579         "    image->depth=%.20g",(double) image->depth);
9580      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9581         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
9582     }
9583
9584   save_image_depth=image_depth;
9585   ping_bit_depth=(png_byte) save_image_depth;
9586
9587
9588 #if defined(PNG_pHYs_SUPPORTED)
9589   if (ping_exclude_pHYs == MagickFalse)
9590   {
9591   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9592       (!mng_info->write_mng || !mng_info->equal_physs))
9593     {
9594       if (logging != MagickFalse)
9595         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9596             "    Setting up pHYs chunk");
9597
9598       if (image->units == PixelsPerInchResolution)
9599         {
9600           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9601           ping_pHYs_x_resolution=
9602              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9603           ping_pHYs_y_resolution=
9604              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9605         }
9606
9607       else if (image->units == PixelsPerCentimeterResolution)
9608         {
9609           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9610           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9611           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9612         }
9613
9614       else
9615         {
9616           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9617           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9618           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9619         }
9620
9621       if (logging != MagickFalse)
9622         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9623           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9624           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9625           (int) ping_pHYs_unit_type);
9626        ping_have_pHYs = MagickTrue;
9627     }
9628   }
9629 #endif
9630
9631   if (ping_exclude_bKGD == MagickFalse)
9632   {
9633   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9634     {
9635        unsigned int
9636          mask;
9637
9638        mask=0xffff;
9639        if (ping_bit_depth == 8)
9640           mask=0x00ff;
9641
9642        if (ping_bit_depth == 4)
9643           mask=0x000f;
9644
9645        if (ping_bit_depth == 2)
9646           mask=0x0003;
9647
9648        if (ping_bit_depth == 1)
9649           mask=0x0001;
9650
9651        ping_background.red=(png_uint_16)
9652          (ScaleQuantumToShort(image->background_color.red) & mask);
9653
9654        ping_background.green=(png_uint_16)
9655          (ScaleQuantumToShort(image->background_color.green) & mask);
9656
9657        ping_background.blue=(png_uint_16)
9658          (ScaleQuantumToShort(image->background_color.blue) & mask);
9659
9660        ping_background.gray=(png_uint_16) ping_background.green;
9661     }
9662
9663   if (logging != MagickFalse)
9664     {
9665       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9666           "    Setting up bKGD chunk (1)");
9667       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9668           "      background_color index is %d",
9669           (int) ping_background.index);
9670
9671       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9672           "    ping_bit_depth=%d",ping_bit_depth);
9673     }
9674
9675   ping_have_bKGD = MagickTrue;
9676   }
9677
9678   /*
9679     Select the color type.
9680   */
9681   matte=image_matte;
9682   old_bit_depth=0;
9683
9684   if (mng_info->IsPalette && mng_info->write_png8)
9685     {
9686       /* To do: make this a function cause it's used twice, except
9687          for reducing the sample depth from 8. */
9688
9689       number_colors=image_colors;
9690
9691       ping_have_tRNS=MagickFalse;
9692
9693       /*
9694         Set image palette.
9695       */
9696       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9697
9698       if (logging != MagickFalse)
9699         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9700             "  Setting up PLTE chunk with %d colors (%d)",
9701             number_colors, image_colors);
9702
9703       for (i=0; i < (ssize_t) number_colors; i++)
9704       {
9705         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9706         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9707         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9708         if (logging != MagickFalse)
9709           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9710 #if MAGICKCORE_QUANTUM_DEPTH == 8
9711             "    %3ld (%3d,%3d,%3d)",
9712 #else
9713             "    %5ld (%5d,%5d,%5d)",
9714 #endif
9715             (long) i,palette[i].red,palette[i].green,palette[i].blue);
9716
9717       }
9718
9719       ping_have_PLTE=MagickTrue;
9720       image_depth=ping_bit_depth;
9721       ping_num_trans=0;
9722
9723       if (matte != MagickFalse)
9724       {
9725           /*
9726             Identify which colormap entry is transparent.
9727           */
9728           assert(number_colors <= 256);
9729           assert(image->colormap != NULL);
9730
9731           for (i=0; i < (ssize_t) number_transparent; i++)
9732              ping_trans_alpha[i]=0;
9733
9734
9735           ping_num_trans=(unsigned short) (number_transparent +
9736              number_semitransparent);
9737
9738           if (ping_num_trans == 0)
9739              ping_have_tRNS=MagickFalse;
9740
9741           else
9742              ping_have_tRNS=MagickTrue;
9743       }
9744
9745       if (ping_exclude_bKGD == MagickFalse)
9746       {
9747        /*
9748         * Identify which colormap entry is the background color.
9749         */
9750
9751         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9752           if (IsPNGColorEqual(ping_background,image->colormap[i]))
9753             break;
9754
9755         ping_background.index=(png_byte) i;
9756
9757         if (logging != MagickFalse)
9758           {
9759             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9760                  "      background_color index is %d",
9761                  (int) ping_background.index);
9762           }
9763       }
9764     } /* end of write_png8 */
9765
9766   else if (mng_info->write_png_colortype == 1)
9767     {
9768       image_matte=MagickFalse;
9769       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9770     }
9771
9772   else if (mng_info->write_png24 || mng_info->write_png48 ||
9773       mng_info->write_png_colortype == 3)
9774     {
9775       image_matte=MagickFalse;
9776       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9777     }
9778
9779   else if (mng_info->write_png32 || mng_info->write_png64 ||
9780       mng_info->write_png_colortype == 7)
9781     {
9782       image_matte=MagickTrue;
9783       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9784     }
9785
9786   else /* mng_info->write_pngNN not specified */
9787     {
9788       image_depth=ping_bit_depth;
9789
9790       if (mng_info->write_png_colortype != 0)
9791         {
9792           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9793
9794           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9795               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9796             image_matte=MagickTrue;
9797
9798           else
9799             image_matte=MagickFalse;
9800
9801           if (logging != MagickFalse)
9802              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9803              "   PNG colortype %d was specified:",(int) ping_color_type);
9804         }
9805
9806       else /* write_png_colortype not specified */
9807         {
9808           if (logging != MagickFalse)
9809              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9810              "  Selecting PNG colortype:");
9811
9812           ping_color_type=(png_byte) ((matte != MagickFalse)?
9813             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9814
9815           if (image_info->type == TrueColorType)
9816             {
9817               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9818               image_matte=MagickFalse;
9819             }
9820
9821           if (image_info->type == TrueColorMatteType)
9822             {
9823               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9824               image_matte=MagickTrue;
9825             }
9826
9827           if (image_info->type == PaletteType ||
9828               image_info->type == PaletteMatteType)
9829             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9830
9831           if (mng_info->write_png_colortype == 0 &&
9832              (image_info->type == UndefinedType ||
9833              image_info->type == OptimizeType))
9834             {
9835               if (ping_have_color == MagickFalse)
9836                 {
9837                   if (image_matte == MagickFalse)
9838                     {
9839                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9840                       image_matte=MagickFalse;
9841                     }
9842
9843                   else
9844                     {
9845                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9846                       image_matte=MagickTrue;
9847                     }
9848                 }
9849               else
9850                 {
9851                   if (image_matte == MagickFalse)
9852                     {
9853                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9854                       image_matte=MagickFalse;
9855                     }
9856
9857                   else
9858                     {
9859                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9860                       image_matte=MagickTrue;
9861                     }
9862                  }
9863             }
9864
9865         }
9866
9867       if (logging != MagickFalse)
9868          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9869          "    Selected PNG colortype=%d",ping_color_type);
9870
9871       if (ping_bit_depth < 8)
9872         {
9873           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9874               ping_color_type == PNG_COLOR_TYPE_RGB ||
9875               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9876             ping_bit_depth=8;
9877         }
9878
9879       old_bit_depth=ping_bit_depth;
9880
9881       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9882         {
9883           if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
9884              ping_bit_depth=1;
9885         }
9886
9887       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9888         {
9889            size_t one = 1;
9890            ping_bit_depth=1;
9891
9892            if (image->colors == 0)
9893            {
9894               /* DO SOMETHING */
9895                 png_error(ping,"image has 0 colors");
9896            }
9897
9898            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9899              ping_bit_depth <<= 1;
9900         }
9901
9902       if (logging != MagickFalse)
9903          {
9904            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9905             "    Number of colors: %.20g",(double) image_colors);
9906
9907            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9908             "    Tentative PNG bit depth: %d",ping_bit_depth);
9909          }
9910
9911       if (ping_bit_depth < (int) mng_info->write_png_depth)
9912          ping_bit_depth = mng_info->write_png_depth;
9913     }
9914
9915   image_depth=ping_bit_depth;
9916
9917   if (logging != MagickFalse)
9918     {
9919       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9920         "    Tentative PNG color type: %s (%.20g)",
9921         PngColorTypeToString(ping_color_type),
9922         (double) ping_color_type);
9923
9924       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9925         "    image_info->type: %.20g",(double) image_info->type);
9926
9927       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9928         "    image_depth: %.20g",(double) image_depth);
9929
9930       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9931
9932         "    image->depth: %.20g",(double) image->depth);
9933
9934       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9935         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
9936     }
9937
9938   if (matte != MagickFalse)
9939     {
9940       if (mng_info->IsPalette)
9941         {
9942           if (mng_info->write_png_colortype == 0)
9943             {
9944               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9945
9946               if (ping_have_color != MagickFalse)
9947                  ping_color_type=PNG_COLOR_TYPE_RGBA;
9948             }
9949
9950           /*
9951            * Determine if there is any transparent color.
9952           */
9953           if (number_transparent + number_semitransparent == 0)
9954             {
9955               /*
9956                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
9957               */
9958
9959               image_matte=MagickFalse;
9960
9961               if (mng_info->write_png_colortype == 0)
9962                 ping_color_type&=0x03;
9963             }
9964
9965           else
9966             {
9967               unsigned int
9968                 mask;
9969
9970               mask=0xffff;
9971
9972               if (ping_bit_depth == 8)
9973                  mask=0x00ff;
9974
9975               if (ping_bit_depth == 4)
9976                  mask=0x000f;
9977
9978               if (ping_bit_depth == 2)
9979                  mask=0x0003;
9980
9981               if (ping_bit_depth == 1)
9982                  mask=0x0001;
9983
9984               ping_trans_color.red=(png_uint_16)
9985                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9986
9987               ping_trans_color.green=(png_uint_16)
9988                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9989
9990               ping_trans_color.blue=(png_uint_16)
9991                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9992
9993               ping_trans_color.gray=(png_uint_16)
9994                 (ScaleQuantumToShort(GetPixelInfoIntensity(
9995                    image->colormap)) & mask);
9996
9997               ping_trans_color.index=(png_byte) 0;
9998
9999               ping_have_tRNS=MagickTrue;
10000             }
10001
10002           if (ping_have_tRNS != MagickFalse)
10003             {
10004               /*
10005                * Determine if there is one and only one transparent color
10006                * and if so if it is fully transparent.
10007                */
10008               if (ping_have_cheap_transparency == MagickFalse)
10009                 ping_have_tRNS=MagickFalse;
10010             }
10011
10012           if (ping_have_tRNS != MagickFalse)
10013             {
10014               if (mng_info->write_png_colortype == 0)
10015                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
10016
10017               if (image_depth == 8)
10018                 {
10019                   ping_trans_color.red&=0xff;
10020                   ping_trans_color.green&=0xff;
10021                   ping_trans_color.blue&=0xff;
10022                   ping_trans_color.gray&=0xff;
10023                 }
10024             }
10025         }
10026       else
10027         {
10028           if (image_depth == 8)
10029             {
10030               ping_trans_color.red&=0xff;
10031               ping_trans_color.green&=0xff;
10032               ping_trans_color.blue&=0xff;
10033               ping_trans_color.gray&=0xff;
10034             }
10035         }
10036     }
10037
10038     matte=image_matte;
10039
10040     if (ping_have_tRNS != MagickFalse)
10041       image_matte=MagickFalse;
10042
10043     if ((mng_info->IsPalette) &&
10044         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10045         ping_have_color == MagickFalse &&
10046         (image_matte == MagickFalse || image_depth >= 8))
10047       {
10048         size_t one=1;
10049
10050         if (image_matte != MagickFalse)
10051           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10052
10053         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10054           {
10055             ping_color_type=PNG_COLOR_TYPE_GRAY;
10056
10057             if (save_image_depth == 16 && image_depth == 8)
10058               {
10059                 if (logging != MagickFalse)
10060                   {
10061                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10062                         "  Scaling ping_trans_color (0)");
10063                   }
10064                     ping_trans_color.gray*=0x0101;
10065               }
10066           }
10067
10068         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10069           image_depth=MAGICKCORE_QUANTUM_DEPTH;
10070
10071         if ((image_colors == 0) ||
10072              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10073           image_colors=(int) (one << image_depth);
10074
10075         if (image_depth > 8)
10076           ping_bit_depth=16;
10077
10078         else
10079           {
10080             ping_bit_depth=8;
10081             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10082               {
10083                 if(!mng_info->write_png_depth)
10084                   {
10085                     ping_bit_depth=1;
10086
10087                     while ((int) (one << ping_bit_depth)
10088                         < (ssize_t) image_colors)
10089                       ping_bit_depth <<= 1;
10090                   }
10091               }
10092
10093             else if (ping_color_type ==
10094                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10095                 mng_info->IsPalette)
10096               {
10097               /* Check if grayscale is reducible */
10098
10099                 int
10100                   depth_4_ok=MagickTrue,
10101                   depth_2_ok=MagickTrue,
10102                   depth_1_ok=MagickTrue;
10103
10104                 for (i=0; i < (ssize_t) image_colors; i++)
10105                 {
10106                    unsigned char
10107                      intensity;
10108
10109                    intensity=ScaleQuantumToChar(image->colormap[i].red);
10110
10111                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10112                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10113                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10114                      depth_2_ok=depth_1_ok=MagickFalse;
10115                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10116                      depth_1_ok=MagickFalse;
10117                 }
10118
10119                 if (depth_1_ok && mng_info->write_png_depth <= 1)
10120                   ping_bit_depth=1;
10121
10122                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10123                   ping_bit_depth=2;
10124
10125                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10126                   ping_bit_depth=4;
10127               }
10128           }
10129
10130           image_depth=ping_bit_depth;
10131       }
10132
10133     else
10134
10135       if (mng_info->IsPalette)
10136       {
10137         number_colors=image_colors;
10138
10139         if (image_depth <= 8)
10140           {
10141             /*
10142               Set image palette.
10143             */
10144             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10145
10146             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10147               {
10148                 for (i=0; i < (ssize_t) number_colors; i++)
10149                 {
10150                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10151                   palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10152                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10153                 }
10154
10155                 if (logging != MagickFalse)
10156                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10157                     "  Setting up PLTE chunk with %d colors",
10158                     number_colors);
10159
10160                 ping_have_PLTE=MagickTrue;
10161               }
10162
10163             /* color_type is PNG_COLOR_TYPE_PALETTE */
10164             if (mng_info->write_png_depth == 0)
10165               {
10166                 size_t
10167                   one;
10168
10169                 ping_bit_depth=1;
10170                 one=1;
10171
10172                 while ((one << ping_bit_depth) < (size_t) number_colors)
10173                   ping_bit_depth <<= 1;
10174               }
10175
10176             ping_num_trans=0;
10177
10178             if (matte != MagickFalse)
10179               {
10180                 /*
10181                  * Set up trans_colors array.
10182                  */
10183                 assert(number_colors <= 256);
10184
10185                 ping_num_trans=(unsigned short) (number_transparent +
10186                   number_semitransparent);
10187
10188                 if (ping_num_trans == 0)
10189                   ping_have_tRNS=MagickFalse;
10190
10191                 else
10192                   {
10193                     if (logging != MagickFalse)
10194                       {
10195                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10196                           "  Scaling ping_trans_color (1)");
10197                       }
10198                     ping_have_tRNS=MagickTrue;
10199
10200                     for (i=0; i < ping_num_trans; i++)
10201                     {
10202                        ping_trans_alpha[i]= (png_byte)
10203                          ScaleQuantumToChar(image->colormap[i].alpha);
10204                     }
10205                   }
10206               }
10207           }
10208       }
10209
10210     else
10211       {
10212
10213         if (image_depth < 8)
10214           image_depth=8;
10215
10216         if ((save_image_depth == 16) && (image_depth == 8))
10217           {
10218             if (logging != MagickFalse)
10219               {
10220                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10221                   "    Scaling ping_trans_color from (%d,%d,%d)",
10222                   (int) ping_trans_color.red,
10223                   (int) ping_trans_color.green,
10224                   (int) ping_trans_color.blue);
10225               }
10226
10227             ping_trans_color.red*=0x0101;
10228             ping_trans_color.green*=0x0101;
10229             ping_trans_color.blue*=0x0101;
10230             ping_trans_color.gray*=0x0101;
10231
10232             if (logging != MagickFalse)
10233               {
10234                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10235                   "    to (%d,%d,%d)",
10236                   (int) ping_trans_color.red,
10237                   (int) ping_trans_color.green,
10238                   (int) ping_trans_color.blue);
10239               }
10240           }
10241       }
10242
10243     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
10244          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
10245
10246     /*
10247       Adjust background and transparency samples in sub-8-bit grayscale files.
10248     */
10249     if (ping_bit_depth < 8 && ping_color_type ==
10250         PNG_COLOR_TYPE_GRAY)
10251       {
10252          png_uint_16
10253            maxval;
10254
10255          size_t
10256            one=1;
10257
10258          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10259
10260          if (ping_exclude_bKGD == MagickFalse)
10261          {
10262
10263          ping_background.gray=(png_uint_16) ((maxval/65535.)*
10264            (ScaleQuantumToShort(((GetPixelInfoIntensity(
10265            &image->background_color))) +.5)));
10266
10267          if (logging != MagickFalse)
10268            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10269              "  Setting up bKGD chunk (2)");
10270          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10271              "      background_color index is %d",
10272              (int) ping_background.index);
10273
10274          ping_have_bKGD = MagickTrue;
10275          }
10276
10277          if (logging != MagickFalse)
10278            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10279              "  Scaling ping_trans_color.gray from %d",
10280              (int)ping_trans_color.gray);
10281
10282          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10283            ping_trans_color.gray)+.5);
10284
10285          if (logging != MagickFalse)
10286            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10287              "      to %d", (int)ping_trans_color.gray);
10288       }
10289
10290   if (ping_exclude_bKGD == MagickFalse)
10291   {
10292     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10293       {
10294         /*
10295            Identify which colormap entry is the background color.
10296         */
10297
10298         number_colors=image_colors;
10299
10300         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10301           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10302             break;
10303
10304         ping_background.index=(png_byte) i;
10305
10306         if (logging != MagickFalse)
10307           {
10308             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10309               "  Setting up bKGD chunk with index=%d",(int) i);
10310           }
10311
10312         if (i < (ssize_t) number_colors)
10313           {
10314             ping_have_bKGD = MagickTrue;
10315
10316             if (logging != MagickFalse)
10317               {
10318                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10319                   "     background   =(%d,%d,%d)",
10320                         (int) ping_background.red,
10321                         (int) ping_background.green,
10322                         (int) ping_background.blue);
10323               }
10324           }
10325
10326         else  /* Can't happen */
10327           {
10328             if (logging != MagickFalse)
10329               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10330                   "      No room in PLTE to add bKGD color");
10331             ping_have_bKGD = MagickFalse;
10332           }
10333       }
10334   }
10335
10336   if (logging != MagickFalse)
10337     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10338       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10339       ping_color_type);
10340   /*
10341     Initialize compression level and filtering.
10342   */
10343   if (logging != MagickFalse)
10344     {
10345       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10346         "  Setting up deflate compression");
10347
10348       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10349         "    Compression buffer size: 32768");
10350     }
10351
10352   png_set_compression_buffer_size(ping,32768L);
10353
10354   if (logging != MagickFalse)
10355     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10356       "    Compression mem level: 9");
10357
10358   png_set_compression_mem_level(ping, 9);
10359
10360   /* Untangle the "-quality" setting:
10361
10362      Undefined is 0; the default is used.
10363      Default is 75
10364
10365      10's digit:
10366
10367         0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10368            zlib default compression level
10369
10370         1-9: the zlib compression level
10371
10372      1's digit:
10373
10374         0-4: the PNG filter method
10375
10376         5:   libpng adaptive filtering if compression level > 5
10377              libpng filter type "none" if compression level <= 5
10378                 or if image is grayscale or palette
10379
10380         6:   libpng adaptive filtering
10381
10382         7:   "LOCO" filtering (intrapixel differing) if writing
10383              a MNG, otherwise "none".  Did not work in IM-6.7.0-9
10384              and earlier because of a missing "else".
10385
10386         8:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10387              filtering. Unused prior to IM-6.7.0-10, was same as 6
10388
10389         9:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10390              Unused prior to IM-6.7.0-10, was same as 6
10391
10392     Note that using the -quality option, not all combinations of
10393     PNG filter type, zlib compression level, and zlib compression
10394     strategy are possible.  This will be addressed soon in a
10395     release that accomodates "-define png:compression-strategy", etc.
10396
10397    */
10398
10399   quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10400      image_info->quality;
10401
10402   if (quality <= 9)
10403     {
10404       if (mng_info->write_png_compression_strategy == 0)
10405         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10406     }
10407
10408   else if (mng_info->write_png_compression_level == 0)
10409     {
10410       int
10411         level;
10412
10413       level=(int) MagickMin((ssize_t) quality/10,9);
10414
10415       mng_info->write_png_compression_level = level+1;
10416     }
10417
10418   if (mng_info->write_png_compression_strategy == 0)
10419     {
10420         if ((quality %10) == 8 || (quality %10) == 9)
10421 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10422           mng_info->write_png_compression_strategy=Z_RLE+1;
10423 #else
10424           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10425 #endif
10426     }
10427
10428   if (mng_info->write_png_compression_filter == 0)
10429         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10430
10431   if (logging != MagickFalse)
10432     {
10433      if (mng_info->write_png_compression_level)
10434         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10435           "    Compression level:    %d",
10436             (int) mng_info->write_png_compression_level-1);
10437
10438      if (mng_info->write_png_compression_strategy)
10439         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10440           "    Compression strategy: %d",
10441             (int) mng_info->write_png_compression_strategy-1);
10442
10443         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10444           "  Setting up filtering");
10445
10446         if (mng_info->write_png_compression_filter == 6)
10447           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10448             "    Base filter method: ADAPTIVE");
10449         else if (mng_info->write_png_compression_filter == 0 ||
10450                  mng_info->write_png_compression_filter == 1)
10451           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10452             "    Base filter method: NONE");
10453         else
10454           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10455             "    Base filter method: %d",
10456             (int) mng_info->write_png_compression_filter-1);
10457     }
10458
10459   if (mng_info->write_png_compression_level != 0)
10460     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10461
10462   if (mng_info->write_png_compression_filter == 6)
10463     {
10464       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10465          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10466          (quality < 50))
10467         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10468       else
10469         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10470      }
10471   else if (mng_info->write_png_compression_filter == 7 ||
10472       mng_info->write_png_compression_filter == 10)
10473     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10474
10475   else if (mng_info->write_png_compression_filter == 8)
10476     {
10477 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10478       if (mng_info->write_mng)
10479       {
10480          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10481              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10482         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10483       }
10484 #endif
10485       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10486     }
10487
10488   else if (mng_info->write_png_compression_filter == 9)
10489     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10490
10491   else if (mng_info->write_png_compression_filter != 0)
10492     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10493        mng_info->write_png_compression_filter-1);
10494
10495   if (mng_info->write_png_compression_strategy != 0)
10496     png_set_compression_strategy(ping,
10497        mng_info->write_png_compression_strategy-1);
10498
10499   ping_interlace_method=image_info->interlace != NoInterlace;
10500
10501   if (mng_info->write_mng)
10502     png_set_sig_bytes(ping,8);
10503
10504   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10505
10506   if (mng_info->write_png_colortype != 0)
10507     {
10508      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10509        if (ping_have_color != MagickFalse)
10510          {
10511            ping_color_type = PNG_COLOR_TYPE_RGB;
10512
10513            if (ping_bit_depth < 8)
10514              ping_bit_depth=8;
10515          }
10516
10517      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10518        if (ping_have_color != MagickFalse)
10519          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10520     }
10521
10522   if (ping_need_colortype_warning != MagickFalse ||
10523      ((mng_info->write_png_depth &&
10524      (int) mng_info->write_png_depth != ping_bit_depth) ||
10525      (mng_info->write_png_colortype &&
10526      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10527       mng_info->write_png_colortype != 7 &&
10528       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10529     {
10530       if (logging != MagickFalse)
10531         {
10532           if (ping_need_colortype_warning != MagickFalse)
10533             {
10534               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10535                  "  Image has transparency but tRNS chunk was excluded");
10536             }
10537
10538           if (mng_info->write_png_depth)
10539             {
10540               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10541                   "  Defined png:bit-depth=%u, Computed depth=%u",
10542                   mng_info->write_png_depth,
10543                   ping_bit_depth);
10544             }
10545
10546           if (mng_info->write_png_colortype)
10547             {
10548               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10549                   "  Defined png:color-type=%u, Computed color type=%u",
10550                   mng_info->write_png_colortype-1,
10551                   ping_color_type);
10552             }
10553         }
10554
10555       png_warning(ping,
10556         "Cannot write image with defined png:bit-depth or png:color-type.");
10557     }
10558
10559   if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10560     {
10561       /* Add an opaque matte channel */
10562       image->alpha_trait = BlendPixelTrait;
10563       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10564
10565       if (logging != MagickFalse)
10566         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10567           "  Added an opaque matte channel");
10568     }
10569
10570   if (number_transparent != 0 || number_semitransparent != 0)
10571     {
10572       if (ping_color_type < 4)
10573         {
10574            ping_have_tRNS=MagickTrue;
10575            if (logging != MagickFalse)
10576              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10577                "  Setting ping_have_tRNS=MagickTrue.");
10578         }
10579     }
10580
10581   if (logging != MagickFalse)
10582     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10583       "  Writing PNG header chunks");
10584
10585   png_set_IHDR(ping,ping_info,ping_width,ping_height,
10586                ping_bit_depth,ping_color_type,
10587                ping_interlace_method,ping_compression_method,
10588                ping_filter_method);
10589
10590   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10591     {
10592       png_set_PLTE(ping,ping_info,palette,number_colors);
10593
10594       if (logging != MagickFalse)
10595         {
10596           for (i=0; i< (ssize_t) number_colors; i++)
10597           {
10598             if (i < ping_num_trans)
10599               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10600                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10601                       (int) i,
10602                       (int) palette[i].red,
10603                       (int) palette[i].green,
10604                       (int) palette[i].blue,
10605                       (int) i,
10606                       (int) ping_trans_alpha[i]);
10607              else
10608               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10609                 "     PLTE[%d] = (%d,%d,%d)",
10610                       (int) i,
10611                       (int) palette[i].red,
10612                       (int) palette[i].green,
10613                       (int) palette[i].blue);
10614            }
10615          }
10616     }
10617
10618   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10619   if (ping_exclude_sRGB != MagickFalse ||
10620      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10621   {
10622     if ((ping_exclude_tEXt == MagickFalse ||
10623        ping_exclude_zTXt == MagickFalse) &&
10624        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10625     {
10626       ResetImageProfileIterator(image);
10627       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10628       {
10629         profile=GetImageProfile(image,name);
10630
10631         if (profile != (StringInfo *) NULL)
10632           {
10633 #ifdef PNG_WRITE_iCCP_SUPPORTED
10634             if ((LocaleCompare(name,"ICC") == 0) ||
10635                 (LocaleCompare(name,"ICM") == 0))
10636              {
10637
10638                if (ping_exclude_iCCP == MagickFalse)
10639                  {
10640                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10641                           "  Setting up iCCP chunk");
10642     
10643                        png_set_iCCP(ping,ping_info,(png_charp) name,0,
10644 #if (PNG_LIBPNG_VER < 10500)
10645                          (png_charp) GetStringInfoDatum(profile),
10646 #else
10647                          (png_const_bytep) GetStringInfoDatum(profile),
10648 #endif
10649                          (png_uint_32) GetStringInfoLength(profile));
10650                        ping_have_iCCP = MagickTrue;
10651                  }
10652              }
10653
10654             else
10655 #endif
10656               if (ping_exclude_zCCP == MagickFalse)
10657                 {
10658                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10659                       "  Setting up zTXT chunk with uuencoded ICC");
10660                   Magick_png_write_raw_profile(image_info,ping,ping_info,
10661                     (unsigned char *) name,(unsigned char *) name,
10662                     GetStringInfoDatum(profile),
10663                     (png_uint_32) GetStringInfoLength(profile));
10664                   ping_have_iCCP = MagickTrue;
10665                 }
10666           }
10667
10668           if (logging != MagickFalse)
10669             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10670               "  Setting up text chunk with %s profile",name);
10671
10672         name=GetNextImageProfile(image);
10673       }
10674     }
10675   }
10676
10677 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10678   if ((mng_info->have_write_global_srgb == 0) &&
10679       ping_have_iCCP != MagickTrue &&
10680       (ping_have_sRGB != MagickFalse ||
10681       png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10682     {
10683       if (ping_exclude_sRGB == MagickFalse)
10684         {
10685           /*
10686             Note image rendering intent.
10687           */
10688           if (logging != MagickFalse)
10689             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10690                 "  Setting up sRGB chunk");
10691
10692           (void) png_set_sRGB(ping,ping_info,(
10693             Magick_RenderingIntent_to_PNG_RenderingIntent(
10694               image->rendering_intent)));
10695
10696           ping_have_sRGB = MagickTrue;
10697         }
10698     }
10699
10700   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10701 #endif
10702     {
10703       if (ping_exclude_gAMA == MagickFalse &&
10704           ping_have_iCCP == MagickFalse &&
10705           ping_have_sRGB == MagickFalse &&
10706           (ping_exclude_sRGB == MagickFalse ||
10707           (image->gamma < .45 || image->gamma > .46)))
10708       {
10709       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10710         {
10711           /*
10712             Note image gamma.
10713             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10714           */
10715           if (logging != MagickFalse)
10716             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10717               "  Setting up gAMA chunk");
10718
10719           png_set_gAMA(ping,ping_info,image->gamma);
10720         }
10721       }
10722
10723       if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10724         {
10725           if ((mng_info->have_write_global_chrm == 0) &&
10726               (image->chromaticity.red_primary.x != 0.0))
10727             {
10728               /*
10729                 Note image chromaticity.
10730                 Note: if cHRM+gAMA == sRGB write sRGB instead.
10731               */
10732                PrimaryInfo
10733                  bp,
10734                  gp,
10735                  rp,
10736                  wp;
10737
10738                wp=image->chromaticity.white_point;
10739                rp=image->chromaticity.red_primary;
10740                gp=image->chromaticity.green_primary;
10741                bp=image->chromaticity.blue_primary;
10742
10743                if (logging != MagickFalse)
10744                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10745                    "  Setting up cHRM chunk");
10746
10747                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10748                    bp.x,bp.y);
10749            }
10750         }
10751     }
10752
10753   if (ping_exclude_bKGD == MagickFalse)
10754     {
10755       if (ping_have_bKGD != MagickFalse)
10756         {
10757           png_set_bKGD(ping,ping_info,&ping_background);
10758           if (logging)
10759             {
10760               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10761                    "    Setting up bKGD chunk");
10762               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10763                    "      background color = (%d,%d,%d)",
10764                         (int) ping_background.red,
10765                         (int) ping_background.green,
10766                         (int) ping_background.blue);
10767               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10768                    "      index = %d, gray=%d",
10769                         (int) ping_background.index,
10770                         (int) ping_background.gray);
10771             }
10772          }
10773     }
10774
10775   if (ping_exclude_pHYs == MagickFalse)
10776     {
10777       if (ping_have_pHYs != MagickFalse)
10778         {
10779           png_set_pHYs(ping,ping_info,
10780              ping_pHYs_x_resolution,
10781              ping_pHYs_y_resolution,
10782              ping_pHYs_unit_type);
10783
10784           if (logging)
10785             {
10786               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10787                    "    Setting up pHYs chunk");
10788               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10789                    "      x_resolution=%lu",
10790                    (unsigned long) ping_pHYs_x_resolution);
10791               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10792                    "      y_resolution=%lu",
10793                    (unsigned long) ping_pHYs_y_resolution);
10794               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10795                    "      unit_type=%lu",
10796                    (unsigned long) ping_pHYs_unit_type);
10797             }
10798         }
10799     }
10800
10801 #if defined(PNG_oFFs_SUPPORTED)
10802   if (ping_exclude_oFFs == MagickFalse)
10803     {
10804       if (image->page.x || image->page.y)
10805         {
10806            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10807               (png_int_32) image->page.y, 0);
10808
10809            if (logging != MagickFalse)
10810              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10811                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
10812                  (int) image->page.x, (int) image->page.y);
10813         }
10814     }
10815 #endif
10816
10817   if (mng_info->need_blob != MagickFalse)
10818   {
10819     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10820        MagickFalse)
10821        png_error(ping,"WriteBlob Failed");
10822
10823      ping_have_blob=MagickTrue;
10824   }
10825
10826   png_write_info_before_PLTE(ping, ping_info);
10827
10828   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10829     {
10830       if (logging != MagickFalse)
10831         {
10832           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10833               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10834         }
10835
10836       if (ping_color_type == 3)
10837          (void) png_set_tRNS(ping, ping_info,
10838                 ping_trans_alpha,
10839                 ping_num_trans,
10840                 NULL);
10841
10842       else
10843         {
10844            (void) png_set_tRNS(ping, ping_info,
10845                   NULL,
10846                   0,
10847                   &ping_trans_color);
10848
10849            if (logging != MagickFalse)
10850              {
10851                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10852                  "     tRNS color   =(%d,%d,%d)",
10853                        (int) ping_trans_color.red,
10854                        (int) ping_trans_color.green,
10855                        (int) ping_trans_color.blue);
10856              }
10857          }
10858     }
10859
10860   /* write any png-chunk-b profiles */
10861   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10862
10863   png_write_info(ping,ping_info);
10864
10865   /* write any PNG-chunk-m profiles */
10866   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10867
10868   if (ping_exclude_vpAg == MagickFalse)
10869     {
10870       if ((image->page.width != 0 && image->page.width != image->columns) ||
10871           (image->page.height != 0 && image->page.height != image->rows))
10872         {
10873           unsigned char
10874             chunk[14];
10875
10876           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
10877           PNGType(chunk,mng_vpAg);
10878           LogPNGChunk(logging,mng_vpAg,9L);
10879           PNGLong(chunk+4,(png_uint_32) image->page.width);
10880           PNGLong(chunk+8,(png_uint_32) image->page.height);
10881           chunk[12]=0;   /* unit = pixels */
10882           (void) WriteBlob(image,13,chunk);
10883           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10884         }
10885     }
10886
10887 #if (PNG_LIBPNG_VER == 10206)
10888     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10889 #define PNG_HAVE_IDAT               0x04
10890     ping->mode |= PNG_HAVE_IDAT;
10891 #undef PNG_HAVE_IDAT
10892 #endif
10893
10894   png_set_packing(ping);
10895   /*
10896     Allocate memory.
10897   */
10898   rowbytes=image->columns;
10899   if (image_depth > 8)
10900     rowbytes*=2;
10901   switch (ping_color_type)
10902     {
10903       case PNG_COLOR_TYPE_RGB:
10904         rowbytes*=3;
10905         break;
10906
10907       case PNG_COLOR_TYPE_GRAY_ALPHA:
10908         rowbytes*=2;
10909         break;
10910
10911       case PNG_COLOR_TYPE_RGBA:
10912         rowbytes*=4;
10913         break;
10914
10915       default:
10916         break;
10917     }
10918
10919   if (logging != MagickFalse)
10920     {
10921       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10922         "  Writing PNG image data");
10923
10924       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10925         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10926     }
10927   pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10928   if (pixel_info == (MemoryInfo *) NULL)
10929     png_error(ping,"Allocation of memory for pixels failed");
10930   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
10931
10932   /*
10933     Initialize image scanlines.
10934   */
10935   quantum_info=AcquireQuantumInfo(image_info,image);
10936   if (quantum_info == (QuantumInfo *) NULL)
10937     png_error(ping,"Memory allocation for quantum_info failed");
10938   quantum_info->format=UndefinedQuantumFormat;
10939   quantum_info->depth=image_depth;
10940   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10941   num_passes=png_set_interlace_handling(ping);
10942
10943   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10944        !mng_info->write_png48 && !mng_info->write_png64 &&
10945        !mng_info->write_png32) &&
10946        (mng_info->IsPalette ||
10947        (image_info->type == BilevelType)) &&
10948        image_matte == MagickFalse &&
10949        ping_have_non_bw == MagickFalse)
10950     {
10951       /* Palette, Bilevel, or Opaque Monochrome */
10952       register const Quantum
10953         *p;
10954
10955       quantum_info->depth=8;
10956       for (pass=0; pass < num_passes; pass++)
10957       {
10958         /*
10959           Convert PseudoClass image to a PNG monochrome image.
10960         */
10961         for (y=0; y < (ssize_t) image->rows; y++)
10962         {
10963           if (logging != MagickFalse && y == 0)
10964              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10965                  "    Writing row of pixels (0)");
10966
10967           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10968
10969           if (p == (const Quantum *) NULL)
10970             break;
10971
10972           if (mng_info->IsPalette)
10973             {
10974               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10975                 quantum_info,GrayQuantum,ping_pixels,exception);
10976               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10977                   mng_info->write_png_depth &&
10978                   mng_info->write_png_depth != old_bit_depth)
10979                 {
10980                   /* Undo pixel scaling */
10981                   for (i=0; i < (ssize_t) image->columns; i++)
10982                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10983                      >> (8-old_bit_depth));
10984                 }
10985             }
10986
10987           else
10988             {
10989               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10990                 quantum_info,RedQuantum,ping_pixels,exception);
10991             }
10992
10993           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10994             for (i=0; i < (ssize_t) image->columns; i++)
10995                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10996                       255 : 0);
10997
10998           if (logging != MagickFalse && y == 0)
10999             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11000                 "    Writing row of pixels (1)");
11001
11002           png_write_row(ping,ping_pixels);
11003         }
11004         if (image->previous == (Image *) NULL)
11005           {
11006             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11007             if (status == MagickFalse)
11008               break;
11009           }
11010       }
11011     }
11012
11013   else   /* Not Palette, Bilevel, or Opaque Monochrome */
11014     {
11015       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11016           !mng_info->write_png48 && !mng_info->write_png64 &&
11017           !mng_info->write_png32) && (image_matte != MagickFalse ||
11018           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11019           (mng_info->IsPalette) && ping_have_color == MagickFalse)
11020         {
11021           register const Quantum
11022             *p;
11023
11024           for (pass=0; pass < num_passes; pass++)
11025           {
11026
11027           for (y=0; y < (ssize_t) image->rows; y++)
11028           {
11029             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11030
11031             if (p == (const Quantum *) NULL)
11032               break;
11033
11034             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11035               {
11036                 if (mng_info->IsPalette)
11037                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11038                     quantum_info,GrayQuantum,ping_pixels,exception);
11039
11040                 else
11041                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11042                     quantum_info,RedQuantum,ping_pixels,exception);
11043
11044                 if (logging != MagickFalse && y == 0)
11045                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11046                        "    Writing GRAY PNG pixels (2)");
11047               }
11048
11049             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11050               {
11051                 if (logging != MagickFalse && y == 0)
11052                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11053                          "    Writing GRAY_ALPHA PNG pixels (2)");
11054
11055                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11056                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11057               }
11058
11059             if (logging != MagickFalse && y == 0)
11060               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11061                   "    Writing row of pixels (2)");
11062
11063             png_write_row(ping,ping_pixels);
11064           }
11065
11066           if (image->previous == (Image *) NULL)
11067             {
11068               status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11069               if (status == MagickFalse)
11070                 break;
11071             }
11072           }
11073         }
11074
11075       else
11076         {
11077           register const Quantum
11078             *p;
11079
11080           for (pass=0; pass < num_passes; pass++)
11081           {
11082             if ((image_depth > 8) ||
11083                 mng_info->write_png24 ||
11084                 mng_info->write_png32 ||
11085                 mng_info->write_png48 ||
11086                 mng_info->write_png64 ||
11087                 (!mng_info->write_png8 && !mng_info->IsPalette))
11088             {
11089               for (y=0; y < (ssize_t) image->rows; y++)
11090               {
11091                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11092
11093                 if (p == (const Quantum *) NULL)
11094                   break;
11095
11096                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11097                   {
11098                     if (image->storage_class == DirectClass)
11099                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11100                         quantum_info,RedQuantum,ping_pixels,exception);
11101
11102                     else
11103                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11104                         quantum_info,GrayQuantum,ping_pixels,exception);
11105                   }
11106
11107                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11108                   {
11109                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11110                       quantum_info,GrayAlphaQuantum,ping_pixels,
11111                       exception);
11112
11113                     if (logging != MagickFalse && y == 0)
11114                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11115                            "    Writing GRAY_ALPHA PNG pixels (3)");
11116                   }
11117
11118                 else if (image_matte != MagickFalse)
11119                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11120                     quantum_info,RGBAQuantum,ping_pixels,exception);
11121
11122                 else
11123                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11124                     quantum_info,RGBQuantum,ping_pixels,exception);
11125
11126                 if (logging != MagickFalse && y == 0)
11127                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11128                       "    Writing row of pixels (3)");
11129
11130                 png_write_row(ping,ping_pixels);
11131               }
11132             }
11133
11134           else
11135             /* not ((image_depth > 8) ||
11136                 mng_info->write_png24 || mng_info->write_png32 ||
11137                 mng_info->write_png48 || mng_info->write_png64 ||
11138                 (!mng_info->write_png8 && !mng_info->IsPalette))
11139              */
11140             {
11141               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11142                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11143                 {
11144                   if (logging != MagickFalse)
11145                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11146                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11147
11148                   quantum_info->depth=8;
11149                   image_depth=8;
11150                 }
11151
11152               for (y=0; y < (ssize_t) image->rows; y++)
11153               {
11154                 if (logging != MagickFalse && y == 0)
11155                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11156                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
11157
11158                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11159
11160                 if (p == (const Quantum *) NULL)
11161                   break;
11162
11163                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11164                   {
11165                     quantum_info->depth=image->depth;
11166
11167                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11168                        quantum_info,GrayQuantum,ping_pixels,exception);
11169                   }
11170
11171                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11172                   {
11173                     if (logging != MagickFalse && y == 0)
11174                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11175                            "  Writing GRAY_ALPHA PNG pixels (4)");
11176
11177                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11178                          quantum_info,GrayAlphaQuantum,ping_pixels,
11179                          exception);
11180                   }
11181
11182                 else
11183                   {
11184                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11185                       quantum_info,IndexQuantum,ping_pixels,exception);
11186
11187                     if (logging != MagickFalse && y <= 2)
11188                     {
11189                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11190                           "  Writing row of non-gray pixels (4)");
11191
11192                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11193                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
11194                           (int)ping_pixels[0],(int)ping_pixels[1]);
11195                     }
11196                   }
11197                 png_write_row(ping,ping_pixels);
11198               }
11199             }
11200
11201             if (image->previous == (Image *) NULL)
11202               {
11203                 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11204                 if (status == MagickFalse)
11205                   break;
11206               }
11207           }
11208         }
11209     }
11210
11211   if (quantum_info != (QuantumInfo *) NULL)
11212     quantum_info=DestroyQuantumInfo(quantum_info);
11213
11214   if (logging != MagickFalse)
11215     {
11216       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11217         "  Wrote PNG image data");
11218
11219       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11220         "    Width: %.20g",(double) ping_width);
11221
11222       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11223         "    Height: %.20g",(double) ping_height);
11224
11225       if (mng_info->write_png_depth)
11226         {
11227           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11228             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
11229         }
11230
11231       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11232         "    PNG bit-depth written: %d",ping_bit_depth);
11233
11234       if (mng_info->write_png_colortype)
11235         {
11236           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11237             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
11238         }
11239
11240       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11241         "    PNG color-type written: %d",ping_color_type);
11242
11243       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11244         "    PNG Interlace method: %d",ping_interlace_method);
11245     }
11246   /*
11247     Generate text chunks after IDAT.
11248   */
11249   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11250   {
11251     ResetImagePropertyIterator(image);
11252     property=GetNextImageProperty(image);
11253     while (property != (const char *) NULL)
11254     {
11255       png_textp
11256         text;
11257
11258       value=GetImageProperty(image,property,exception);
11259
11260       /* Don't write any "png:" or "jpeg:" properties; those are just for
11261        * "identify" or for passing through to another JPEG
11262        */
11263       if ((LocaleNCompare(property,"png:",4) != 0 &&
11264            LocaleNCompare(property,"jpeg:",5) != 0) &&
11265
11266
11267           /* Suppress density and units if we wrote a pHYs chunk */
11268           (ping_exclude_pHYs != MagickFalse      ||
11269           LocaleCompare(property,"density") != 0 ||
11270           LocaleCompare(property,"units") != 0) &&
11271
11272           /* Suppress the IM-generated Date:create and Date:modify */
11273           (ping_exclude_date == MagickFalse      ||
11274           LocaleNCompare(property, "Date:",5) != 0))
11275         {
11276         if (value != (const char *) NULL)
11277           {
11278
11279 #if PNG_LIBPNG_VER >= 10400
11280             text=(png_textp) png_malloc(ping,
11281                  (png_alloc_size_t) sizeof(png_text));
11282 #else
11283             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11284 #endif
11285             text[0].key=(char *) property;
11286             text[0].text=(char *) value;
11287             text[0].text_length=strlen(value);
11288
11289             if (ping_exclude_tEXt != MagickFalse)
11290                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11291
11292             else if (ping_exclude_zTXt != MagickFalse)
11293                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11294
11295             else
11296             {
11297                text[0].compression=image_info->compression == NoCompression ||
11298                  (image_info->compression == UndefinedCompression &&
11299                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11300                  PNG_TEXT_COMPRESSION_zTXt ;
11301             }
11302
11303             if (logging != MagickFalse)
11304               {
11305                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11306                   "  Setting up text chunk");
11307
11308                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11309                   "    keyword: '%s'",text[0].key);
11310               }
11311
11312             png_set_text(ping,ping_info,text,1);
11313             png_free(ping,text);
11314           }
11315         }
11316       property=GetNextImageProperty(image);
11317     }
11318   }
11319
11320   /* write any PNG-chunk-e profiles */
11321   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11322
11323   if (logging != MagickFalse)
11324     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11325       "  Writing PNG end info");
11326
11327   png_write_end(ping,ping_info);
11328
11329   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11330     {
11331       if (mng_info->page.x || mng_info->page.y ||
11332           (ping_width != mng_info->page.width) ||
11333           (ping_height != mng_info->page.height))
11334         {
11335           unsigned char
11336             chunk[32];
11337
11338           /*
11339             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11340           */
11341           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11342           PNGType(chunk,mng_FRAM);
11343           LogPNGChunk(logging,mng_FRAM,27L);
11344           chunk[4]=4;
11345           chunk[5]=0;  /* frame name separator (no name) */
11346           chunk[6]=1;  /* flag for changing delay, for next frame only */
11347           chunk[7]=0;  /* flag for changing frame timeout */
11348           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11349           chunk[9]=0;  /* flag for changing frame sync_id */
11350           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11351           chunk[14]=0; /* clipping boundaries delta type */
11352           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11353           PNGLong(chunk+19,
11354              (png_uint_32) (mng_info->page.x + ping_width));
11355           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11356           PNGLong(chunk+27,
11357              (png_uint_32) (mng_info->page.y + ping_height));
11358           (void) WriteBlob(image,31,chunk);
11359           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11360           mng_info->old_framing_mode=4;
11361           mng_info->framing_mode=1;
11362         }
11363
11364       else
11365         mng_info->framing_mode=3;
11366     }
11367   if (mng_info->write_mng && !mng_info->need_fram &&
11368       ((int) image->dispose == 3))
11369      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11370
11371   /*
11372     Free PNG resources.
11373   */
11374
11375   png_destroy_write_struct(&ping,&ping_info);
11376
11377   pixel_info=RelinquishVirtualMemory(pixel_info);
11378
11379   if (ping_have_blob != MagickFalse)
11380      (void) CloseBlob(image);
11381
11382   image_info=DestroyImageInfo(image_info);
11383   image=DestroyImage(image);
11384
11385   /* Store bit depth actually written */
11386   s[0]=(char) ping_bit_depth;
11387   s[1]='\0';
11388
11389   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11390
11391   if (logging != MagickFalse)
11392     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11393       "  exit WriteOnePNGImage()");
11394
11395 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11396   UnlockSemaphoreInfo(ping_semaphore);
11397 #endif
11398
11399    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11400     *    Throwing an Exception when an error occurs.
11401     */
11402
11403   return(MagickTrue);
11404 /*  End write one PNG image */
11405
11406 }
11407
11408 /*
11409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11410 %                                                                             %
11411 %                                                                             %
11412 %                                                                             %
11413 %   W r i t e P N G I m a g e                                                 %
11414 %                                                                             %
11415 %                                                                             %
11416 %                                                                             %
11417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11418 %
11419 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11420 %  Multiple-image Network Graphics (MNG) image file.
11421 %
11422 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11423 %
11424 %  The format of the WritePNGImage method is:
11425 %
11426 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11427 %        Image *image,ExceptionInfo *exception)
11428 %
11429 %  A description of each parameter follows:
11430 %
11431 %    o image_info: the image info.
11432 %
11433 %    o image:  The image.
11434 %
11435 %    o exception: return any errors or warnings in this structure.
11436 %
11437 %  Returns MagickTrue on success, MagickFalse on failure.
11438 %
11439 %  Communicating with the PNG encoder:
11440 %
11441 %  While the datastream written is always in PNG format and normally would
11442 %  be given the "png" file extension, this method also writes the following
11443 %  pseudo-formats which are subsets of png:
11444 %
11445 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11446 %               a depth greater than 8, the depth is reduced. If transparency
11447 %               is present, the tRNS chunk must only have values 0 and 255
11448 %               (i.e., transparency is binary: fully opaque or fully
11449 %               transparent).  If other values are present they will be
11450 %               50%-thresholded to binary transparency.  If more than 256
11451 %               colors are present, they will be quantized to the 4-4-4-1,
11452 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11453 %               of any resulting fully-transparent pixels is changed to
11454 %               the image's background color.
11455 %
11456 %               If you want better quantization or dithering of the colors
11457 %               or alpha than that, you need to do it before calling the
11458 %               PNG encoder. The pixels contain 8-bit indices even if
11459 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11460 %               images will be written as indexed PNG files even though the
11461 %               PNG grayscale type might be slightly more efficient.  Please
11462 %               note that writing to the PNG8 format may result in loss
11463 %               of color and alpha data.
11464 %
11465 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11466 %               chunk can be present to convey binary transparency by naming
11467 %               one of the colors as transparent.  The only loss incurred
11468 %               is reduction of sample depth to 8.  If the image has more
11469 %               than one transparent color, has semitransparent pixels, or
11470 %               has an opaque pixel with the same RGB components as the
11471 %               transparent color, an image is not written.
11472 %
11473 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
11474 %               transparency is permitted, i.e., the alpha sample for
11475 %               each pixel can have any value from 0 to 255. The alpha
11476 %               channel is present even if the image is fully opaque.
11477 %               The only loss in data is the reduction of the sample depth
11478 %               to 8.
11479 %
11480 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
11481 %               chunk can be present to convey binary transparency by naming
11482 %               one of the colors as transparent.  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 PNG64:   A 16-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 65535. The alpha
11490 %               channel is present even if the image is fully opaque.
11491 %
11492 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
11493 %               image, if the input was a PNG, is written.  If these values
11494 %               cannot be found, then "PNG00" falls back to the regular "PNG"
11495 %               format.
11496 %
11497 %    o -define: For more precise control of the PNG output, you can use the
11498 %               Image options "png:bit-depth" and "png:color-type".  These
11499 %               can be set from the commandline with "-define" and also
11500 %               from the application programming interfaces.  The options
11501 %               are case-independent and are converted to lowercase before
11502 %               being passed to this encoder.
11503 %
11504 %               png:color-type can be 0, 2, 3, 4, or 6.
11505 %
11506 %               When png:color-type is 0 (Grayscale), png:bit-depth can
11507 %               be 1, 2, 4, 8, or 16.
11508 %
11509 %               When png:color-type is 2 (RGB), png:bit-depth can
11510 %               be 8 or 16.
11511 %
11512 %               When png:color-type is 3 (Indexed), png:bit-depth can
11513 %               be 1, 2, 4, or 8.  This refers to the number of bits
11514 %               used to store the index.  The color samples always have
11515 %               bit-depth 8 in indexed PNG files.
11516 %
11517 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11518 %               png:bit-depth can be 8 or 16.
11519 %
11520 %               If the image cannot be written without loss with the
11521 %               requested bit-depth and color-type, a PNG file will not
11522 %               be written, a warning will be issued, and the encoder will
11523 %               return MagickFalse.
11524 %
11525 %  Since image encoders should not be responsible for the "heavy lifting",
11526 %  the user should make sure that ImageMagick has already reduced the
11527 %  image depth and number of colors and limit transparency to binary
11528 %  transparency prior to attempting to write the image with depth, color,
11529 %  or transparency limitations.
11530 %
11531 %  Note that another definition, "png:bit-depth-written" exists, but it
11532 %  is not intended for external use.  It is only used internally by the
11533 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11534 %
11535 %  It is possible to request that the PNG encoder write previously-formatted
11536 %  ancillary chunks in the output PNG file, using the "-profile" commandline
11537 %  option as shown below or by setting the profile via a programming
11538 %  interface:
11539 %
11540 %     -profile PNG-chunk-x:<file>
11541 %
11542 %  where x is a location flag and <file> is a file containing the chunk
11543 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11544 %  This encoder will compute the chunk length and CRC, so those must not
11545 %  be included in the file.
11546 %
11547 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11548 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
11549 %  of the same type, then add a short unique string after the "x" to prevent
11550 %  subsequent profiles from overwriting the preceding ones, e.g.,
11551 %
11552 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11553 %
11554 %  As of version 6.6.6 the following optimizations are always done:
11555 %
11556 %   o  32-bit depth is reduced to 16.
11557 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
11558 %      high byte and low byte are identical.
11559 %   o  Palette is sorted to remove unused entries and to put a
11560 %      transparent color first, if BUILD_PNG_PALETTE is defined.
11561 %   o  Opaque matte channel is removed when writing an indexed PNG.
11562 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
11563 %      this can be done without loss and a larger bit depth N was not
11564 %      requested via the "-define png:bit-depth=N" option.
11565 %   o  If matte channel is present but only one transparent color is
11566 %      present, RGB+tRNS is written instead of RGBA
11567 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
11568 %      was requested when converting an opaque image).
11569 %
11570 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11571 */
11572 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11573   Image *image,ExceptionInfo *exception)
11574 {
11575   MagickBooleanType
11576     excluding,
11577     logging,
11578     have_mng_structure,
11579     status;
11580
11581   MngInfo
11582     *mng_info;
11583
11584   const char
11585     *value;
11586
11587   int
11588     source;
11589
11590   /*
11591     Open image file.
11592   */
11593   assert(image_info != (const ImageInfo *) NULL);
11594   assert(image_info->signature == MagickSignature);
11595   assert(image != (Image *) NULL);
11596   assert(image->signature == MagickSignature);
11597   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11598   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11599   /*
11600     Allocate a MngInfo structure.
11601   */
11602   have_mng_structure=MagickFalse;
11603   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11604
11605   if (mng_info == (MngInfo *) NULL)
11606     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11607
11608   /*
11609     Initialize members of the MngInfo structure.
11610   */
11611   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11612   mng_info->image=image;
11613   mng_info->equal_backgrounds=MagickTrue;
11614   have_mng_structure=MagickTrue;
11615
11616   /* See if user has requested a specific PNG subformat */
11617
11618   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11619   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11620   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11621   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11622   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11623
11624   value=GetImageOption(image_info,"png:format");
11625   if (value == (char *) NULL)
11626     if (LocaleCompare(image_info->magick,"PNG00") == 0)
11627
11628   if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
11629     {
11630       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11631          "  Format=%s",value);
11632
11633       mng_info->write_png8 = MagickFalse;
11634       mng_info->write_png24 = MagickFalse;
11635       mng_info->write_png32 = MagickFalse;
11636       mng_info->write_png48 = MagickFalse;
11637       mng_info->write_png64 = MagickFalse;
11638
11639       if (LocaleCompare(value,"png8") == 0)
11640         mng_info->write_png8 = MagickTrue;
11641
11642       else if (LocaleCompare(value,"png24") == 0)
11643         mng_info->write_png24 = MagickTrue;
11644
11645       else if (LocaleCompare(value,"png32") == 0)
11646         mng_info->write_png32 = MagickTrue;
11647
11648       else if (LocaleCompare(value,"png48") == 0)
11649         mng_info->write_png48 = MagickTrue;
11650
11651       else if (LocaleCompare(value,"png64") == 0)
11652         mng_info->write_png64 = MagickTrue;
11653
11654       else if ((LocaleCompare(value,"png00") == 0) ||
11655          LocaleCompare(image_info->magick,"PNG00") == 0)
11656         {
11657           /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11658           value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11659     
11660           if (value != (char *) NULL)
11661             {
11662               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11663                  "  png00 inherited bit depth=%s",value);
11664     
11665               if (LocaleCompare(value,"1") == 0)
11666                 mng_info->write_png_depth = 1;
11667     
11668               else if (LocaleCompare(value,"2") == 0)
11669                 mng_info->write_png_depth = 2;
11670     
11671               else if (LocaleCompare(value,"4") == 0)
11672                 mng_info->write_png_depth = 4;
11673     
11674               else if (LocaleCompare(value,"8") == 0)
11675                 mng_info->write_png_depth = 8;
11676     
11677               else if (LocaleCompare(value,"16") == 0)
11678                 mng_info->write_png_depth = 16;
11679             }
11680     
11681           value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11682     
11683           if (value != (char *) NULL)
11684             {
11685               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11686                  "  png00 inherited color type=%s",value);
11687     
11688               if (LocaleCompare(value,"0") == 0)
11689                 mng_info->write_png_colortype = 1;
11690     
11691               else if (LocaleCompare(value,"2") == 0)
11692                 mng_info->write_png_colortype = 3;
11693     
11694               else if (LocaleCompare(value,"3") == 0)
11695                 mng_info->write_png_colortype = 4;
11696     
11697               else if (LocaleCompare(value,"4") == 0)
11698                 mng_info->write_png_colortype = 5;
11699     
11700               else if (LocaleCompare(value,"6") == 0)
11701                 mng_info->write_png_colortype = 7;
11702             }
11703         }
11704     }
11705
11706   if (mng_info->write_png8)
11707     {
11708       mng_info->write_png_colortype = /* 3 */ 4;
11709       mng_info->write_png_depth = 8;
11710       image->depth = 8;
11711     }
11712
11713   if (mng_info->write_png24)
11714     {
11715       mng_info->write_png_colortype = /* 2 */ 3;
11716       mng_info->write_png_depth = 8;
11717       image->depth = 8;
11718
11719       if (image->alpha_trait == BlendPixelTrait)
11720         (void) SetImageType(image,TrueColorMatteType,exception);
11721
11722       else
11723         (void) SetImageType(image,TrueColorType,exception);
11724
11725       (void) SyncImage(image,exception);
11726     }
11727
11728   if (mng_info->write_png32)
11729     {
11730       mng_info->write_png_colortype = /* 6 */  7;
11731       mng_info->write_png_depth = 8;
11732       image->depth = 8;
11733       image->alpha_trait = BlendPixelTrait;
11734
11735       (void) SetImageType(image,TrueColorMatteType,exception);
11736       (void) SyncImage(image,exception);
11737     }
11738
11739   if (mng_info->write_png48)
11740     {
11741       mng_info->write_png_colortype = /* 2 */ 3;
11742       mng_info->write_png_depth = 16;
11743       image->depth = 16;
11744
11745       if (image->alpha_trait == BlendPixelTrait)
11746         (void) SetImageType(image,TrueColorMatteType,exception);
11747
11748       else
11749         (void) SetImageType(image,TrueColorType,exception);
11750
11751       (void) SyncImage(image,exception);
11752     }
11753
11754   if (mng_info->write_png64)
11755     {
11756       mng_info->write_png_colortype = /* 6 */  7;
11757       mng_info->write_png_depth = 16;
11758       image->depth = 16;
11759       image->alpha_trait = BlendPixelTrait;
11760
11761       (void) SetImageType(image,TrueColorMatteType,exception);
11762       (void) SyncImage(image,exception);
11763     }
11764
11765   value=GetImageOption(image_info,"png:bit-depth");
11766
11767   if (value != (char *) NULL)
11768     {
11769       if (LocaleCompare(value,"1") == 0)
11770         mng_info->write_png_depth = 1;
11771
11772       else if (LocaleCompare(value,"2") == 0)
11773         mng_info->write_png_depth = 2;
11774
11775       else if (LocaleCompare(value,"4") == 0)
11776         mng_info->write_png_depth = 4;
11777
11778       else if (LocaleCompare(value,"8") == 0)
11779         mng_info->write_png_depth = 8;
11780
11781       else if (LocaleCompare(value,"16") == 0)
11782         mng_info->write_png_depth = 16;
11783
11784       else
11785         (void) ThrowMagickException(exception,
11786              GetMagickModule(),CoderWarning,
11787              "ignoring invalid defined png:bit-depth",
11788              "=%s",value);
11789
11790       if (logging != MagickFalse)
11791         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11792           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11793     }
11794
11795   value=GetImageOption(image_info,"png:color-type");
11796
11797   if (value != (char *) NULL)
11798     {
11799       /* We must store colortype+1 because 0 is a valid colortype */
11800       if (LocaleCompare(value,"0") == 0)
11801         mng_info->write_png_colortype = 1;
11802
11803       else if (LocaleCompare(value,"1") == 0)
11804         mng_info->write_png_colortype = 2;
11805
11806       else if (LocaleCompare(value,"2") == 0)
11807         mng_info->write_png_colortype = 3;
11808
11809       else if (LocaleCompare(value,"3") == 0)
11810         mng_info->write_png_colortype = 4;
11811
11812       else if (LocaleCompare(value,"4") == 0)
11813         mng_info->write_png_colortype = 5;
11814
11815       else if (LocaleCompare(value,"6") == 0)
11816         mng_info->write_png_colortype = 7;
11817
11818       else
11819         (void) ThrowMagickException(exception,
11820              GetMagickModule(),CoderWarning,
11821              "ignoring invalid defined png:color-type",
11822              "=%s",value);
11823
11824       if (logging != MagickFalse)
11825         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11826           "  png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11827     }
11828
11829   /* Check for chunks to be excluded:
11830    *
11831    * The default is to not exclude any known chunks except for any
11832    * listed in the "unused_chunks" array, above.
11833    *
11834    * Chunks can be listed for exclusion via a "png:exclude-chunk"
11835    * define (in the image properties or in the image artifacts)
11836    * or via a mng_info member.  For convenience, in addition
11837    * to or instead of a comma-separated list of chunks, the
11838    * "exclude-chunk" string can be simply "all" or "none".
11839    *
11840    * The exclude-chunk define takes priority over the mng_info.
11841    *
11842    * A "png:include-chunk" define takes  priority over both the
11843    * mng_info and the "png:exclude-chunk" define.  Like the
11844    * "exclude-chunk" string, it can define "all" or "none" as
11845    * well as a comma-separated list.  Chunks that are unknown to
11846    * ImageMagick are always excluded, regardless of their "copy-safe"
11847    * status according to the PNG specification, and even if they
11848    * appear in the "include-chunk" list. Such defines appearing among
11849    * the image options take priority over those found among the image
11850    * artifacts.
11851    *
11852    * Finally, all chunks listed in the "unused_chunks" array are
11853    * automatically excluded, regardless of the other instructions
11854    * or lack thereof.
11855    *
11856    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11857    * will not be written and the gAMA chunk will only be written if it
11858    * is not between .45 and .46, or approximately (1.0/2.2).
11859    *
11860    * If you exclude tRNS and the image has transparency, the colortype
11861    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11862    *
11863    * The -strip option causes StripImage() to set the png:include-chunk
11864    * artifact to "none,trns,gama".
11865    */
11866
11867   mng_info->ping_exclude_bKGD=MagickFalse;
11868   mng_info->ping_exclude_cHRM=MagickFalse;
11869   mng_info->ping_exclude_date=MagickFalse;
11870   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11871   mng_info->ping_exclude_gAMA=MagickFalse;
11872   mng_info->ping_exclude_iCCP=MagickFalse;
11873   /* mng_info->ping_exclude_iTXt=MagickFalse; */
11874   mng_info->ping_exclude_oFFs=MagickFalse;
11875   mng_info->ping_exclude_pHYs=MagickFalse;
11876   mng_info->ping_exclude_sRGB=MagickFalse;
11877   mng_info->ping_exclude_tEXt=MagickFalse;
11878   mng_info->ping_exclude_tRNS=MagickFalse;
11879   mng_info->ping_exclude_vpAg=MagickFalse;
11880   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11881   mng_info->ping_exclude_zTXt=MagickFalse;
11882
11883   mng_info->ping_preserve_colormap=MagickFalse;
11884
11885   value=GetImageOption(image_info,"png:preserve-colormap");
11886   if (value == NULL)
11887      value=GetImageArtifact(image,"png:preserve-colormap");
11888   if (value != NULL)
11889      mng_info->ping_preserve_colormap=MagickTrue;
11890
11891   mng_info->ping_preserve_iCCP=MagickFalse;
11892
11893   value=GetImageOption(image_info,"png:preserve-iCCP");
11894   if (value == NULL)
11895      value=GetImageArtifact(image,"png:preserve-iCCP");
11896   if (value != NULL)
11897      mng_info->ping_preserve_iCCP=MagickTrue;
11898
11899   /* These compression-level, compression-strategy, and compression-filter
11900    * defines take precedence over values from the -quality option.
11901    */
11902   value=GetImageOption(image_info,"png:compression-level");
11903   if (value == NULL)
11904      value=GetImageArtifact(image,"png:compression-level");
11905   if (value != NULL)
11906   {
11907       /* We have to add 1 to everything because 0 is a valid input,
11908        * and we want to use 0 (the default) to mean undefined.
11909        */
11910       if (LocaleCompare(value,"0") == 0)
11911         mng_info->write_png_compression_level = 1;
11912
11913       else if (LocaleCompare(value,"1") == 0)
11914         mng_info->write_png_compression_level = 2;
11915
11916       else if (LocaleCompare(value,"2") == 0)
11917         mng_info->write_png_compression_level = 3;
11918
11919       else if (LocaleCompare(value,"3") == 0)
11920         mng_info->write_png_compression_level = 4;
11921
11922       else if (LocaleCompare(value,"4") == 0)
11923         mng_info->write_png_compression_level = 5;
11924
11925       else if (LocaleCompare(value,"5") == 0)
11926         mng_info->write_png_compression_level = 6;
11927
11928       else if (LocaleCompare(value,"6") == 0)
11929         mng_info->write_png_compression_level = 7;
11930
11931       else if (LocaleCompare(value,"7") == 0)
11932         mng_info->write_png_compression_level = 8;
11933
11934       else if (LocaleCompare(value,"8") == 0)
11935         mng_info->write_png_compression_level = 9;
11936
11937       else if (LocaleCompare(value,"9") == 0)
11938         mng_info->write_png_compression_level = 10;
11939
11940       else
11941         (void) ThrowMagickException(exception,
11942              GetMagickModule(),CoderWarning,
11943              "ignoring invalid defined png:compression-level",
11944              "=%s",value);
11945     }
11946
11947   value=GetImageOption(image_info,"png:compression-strategy");
11948   if (value == NULL)
11949      value=GetImageArtifact(image,"png:compression-strategy");
11950   if (value != NULL)
11951   {
11952       if (LocaleCompare(value,"0") == 0)
11953         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11954
11955       else if (LocaleCompare(value,"1") == 0)
11956         mng_info->write_png_compression_strategy = Z_FILTERED+1;
11957
11958       else if (LocaleCompare(value,"2") == 0)
11959         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11960
11961       else if (LocaleCompare(value,"3") == 0)
11962 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
11963         mng_info->write_png_compression_strategy = Z_RLE+1;
11964 #else
11965         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11966 #endif
11967
11968       else if (LocaleCompare(value,"4") == 0)
11969 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
11970         mng_info->write_png_compression_strategy = Z_FIXED+1;
11971 #else
11972         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11973 #endif
11974
11975       else
11976         (void) ThrowMagickException(exception,
11977              GetMagickModule(),CoderWarning,
11978              "ignoring invalid defined png:compression-strategy",
11979              "=%s",value);
11980     }
11981
11982   value=GetImageOption(image_info,"png:compression-filter");
11983   if (value == NULL)
11984      value=GetImageArtifact(image,"png:compression-filter");
11985   if (value != NULL)
11986   {
11987       /* To do: combinations of filters allowed by libpng
11988        * masks 0x08 through 0xf8
11989        *
11990        * Implement this as a comma-separated list of 0,1,2,3,4,5
11991        * where 5 is a special case meaning PNG_ALL_FILTERS.
11992        */
11993
11994       if (LocaleCompare(value,"0") == 0)
11995         mng_info->write_png_compression_filter = 1;
11996
11997       else if (LocaleCompare(value,"1") == 0)
11998         mng_info->write_png_compression_filter = 2;
11999
12000       else if (LocaleCompare(value,"2") == 0)
12001         mng_info->write_png_compression_filter = 3;
12002
12003       else if (LocaleCompare(value,"3") == 0)
12004         mng_info->write_png_compression_filter = 4;
12005
12006       else if (LocaleCompare(value,"4") == 0)
12007         mng_info->write_png_compression_filter = 5;
12008
12009       else if (LocaleCompare(value,"5") == 0)
12010         mng_info->write_png_compression_filter = 6;
12011
12012       else
12013         (void) ThrowMagickException(exception,
12014              GetMagickModule(),CoderWarning,
12015              "ignoring invalid defined png:compression-filter",
12016              "=%s",value);
12017   }
12018
12019   for (source=0; source<8; source++)
12020   {
12021     value = NULL;
12022
12023     if (source == 0)
12024       value=GetImageOption(image_info,"png:exclude-chunks");
12025
12026     if (source == 1)
12027       value=GetImageArtifact(image,"png:exclude-chunks");
12028
12029     if (source == 2)
12030       value=GetImageOption(image_info,"png:exclude-chunk");
12031
12032     if (source == 3)
12033       value=GetImageArtifact(image,"png:exclude-chunk");
12034
12035     if (source == 4)
12036       value=GetImageOption(image_info,"png:include-chunks");
12037
12038     if (source == 5)
12039       value=GetImageArtifact(image,"png:include-chunks");
12040
12041     if (source == 6)
12042       value=GetImageOption(image_info,"png:include-chunk");
12043
12044     if (source == 7)
12045       value=GetImageArtifact(image,"png:include-chunk");
12046
12047     if (value == NULL)
12048        continue;
12049
12050     if (source < 4)
12051       excluding = MagickTrue;
12052     else
12053       excluding = MagickFalse;
12054
12055     if (logging != MagickFalse)
12056       {
12057         if (source == 0 || source == 2)
12058            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12059               "  png:exclude-chunk=%s found in image options.\n", value);
12060         else if (source == 1 || source == 3)
12061            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12062               "  png:exclude-chunk=%s found in image artifacts.\n", value);
12063         else if (source == 4 || source == 6)
12064            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12065               "  png:include-chunk=%s found in image options.\n", value);
12066         else /* if (source == 5 || source == 7) */
12067            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12068               "  png:include-chunk=%s found in image artifacts.\n", value);
12069       }
12070
12071     if (IsOptionMember("all",value) != MagickFalse)
12072       {
12073         mng_info->ping_exclude_bKGD=excluding;
12074         mng_info->ping_exclude_cHRM=excluding;
12075         mng_info->ping_exclude_date=excluding;
12076         mng_info->ping_exclude_EXIF=excluding;
12077         mng_info->ping_exclude_gAMA=excluding;
12078         mng_info->ping_exclude_iCCP=excluding;
12079         /* mng_info->ping_exclude_iTXt=excluding; */
12080         mng_info->ping_exclude_oFFs=excluding;
12081         mng_info->ping_exclude_pHYs=excluding;
12082         mng_info->ping_exclude_sRGB=excluding;
12083         mng_info->ping_exclude_tEXt=excluding;
12084         mng_info->ping_exclude_tRNS=excluding;
12085         mng_info->ping_exclude_vpAg=excluding;
12086         mng_info->ping_exclude_zCCP=excluding;
12087         mng_info->ping_exclude_zTXt=excluding;
12088       }
12089
12090     if (IsOptionMember("none",value) != MagickFalse)
12091       {
12092         mng_info->ping_exclude_bKGD=!excluding;
12093         mng_info->ping_exclude_cHRM=!excluding;
12094         mng_info->ping_exclude_date=!excluding;
12095         mng_info->ping_exclude_EXIF=!excluding;
12096         mng_info->ping_exclude_gAMA=!excluding;
12097         mng_info->ping_exclude_iCCP=!excluding;
12098         /* mng_info->ping_exclude_iTXt=!excluding; */
12099         mng_info->ping_exclude_oFFs=!excluding;
12100         mng_info->ping_exclude_pHYs=!excluding;
12101         mng_info->ping_exclude_sRGB=!excluding;
12102         mng_info->ping_exclude_tEXt=!excluding;
12103         mng_info->ping_exclude_tRNS=!excluding;
12104         mng_info->ping_exclude_vpAg=!excluding;
12105         mng_info->ping_exclude_zCCP=!excluding;
12106         mng_info->ping_exclude_zTXt=!excluding;
12107       }
12108
12109     if (IsOptionMember("bkgd",value) != MagickFalse)
12110       mng_info->ping_exclude_bKGD=excluding;
12111
12112     if (IsOptionMember("chrm",value) != MagickFalse)
12113       mng_info->ping_exclude_cHRM=excluding;
12114
12115     if (IsOptionMember("date",value) != MagickFalse)
12116       mng_info->ping_exclude_date=excluding;
12117
12118     if (IsOptionMember("exif",value) != MagickFalse)
12119       mng_info->ping_exclude_EXIF=excluding;
12120
12121     if (IsOptionMember("gama",value) != MagickFalse)
12122       mng_info->ping_exclude_gAMA=excluding;
12123
12124     if (IsOptionMember("iccp",value) != MagickFalse)
12125       mng_info->ping_exclude_iCCP=excluding;
12126
12127 #if 0
12128     if (IsOptionMember("itxt",value) != MagickFalse)
12129       mng_info->ping_exclude_iTXt=excluding;
12130 #endif
12131
12132     if (IsOptionMember("offs",value) != MagickFalse)
12133       mng_info->ping_exclude_oFFs=excluding;
12134
12135     if (IsOptionMember("phys",value) != MagickFalse)
12136       mng_info->ping_exclude_pHYs=excluding;
12137
12138     if (IsOptionMember("srgb",value) != MagickFalse)
12139       mng_info->ping_exclude_sRGB=excluding;
12140
12141     if (IsOptionMember("text",value) != MagickFalse)
12142       mng_info->ping_exclude_tEXt=excluding;
12143
12144     if (IsOptionMember("trns",value) != MagickFalse)
12145       mng_info->ping_exclude_tRNS=excluding;
12146
12147     if (IsOptionMember("vpag",value) != MagickFalse)
12148       mng_info->ping_exclude_vpAg=excluding;
12149
12150     if (IsOptionMember("zccp",value) != MagickFalse)
12151       mng_info->ping_exclude_zCCP=excluding;
12152
12153     if (IsOptionMember("ztxt",value) != MagickFalse)
12154       mng_info->ping_exclude_zTXt=excluding;
12155   }
12156
12157   if (logging != MagickFalse)
12158   {
12159     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12160       "  Chunks to be excluded from the output png:");
12161     if (mng_info->ping_exclude_bKGD != MagickFalse)
12162       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12163           "    bKGD");
12164     if (mng_info->ping_exclude_cHRM != MagickFalse)
12165       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12166           "    cHRM");
12167     if (mng_info->ping_exclude_date != MagickFalse)
12168       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12169           "    date");
12170     if (mng_info->ping_exclude_EXIF != MagickFalse)
12171       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12172           "    EXIF");
12173     if (mng_info->ping_exclude_gAMA != MagickFalse)
12174       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12175           "    gAMA");
12176     if (mng_info->ping_exclude_iCCP != MagickFalse)
12177       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12178           "    iCCP");
12179 #if 0
12180     if (mng_info->ping_exclude_iTXt != MagickFalse)
12181       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12182           "    iTXt");
12183 #endif
12184
12185     if (mng_info->ping_exclude_oFFs != MagickFalse)
12186       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12187           "    oFFs");
12188     if (mng_info->ping_exclude_pHYs != MagickFalse)
12189       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12190           "    pHYs");
12191     if (mng_info->ping_exclude_sRGB != MagickFalse)
12192       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12193           "    sRGB");
12194     if (mng_info->ping_exclude_tEXt != MagickFalse)
12195       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12196           "    tEXt");
12197     if (mng_info->ping_exclude_tRNS != MagickFalse)
12198       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12199           "    tRNS");
12200     if (mng_info->ping_exclude_vpAg != MagickFalse)
12201       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12202           "    vpAg");
12203     if (mng_info->ping_exclude_zCCP != MagickFalse)
12204       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12205           "    zCCP");
12206     if (mng_info->ping_exclude_zTXt != MagickFalse)
12207       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12208           "    zTXt");
12209   }
12210
12211   mng_info->need_blob = MagickTrue;
12212
12213   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12214
12215   MngInfoFreeStruct(mng_info,&have_mng_structure);
12216
12217   if (logging != MagickFalse)
12218     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12219
12220   return(status);
12221 }
12222
12223 #if defined(JNG_SUPPORTED)
12224
12225 /* Write one JNG image */
12226 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12227    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12228 {
12229   Image
12230     *jpeg_image;
12231
12232   ImageInfo
12233     *jpeg_image_info;
12234
12235   MagickBooleanType
12236     logging,
12237     status;
12238
12239   size_t
12240     length;
12241
12242   unsigned char
12243     *blob,
12244     chunk[80],
12245     *p;
12246
12247   unsigned int
12248     jng_alpha_compression_method,
12249     jng_alpha_sample_depth,
12250     jng_color_type,
12251     transparent;
12252
12253   size_t
12254     jng_alpha_quality,
12255     jng_quality;
12256
12257   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12258     "  Enter WriteOneJNGImage()");
12259
12260   blob=(unsigned char *) NULL;
12261   jpeg_image=(Image *) NULL;
12262   jpeg_image_info=(ImageInfo *) NULL;
12263
12264   status=MagickTrue;
12265   transparent=image_info->type==GrayscaleMatteType ||
12266      image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
12267
12268   jng_alpha_sample_depth = 0;
12269
12270   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12271
12272   jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12273
12274   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12275       image_info->quality;
12276
12277   if (jng_alpha_quality >= 1000)
12278     jng_alpha_quality /= 1000;
12279
12280   length=0;
12281
12282   if (transparent)
12283     {
12284       jng_color_type=14;
12285
12286       /* Create JPEG blob, image, and image_info */
12287       if (logging != MagickFalse)
12288         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12289           "  Creating jpeg_image_info for alpha.");
12290
12291       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12292
12293       if (jpeg_image_info == (ImageInfo *) NULL)
12294         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12295
12296       if (logging != MagickFalse)
12297         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12298           "  Creating jpeg_image.");
12299
12300       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12301       if (jpeg_image == (Image *) NULL)
12302         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12303       (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12304       jpeg_image->alpha_trait=UndefinedPixelTrait;
12305       jpeg_image->quality=jng_alpha_quality;
12306       jpeg_image_info->type=GrayscaleType;
12307       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12308       (void) AcquireUniqueFilename(jpeg_image->filename);
12309       (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
12310         "%s",jpeg_image->filename);
12311     }
12312   else
12313     {
12314       jng_alpha_compression_method=0;
12315       jng_color_type=10;
12316       jng_alpha_sample_depth=0;
12317     }
12318
12319   /* To do: check bit depth of PNG alpha channel */
12320
12321   /* Check if image is grayscale. */
12322   if (image_info->type != TrueColorMatteType && image_info->type !=
12323     TrueColorType && IsImageGray(image,exception))
12324     jng_color_type-=2;
12325
12326   if (logging != MagickFalse)
12327     {
12328         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12329           "    JNG Quality           = %d",(int) jng_quality);
12330         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12331           "    JNG Color Type        = %d",jng_color_type);
12332         if (transparent)
12333           {
12334             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12335               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12336             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12337               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12338             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12339               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12340           }
12341     }
12342
12343   if (transparent)
12344     {
12345       if (jng_alpha_compression_method==0)
12346         {
12347           const char
12348             *value;
12349
12350           /* Encode alpha as a grayscale PNG blob */
12351           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12352             exception);
12353           if (logging != MagickFalse)
12354             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12355               "  Creating PNG blob.");
12356
12357           (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12358           (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12359           jpeg_image_info->interlace=NoInterlace;
12360
12361           /* Exclude all ancillary chunks */
12362           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12363
12364           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12365             exception);
12366
12367           /* Retrieve sample depth used */
12368           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12369           if (value != (char *) NULL)
12370             jng_alpha_sample_depth= (unsigned int) value[0];
12371         }
12372       else
12373         {
12374           /* Encode alpha as a grayscale JPEG blob */
12375
12376           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12377             exception);
12378
12379           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12380           (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12381           jpeg_image_info->interlace=NoInterlace;
12382           if (logging != MagickFalse)
12383             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12384               "  Creating blob.");
12385           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12386            exception);
12387           jng_alpha_sample_depth=8;
12388
12389           if (logging != MagickFalse)
12390             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12391               "  Successfully read jpeg_image into a blob, length=%.20g.",
12392               (double) length);
12393
12394         }
12395       /* Destroy JPEG image and image_info */
12396       jpeg_image=DestroyImage(jpeg_image);
12397       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12398       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12399     }
12400
12401   /* Write JHDR chunk */
12402   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12403   PNGType(chunk,mng_JHDR);
12404   LogPNGChunk(logging,mng_JHDR,16L);
12405   PNGLong(chunk+4,(png_uint_32) image->columns);
12406   PNGLong(chunk+8,(png_uint_32) image->rows);
12407   chunk[12]=jng_color_type;
12408   chunk[13]=8;  /* sample depth */
12409   chunk[14]=8; /*jng_image_compression_method */
12410   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12411   chunk[16]=jng_alpha_sample_depth;
12412   chunk[17]=jng_alpha_compression_method;
12413   chunk[18]=0; /*jng_alpha_filter_method */
12414   chunk[19]=0; /*jng_alpha_interlace_method */
12415   (void) WriteBlob(image,20,chunk);
12416   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12417   if (logging != MagickFalse)
12418     {
12419       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12420         "    JNG width:%15lu",(unsigned long) image->columns);
12421
12422       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12423         "    JNG height:%14lu",(unsigned long) image->rows);
12424
12425       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12426         "    JNG color type:%10d",jng_color_type);
12427
12428       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12429         "    JNG sample depth:%8d",8);
12430
12431       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12432         "    JNG compression:%9d",8);
12433
12434       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12435         "    JNG interlace:%11d",0);
12436
12437       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12438         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
12439
12440       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12441         "    JNG alpha compression:%3d",jng_alpha_compression_method);
12442
12443       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12444         "    JNG alpha filter:%8d",0);
12445
12446       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12447         "    JNG alpha interlace:%5d",0);
12448     }
12449
12450   /* Write any JNG-chunk-b profiles */
12451   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12452
12453   /*
12454      Write leading ancillary chunks
12455   */
12456
12457   if (transparent)
12458   {
12459     /*
12460       Write JNG bKGD chunk
12461     */
12462
12463     unsigned char
12464       blue,
12465       green,
12466       red;
12467
12468     ssize_t
12469       num_bytes;
12470
12471     if (jng_color_type == 8 || jng_color_type == 12)
12472       num_bytes=6L;
12473     else
12474       num_bytes=10L;
12475     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12476     PNGType(chunk,mng_bKGD);
12477     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12478     red=ScaleQuantumToChar(image->background_color.red);
12479     green=ScaleQuantumToChar(image->background_color.green);
12480     blue=ScaleQuantumToChar(image->background_color.blue);
12481     *(chunk+4)=0;
12482     *(chunk+5)=red;
12483     *(chunk+6)=0;
12484     *(chunk+7)=green;
12485     *(chunk+8)=0;
12486     *(chunk+9)=blue;
12487     (void) WriteBlob(image,(size_t) num_bytes,chunk);
12488     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12489   }
12490
12491   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12492     {
12493       /*
12494         Write JNG sRGB chunk
12495       */
12496       (void) WriteBlobMSBULong(image,1L);
12497       PNGType(chunk,mng_sRGB);
12498       LogPNGChunk(logging,mng_sRGB,1L);
12499
12500       if (image->rendering_intent != UndefinedIntent)
12501         chunk[4]=(unsigned char)
12502           Magick_RenderingIntent_to_PNG_RenderingIntent(
12503           (image->rendering_intent));
12504
12505       else
12506         chunk[4]=(unsigned char)
12507           Magick_RenderingIntent_to_PNG_RenderingIntent(
12508           (PerceptualIntent));
12509
12510       (void) WriteBlob(image,5,chunk);
12511       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12512     }
12513   else
12514     {
12515       if (image->gamma != 0.0)
12516         {
12517           /*
12518              Write JNG gAMA chunk
12519           */
12520           (void) WriteBlobMSBULong(image,4L);
12521           PNGType(chunk,mng_gAMA);
12522           LogPNGChunk(logging,mng_gAMA,4L);
12523           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12524           (void) WriteBlob(image,8,chunk);
12525           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12526         }
12527
12528       if ((mng_info->equal_chrms == MagickFalse) &&
12529           (image->chromaticity.red_primary.x != 0.0))
12530         {
12531           PrimaryInfo
12532             primary;
12533
12534           /*
12535              Write JNG cHRM chunk
12536           */
12537           (void) WriteBlobMSBULong(image,32L);
12538           PNGType(chunk,mng_cHRM);
12539           LogPNGChunk(logging,mng_cHRM,32L);
12540           primary=image->chromaticity.white_point;
12541           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12542           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12543           primary=image->chromaticity.red_primary;
12544           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12545           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12546           primary=image->chromaticity.green_primary;
12547           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12548           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12549           primary=image->chromaticity.blue_primary;
12550           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12551           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12552           (void) WriteBlob(image,36,chunk);
12553           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12554         }
12555     }
12556
12557   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12558     {
12559       /*
12560          Write JNG pHYs chunk
12561       */
12562       (void) WriteBlobMSBULong(image,9L);
12563       PNGType(chunk,mng_pHYs);
12564       LogPNGChunk(logging,mng_pHYs,9L);
12565       if (image->units == PixelsPerInchResolution)
12566         {
12567           PNGLong(chunk+4,(png_uint_32)
12568             (image->resolution.x*100.0/2.54+0.5));
12569
12570           PNGLong(chunk+8,(png_uint_32)
12571             (image->resolution.y*100.0/2.54+0.5));
12572
12573           chunk[12]=1;
12574         }
12575
12576       else
12577         {
12578           if (image->units == PixelsPerCentimeterResolution)
12579             {
12580               PNGLong(chunk+4,(png_uint_32)
12581                 (image->resolution.x*100.0+0.5));
12582
12583               PNGLong(chunk+8,(png_uint_32)
12584                 (image->resolution.y*100.0+0.5));
12585
12586               chunk[12]=1;
12587             }
12588
12589           else
12590             {
12591               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12592               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12593               chunk[12]=0;
12594             }
12595         }
12596       (void) WriteBlob(image,13,chunk);
12597       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12598     }
12599
12600   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12601     {
12602       /*
12603          Write JNG oFFs chunk
12604       */
12605       (void) WriteBlobMSBULong(image,9L);
12606       PNGType(chunk,mng_oFFs);
12607       LogPNGChunk(logging,mng_oFFs,9L);
12608       PNGsLong(chunk+4,(ssize_t) (image->page.x));
12609       PNGsLong(chunk+8,(ssize_t) (image->page.y));
12610       chunk[12]=0;
12611       (void) WriteBlob(image,13,chunk);
12612       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12613     }
12614   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12615     {
12616        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
12617        PNGType(chunk,mng_vpAg);
12618        LogPNGChunk(logging,mng_vpAg,9L);
12619        PNGLong(chunk+4,(png_uint_32) image->page.width);
12620        PNGLong(chunk+8,(png_uint_32) image->page.height);
12621        chunk[12]=0;   /* unit = pixels */
12622        (void) WriteBlob(image,13,chunk);
12623        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12624     }
12625
12626
12627   if (transparent)
12628     {
12629       if (jng_alpha_compression_method==0)
12630         {
12631           register ssize_t
12632             i;
12633
12634           size_t
12635             len;
12636
12637           /* Write IDAT chunk header */
12638           if (logging != MagickFalse)
12639             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12640               "  Write IDAT chunks from blob, length=%.20g.",(double)
12641               length);
12642
12643           /* Copy IDAT chunks */
12644           len=0;
12645           p=blob+8;
12646           for (i=8; i<(ssize_t) length; i+=len+12)
12647           {
12648             len=(size_t) (*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12649             p+=4;
12650
12651             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12652               {
12653                 /* Found an IDAT chunk. */
12654                 (void) WriteBlobMSBULong(image,len);
12655                 LogPNGChunk(logging,mng_IDAT,len);
12656                 (void) WriteBlob(image,len+4,p);
12657                 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
12658               }
12659
12660             else
12661               {
12662                 if (logging != MagickFalse)
12663                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12664                     "    Skipping %c%c%c%c chunk, length=%.20g.",
12665                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
12666               }
12667             p+=(8+len);
12668           }
12669         }
12670       else
12671         {
12672           /* Write JDAA chunk header */
12673           if (logging != MagickFalse)
12674             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12675               "  Write JDAA chunk, length=%.20g.",(double) length);
12676           (void) WriteBlobMSBULong(image,(size_t) length);
12677           PNGType(chunk,mng_JDAA);
12678           LogPNGChunk(logging,mng_JDAA,length);
12679           /* Write JDAT chunk(s) data */
12680           (void) WriteBlob(image,4,chunk);
12681           (void) WriteBlob(image,length,blob);
12682           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12683              (uInt) length));
12684         }
12685       blob=(unsigned char *) RelinquishMagickMemory(blob);
12686     }
12687
12688   /* Encode image as a JPEG blob */
12689   if (logging != MagickFalse)
12690     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12691       "  Creating jpeg_image_info.");
12692   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12693   if (jpeg_image_info == (ImageInfo *) NULL)
12694     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12695
12696   if (logging != MagickFalse)
12697     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12698       "  Creating jpeg_image.");
12699
12700   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12701   if (jpeg_image == (Image *) NULL)
12702     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12703   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12704
12705   (void) AcquireUniqueFilename(jpeg_image->filename);
12706   (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12707     jpeg_image->filename);
12708
12709   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12710     exception);
12711
12712   if (logging != MagickFalse)
12713     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12714       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12715       (double) jpeg_image->rows);
12716
12717   if (jng_color_type == 8 || jng_color_type == 12)
12718     jpeg_image_info->type=GrayscaleType;
12719
12720   jpeg_image_info->quality=jng_quality;
12721   jpeg_image->quality=jng_quality;
12722   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12723   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12724
12725   if (logging != MagickFalse)
12726     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12727       "  Creating blob.");
12728
12729   blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12730
12731   if (logging != MagickFalse)
12732     {
12733       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12734         "  Successfully read jpeg_image into a blob, length=%.20g.",
12735         (double) length);
12736
12737       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12738         "  Write JDAT chunk, length=%.20g.",(double) length);
12739     }
12740
12741   /* Write JDAT chunk(s) */
12742   (void) WriteBlobMSBULong(image,(size_t) length);
12743   PNGType(chunk,mng_JDAT);
12744   LogPNGChunk(logging,mng_JDAT,length);
12745   (void) WriteBlob(image,4,chunk);
12746   (void) WriteBlob(image,length,blob);
12747   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12748
12749   jpeg_image=DestroyImage(jpeg_image);
12750   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12751   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12752   blob=(unsigned char *) RelinquishMagickMemory(blob);
12753
12754   /* Write any JNG-chunk-e profiles */
12755   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12756
12757   /* Write IEND chunk */
12758   (void) WriteBlobMSBULong(image,0L);
12759   PNGType(chunk,mng_IEND);
12760   LogPNGChunk(logging,mng_IEND,0);
12761   (void) WriteBlob(image,4,chunk);
12762   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12763
12764   if (logging != MagickFalse)
12765     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12766       "  exit WriteOneJNGImage()");
12767
12768   return(status);
12769 }
12770
12771
12772 /*
12773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12774 %                                                                             %
12775 %                                                                             %
12776 %                                                                             %
12777 %   W r i t e J N G I m a g e                                                 %
12778 %                                                                             %
12779 %                                                                             %
12780 %                                                                             %
12781 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12782 %
12783 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12784 %
12785 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
12786 %
12787 %  The format of the WriteJNGImage method is:
12788 %
12789 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12790 %        Image *image,ExceptionInfo *exception)
12791 %
12792 %  A description of each parameter follows:
12793 %
12794 %    o image_info: the image info.
12795 %
12796 %    o image:  The image.
12797 %
12798 %    o exception: return any errors or warnings in this structure.
12799 %
12800 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12801 */
12802 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12803   ExceptionInfo *exception)
12804 {
12805   MagickBooleanType
12806     have_mng_structure,
12807     logging,
12808     status;
12809
12810   MngInfo
12811     *mng_info;
12812
12813   /*
12814     Open image file.
12815   */
12816   assert(image_info != (const ImageInfo *) NULL);
12817   assert(image_info->signature == MagickSignature);
12818   assert(image != (Image *) NULL);
12819   assert(image->signature == MagickSignature);
12820   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12821   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12822   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12823   if (status == MagickFalse)
12824     return(status);
12825
12826   /*
12827     Allocate a MngInfo structure.
12828   */
12829   have_mng_structure=MagickFalse;
12830   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12831   if (mng_info == (MngInfo *) NULL)
12832     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12833   /*
12834     Initialize members of the MngInfo structure.
12835   */
12836   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12837   mng_info->image=image;
12838   have_mng_structure=MagickTrue;
12839
12840   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12841
12842   status=WriteOneJNGImage(mng_info,image_info,image,exception);
12843   (void) CloseBlob(image);
12844
12845   (void) CatchImageException(image);
12846   MngInfoFreeStruct(mng_info,&have_mng_structure);
12847   if (logging != MagickFalse)
12848     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12849   return(status);
12850 }
12851 #endif
12852
12853 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12854   ExceptionInfo *exception)
12855 {
12856   const char
12857     *option;
12858
12859   Image
12860     *next_image;
12861
12862   MagickBooleanType
12863     have_mng_structure,
12864     status;
12865
12866   volatile MagickBooleanType
12867     logging;
12868
12869   MngInfo
12870     *mng_info;
12871
12872   int
12873     image_count,
12874     need_iterations,
12875     need_matte;
12876
12877   volatile int
12878 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12879     defined(PNG_MNG_FEATURES_SUPPORTED)
12880     need_local_plte,
12881 #endif
12882     all_images_are_gray,
12883     need_defi,
12884     use_global_plte;
12885
12886   register ssize_t
12887     i;
12888
12889   unsigned char
12890     chunk[800];
12891
12892   volatile unsigned int
12893     write_jng,
12894     write_mng;
12895
12896   volatile size_t
12897     scene;
12898
12899   size_t
12900     final_delay=0,
12901     initial_delay;
12902
12903 #if (PNG_LIBPNG_VER < 10200)
12904     if (image_info->verbose)
12905       printf("Your PNG library (libpng-%s) is rather old.\n",
12906          PNG_LIBPNG_VER_STRING);
12907 #endif
12908
12909   /*
12910     Open image file.
12911   */
12912   assert(image_info != (const ImageInfo *) NULL);
12913   assert(image_info->signature == MagickSignature);
12914   assert(image != (Image *) NULL);
12915   assert(image->signature == MagickSignature);
12916   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12917   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12918   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12919   if (status == MagickFalse)
12920     return(status);
12921
12922   /*
12923     Allocate a MngInfo structure.
12924   */
12925   have_mng_structure=MagickFalse;
12926   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12927   if (mng_info == (MngInfo *) NULL)
12928     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12929   /*
12930     Initialize members of the MngInfo structure.
12931   */
12932   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12933   mng_info->image=image;
12934   have_mng_structure=MagickTrue;
12935   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12936
12937   /*
12938    * See if user has requested a specific PNG subformat to be used
12939    * for all of the PNGs in the MNG being written, e.g.,
12940    *
12941    *    convert *.png png8:animation.mng
12942    *
12943    * To do: check -define png:bit_depth and png:color_type as well,
12944    * or perhaps use mng:bit_depth and mng:color_type instead for
12945    * global settings.
12946    */
12947
12948   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12949   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12950   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12951
12952   write_jng=MagickFalse;
12953   if (image_info->compression == JPEGCompression)
12954     write_jng=MagickTrue;
12955
12956   mng_info->adjoin=image_info->adjoin &&
12957     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12958
12959   if (logging != MagickFalse)
12960     {
12961       /* Log some info about the input */
12962       Image
12963         *p;
12964
12965       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12966         "  Checking input image(s)\n"
12967         "    Image_info depth: %.20g,    Type: %d",
12968         (double) image_info->depth, image_info->type);
12969
12970       scene=0;
12971       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12972       {
12973
12974         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12975            "    Scene: %.20g\n,   Image depth: %.20g",
12976            (double) scene++, (double) p->depth);
12977
12978         if (p->alpha_trait == BlendPixelTrait)
12979           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12980             "      Matte: True");
12981
12982         else
12983           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12984             "      Matte: False");
12985
12986         if (p->storage_class == PseudoClass)
12987           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12988             "      Storage class: PseudoClass");
12989
12990         else
12991           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12992             "      Storage class: DirectClass");
12993
12994         if (p->colors)
12995           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12996             "      Number of colors: %.20g",(double) p->colors);
12997
12998         else
12999           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13000             "      Number of colors: unspecified");
13001
13002         if (mng_info->adjoin == MagickFalse)
13003           break;
13004       }
13005     }
13006
13007   use_global_plte=MagickFalse;
13008   all_images_are_gray=MagickFalse;
13009 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13010   need_local_plte=MagickTrue;
13011 #endif
13012   need_defi=MagickFalse;
13013   need_matte=MagickFalse;
13014   mng_info->framing_mode=1;
13015   mng_info->old_framing_mode=1;
13016
13017   if (write_mng)
13018       if (image_info->page != (char *) NULL)
13019         {
13020           /*
13021             Determine image bounding box.
13022           */
13023           SetGeometry(image,&mng_info->page);
13024           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13025             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13026         }
13027   if (write_mng)
13028     {
13029       unsigned int
13030         need_geom;
13031
13032       unsigned short
13033         red,
13034         green,
13035         blue;
13036
13037       mng_info->page=image->page;
13038       need_geom=MagickTrue;
13039       if (mng_info->page.width || mng_info->page.height)
13040          need_geom=MagickFalse;
13041       /*
13042         Check all the scenes.
13043       */
13044       initial_delay=image->delay;
13045       need_iterations=MagickFalse;
13046       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13047       mng_info->equal_physs=MagickTrue,
13048       mng_info->equal_gammas=MagickTrue;
13049       mng_info->equal_srgbs=MagickTrue;
13050       mng_info->equal_backgrounds=MagickTrue;
13051       image_count=0;
13052 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13053     defined(PNG_MNG_FEATURES_SUPPORTED)
13054       all_images_are_gray=MagickTrue;
13055       mng_info->equal_palettes=MagickFalse;
13056       need_local_plte=MagickFalse;
13057 #endif
13058       for (next_image=image; next_image != (Image *) NULL; )
13059       {
13060         if (need_geom)
13061           {
13062             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13063               mng_info->page.width=next_image->columns+next_image->page.x;
13064
13065             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13066               mng_info->page.height=next_image->rows+next_image->page.y;
13067           }
13068
13069         if (next_image->page.x || next_image->page.y)
13070           need_defi=MagickTrue;
13071
13072         if (next_image->alpha_trait == BlendPixelTrait)
13073           need_matte=MagickTrue;
13074
13075         if ((int) next_image->dispose >= BackgroundDispose)
13076           if ((next_image->alpha_trait == BlendPixelTrait) ||
13077                next_image->page.x || next_image->page.y ||
13078               ((next_image->columns < mng_info->page.width) &&
13079                (next_image->rows < mng_info->page.height)))
13080             mng_info->need_fram=MagickTrue;
13081
13082         if (next_image->iterations)
13083           need_iterations=MagickTrue;
13084
13085         final_delay=next_image->delay;
13086
13087         if (final_delay != initial_delay || final_delay > 1UL*
13088            next_image->ticks_per_second)
13089           mng_info->need_fram=1;
13090
13091 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13092     defined(PNG_MNG_FEATURES_SUPPORTED)
13093         /*
13094           check for global palette possibility.
13095         */
13096         if (image->alpha_trait == BlendPixelTrait)
13097            need_local_plte=MagickTrue;
13098
13099         if (need_local_plte == 0)
13100           {
13101             if (IsImageGray(image,exception) == MagickFalse)
13102               all_images_are_gray=MagickFalse;
13103             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13104             if (use_global_plte == 0)
13105               use_global_plte=mng_info->equal_palettes;
13106             need_local_plte=!mng_info->equal_palettes;
13107           }
13108 #endif
13109         if (GetNextImageInList(next_image) != (Image *) NULL)
13110           {
13111             if (next_image->background_color.red !=
13112                 next_image->next->background_color.red ||
13113                 next_image->background_color.green !=
13114                 next_image->next->background_color.green ||
13115                 next_image->background_color.blue !=
13116                 next_image->next->background_color.blue)
13117               mng_info->equal_backgrounds=MagickFalse;
13118
13119             if (next_image->gamma != next_image->next->gamma)
13120               mng_info->equal_gammas=MagickFalse;
13121
13122             if (next_image->rendering_intent !=
13123                 next_image->next->rendering_intent)
13124               mng_info->equal_srgbs=MagickFalse;
13125
13126             if ((next_image->units != next_image->next->units) ||
13127                 (next_image->resolution.x != next_image->next->resolution.x) ||
13128                 (next_image->resolution.y != next_image->next->resolution.y))
13129               mng_info->equal_physs=MagickFalse;
13130
13131             if (mng_info->equal_chrms)
13132               {
13133                 if (next_image->chromaticity.red_primary.x !=
13134                     next_image->next->chromaticity.red_primary.x ||
13135                     next_image->chromaticity.red_primary.y !=
13136                     next_image->next->chromaticity.red_primary.y ||
13137                     next_image->chromaticity.green_primary.x !=
13138                     next_image->next->chromaticity.green_primary.x ||
13139                     next_image->chromaticity.green_primary.y !=
13140                     next_image->next->chromaticity.green_primary.y ||
13141                     next_image->chromaticity.blue_primary.x !=
13142                     next_image->next->chromaticity.blue_primary.x ||
13143                     next_image->chromaticity.blue_primary.y !=
13144                     next_image->next->chromaticity.blue_primary.y ||
13145                     next_image->chromaticity.white_point.x !=
13146                     next_image->next->chromaticity.white_point.x ||
13147                     next_image->chromaticity.white_point.y !=
13148                     next_image->next->chromaticity.white_point.y)
13149                   mng_info->equal_chrms=MagickFalse;
13150               }
13151           }
13152         image_count++;
13153         next_image=GetNextImageInList(next_image);
13154       }
13155       if (image_count < 2)
13156         {
13157           mng_info->equal_backgrounds=MagickFalse;
13158           mng_info->equal_chrms=MagickFalse;
13159           mng_info->equal_gammas=MagickFalse;
13160           mng_info->equal_srgbs=MagickFalse;
13161           mng_info->equal_physs=MagickFalse;
13162           use_global_plte=MagickFalse;
13163 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13164           need_local_plte=MagickTrue;
13165 #endif
13166           need_iterations=MagickFalse;
13167         }
13168
13169      if (mng_info->need_fram == MagickFalse)
13170        {
13171          /*
13172            Only certain framing rates 100/n are exactly representable without
13173            the FRAM chunk but we'll allow some slop in VLC files
13174          */
13175          if (final_delay == 0)
13176            {
13177              if (need_iterations != MagickFalse)
13178                {
13179                  /*
13180                    It's probably a GIF with loop; don't run it *too* fast.
13181                  */
13182                  if (mng_info->adjoin)
13183                    {
13184                      final_delay=10;
13185                      (void) ThrowMagickException(exception,GetMagickModule(),
13186                        CoderWarning,
13187                        "input has zero delay between all frames; assuming",
13188                        " 10 cs `%s'","");
13189                    }
13190                }
13191              else
13192                mng_info->ticks_per_second=0;
13193            }
13194          if (final_delay != 0)
13195            mng_info->ticks_per_second=(png_uint_32)
13196               (image->ticks_per_second/final_delay);
13197          if (final_delay > 50)
13198            mng_info->ticks_per_second=2;
13199
13200          if (final_delay > 75)
13201            mng_info->ticks_per_second=1;
13202
13203          if (final_delay > 125)
13204            mng_info->need_fram=MagickTrue;
13205
13206          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13207             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13208             (final_delay != 25) && (final_delay != 50) && (1UL*final_delay !=
13209                1UL*image->ticks_per_second))
13210            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13211        }
13212
13213      if (mng_info->need_fram != MagickFalse)
13214         mng_info->ticks_per_second=1UL*image->ticks_per_second;
13215      /*
13216         If pseudocolor, we should also check to see if all the
13217         palettes are identical and write a global PLTE if they are.
13218         ../glennrp Feb 99.
13219      */
13220      /*
13221         Write the MNG version 1.0 signature and MHDR chunk.
13222      */
13223      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13224      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13225      PNGType(chunk,mng_MHDR);
13226      LogPNGChunk(logging,mng_MHDR,28L);
13227      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13228      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13229      PNGLong(chunk+12,mng_info->ticks_per_second);
13230      PNGLong(chunk+16,0L);  /* layer count=unknown */
13231      PNGLong(chunk+20,0L);  /* frame count=unknown */
13232      PNGLong(chunk+24,0L);  /* play time=unknown   */
13233      if (write_jng)
13234        {
13235          if (need_matte)
13236            {
13237              if (need_defi || mng_info->need_fram || use_global_plte)
13238                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13239
13240              else
13241                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13242            }
13243
13244          else
13245            {
13246              if (need_defi || mng_info->need_fram || use_global_plte)
13247                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13248
13249              else
13250                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13251            }
13252        }
13253
13254      else
13255        {
13256          if (need_matte)
13257            {
13258              if (need_defi || mng_info->need_fram || use_global_plte)
13259                PNGLong(chunk+28,11L);    /* simplicity=LC */
13260
13261              else
13262                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13263            }
13264
13265          else
13266            {
13267              if (need_defi || mng_info->need_fram || use_global_plte)
13268                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13269
13270              else
13271                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13272            }
13273        }
13274      (void) WriteBlob(image,32,chunk);
13275      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13276      option=GetImageOption(image_info,"mng:need-cacheoff");
13277      if (option != (const char *) NULL)
13278        {
13279          size_t
13280            length;
13281
13282          /*
13283            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13284          */
13285          PNGType(chunk,mng_nEED);
13286          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13287          (void) WriteBlobMSBULong(image,(size_t) length);
13288          LogPNGChunk(logging,mng_nEED,(size_t) length);
13289          length+=4;
13290          (void) WriteBlob(image,length,chunk);
13291          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13292        }
13293      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13294          (GetNextImageInList(image) != (Image *) NULL) &&
13295          (image->iterations != 1))
13296        {
13297          /*
13298            Write MNG TERM chunk
13299          */
13300          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13301          PNGType(chunk,mng_TERM);
13302          LogPNGChunk(logging,mng_TERM,10L);
13303          chunk[4]=3;  /* repeat animation */
13304          chunk[5]=0;  /* show last frame when done */
13305          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13306             final_delay/MagickMax(image->ticks_per_second,1)));
13307
13308          if (image->iterations == 0)
13309            PNGLong(chunk+10,PNG_UINT_31_MAX);
13310
13311          else
13312            PNGLong(chunk+10,(png_uint_32) image->iterations);
13313
13314          if (logging != MagickFalse)
13315            {
13316              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13317                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13318               final_delay/MagickMax(image->ticks_per_second,1)));
13319
13320              if (image->iterations == 0)
13321                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13322                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13323
13324              else
13325                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13326                  "     Image iterations: %.20g",(double) image->iterations);
13327            }
13328          (void) WriteBlob(image,14,chunk);
13329          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13330        }
13331      /*
13332        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13333      */
13334      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13335           mng_info->equal_srgbs)
13336        {
13337          /*
13338            Write MNG sRGB chunk
13339          */
13340          (void) WriteBlobMSBULong(image,1L);
13341          PNGType(chunk,mng_sRGB);
13342          LogPNGChunk(logging,mng_sRGB,1L);
13343
13344          if (image->rendering_intent != UndefinedIntent)
13345            chunk[4]=(unsigned char)
13346              Magick_RenderingIntent_to_PNG_RenderingIntent(
13347              (image->rendering_intent));
13348
13349          else
13350            chunk[4]=(unsigned char)
13351              Magick_RenderingIntent_to_PNG_RenderingIntent(
13352                (PerceptualIntent));
13353
13354          (void) WriteBlob(image,5,chunk);
13355          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13356          mng_info->have_write_global_srgb=MagickTrue;
13357        }
13358
13359      else
13360        {
13361          if (image->gamma && mng_info->equal_gammas)
13362            {
13363              /*
13364                 Write MNG gAMA chunk
13365              */
13366              (void) WriteBlobMSBULong(image,4L);
13367              PNGType(chunk,mng_gAMA);
13368              LogPNGChunk(logging,mng_gAMA,4L);
13369              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13370              (void) WriteBlob(image,8,chunk);
13371              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13372              mng_info->have_write_global_gama=MagickTrue;
13373            }
13374          if (mng_info->equal_chrms)
13375            {
13376              PrimaryInfo
13377                primary;
13378
13379              /*
13380                 Write MNG cHRM chunk
13381              */
13382              (void) WriteBlobMSBULong(image,32L);
13383              PNGType(chunk,mng_cHRM);
13384              LogPNGChunk(logging,mng_cHRM,32L);
13385              primary=image->chromaticity.white_point;
13386              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13387              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13388              primary=image->chromaticity.red_primary;
13389              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13390              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13391              primary=image->chromaticity.green_primary;
13392              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13393              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13394              primary=image->chromaticity.blue_primary;
13395              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13396              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13397              (void) WriteBlob(image,36,chunk);
13398              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13399              mng_info->have_write_global_chrm=MagickTrue;
13400            }
13401        }
13402      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13403        {
13404          /*
13405             Write MNG pHYs chunk
13406          */
13407          (void) WriteBlobMSBULong(image,9L);
13408          PNGType(chunk,mng_pHYs);
13409          LogPNGChunk(logging,mng_pHYs,9L);
13410
13411          if (image->units == PixelsPerInchResolution)
13412            {
13413              PNGLong(chunk+4,(png_uint_32)
13414                (image->resolution.x*100.0/2.54+0.5));
13415
13416              PNGLong(chunk+8,(png_uint_32)
13417                (image->resolution.y*100.0/2.54+0.5));
13418
13419              chunk[12]=1;
13420            }
13421
13422          else
13423            {
13424              if (image->units == PixelsPerCentimeterResolution)
13425                {
13426                  PNGLong(chunk+4,(png_uint_32)
13427                    (image->resolution.x*100.0+0.5));
13428
13429                  PNGLong(chunk+8,(png_uint_32)
13430                    (image->resolution.y*100.0+0.5));
13431
13432                  chunk[12]=1;
13433                }
13434
13435              else
13436                {
13437                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13438                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13439                  chunk[12]=0;
13440                }
13441            }
13442          (void) WriteBlob(image,13,chunk);
13443          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13444        }
13445      /*
13446        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13447        or does not cover the entire frame.
13448      */
13449      if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
13450          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13451          (image->page.width+image->page.x < mng_info->page.width))
13452          || (image->page.height && (image->page.height+image->page.y
13453          < mng_info->page.height))))
13454        {
13455          (void) WriteBlobMSBULong(image,6L);
13456          PNGType(chunk,mng_BACK);
13457          LogPNGChunk(logging,mng_BACK,6L);
13458          red=ScaleQuantumToShort(image->background_color.red);
13459          green=ScaleQuantumToShort(image->background_color.green);
13460          blue=ScaleQuantumToShort(image->background_color.blue);
13461          PNGShort(chunk+4,red);
13462          PNGShort(chunk+6,green);
13463          PNGShort(chunk+8,blue);
13464          (void) WriteBlob(image,10,chunk);
13465          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13466          if (mng_info->equal_backgrounds)
13467            {
13468              (void) WriteBlobMSBULong(image,6L);
13469              PNGType(chunk,mng_bKGD);
13470              LogPNGChunk(logging,mng_bKGD,6L);
13471              (void) WriteBlob(image,10,chunk);
13472              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13473            }
13474        }
13475
13476 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13477      if ((need_local_plte == MagickFalse) &&
13478          (image->storage_class == PseudoClass) &&
13479          (all_images_are_gray == MagickFalse))
13480        {
13481          size_t
13482            data_length;
13483
13484          /*
13485            Write MNG PLTE chunk
13486          */
13487          data_length=3*image->colors;
13488          (void) WriteBlobMSBULong(image,data_length);
13489          PNGType(chunk,mng_PLTE);
13490          LogPNGChunk(logging,mng_PLTE,data_length);
13491
13492          for (i=0; i < (ssize_t) image->colors; i++)
13493          {
13494            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13495              image->colormap[i].red) & 0xff);
13496            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13497              image->colormap[i].green) & 0xff);
13498            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13499              image->colormap[i].blue) & 0xff);
13500          }
13501
13502          (void) WriteBlob(image,data_length+4,chunk);
13503          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13504          mng_info->have_write_global_plte=MagickTrue;
13505        }
13506 #endif
13507     }
13508   scene=0;
13509   mng_info->delay=0;
13510 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13511     defined(PNG_MNG_FEATURES_SUPPORTED)
13512   mng_info->equal_palettes=MagickFalse;
13513 #endif
13514   do
13515   {
13516     if (mng_info->adjoin)
13517     {
13518 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13519     defined(PNG_MNG_FEATURES_SUPPORTED)
13520     /*
13521       If we aren't using a global palette for the entire MNG, check to
13522       see if we can use one for two or more consecutive images.
13523     */
13524     if (need_local_plte && use_global_plte && !all_images_are_gray)
13525       {
13526         if (mng_info->IsPalette)
13527           {
13528             /*
13529               When equal_palettes is true, this image has the same palette
13530               as the previous PseudoClass image
13531             */
13532             mng_info->have_write_global_plte=mng_info->equal_palettes;
13533             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13534             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13535               {
13536                 /*
13537                   Write MNG PLTE chunk
13538                 */
13539                 size_t
13540                   data_length;
13541
13542                 data_length=3*image->colors;
13543                 (void) WriteBlobMSBULong(image,data_length);
13544                 PNGType(chunk,mng_PLTE);
13545                 LogPNGChunk(logging,mng_PLTE,data_length);
13546
13547                 for (i=0; i < (ssize_t) image->colors; i++)
13548                 {
13549                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13550                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13551                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13552                 }
13553
13554                 (void) WriteBlob(image,data_length+4,chunk);
13555                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13556                    (uInt) (data_length+4)));
13557                 mng_info->have_write_global_plte=MagickTrue;
13558               }
13559           }
13560         else
13561           mng_info->have_write_global_plte=MagickFalse;
13562       }
13563 #endif
13564     if (need_defi)
13565       {
13566         ssize_t
13567           previous_x,
13568           previous_y;
13569
13570         if (scene)
13571           {
13572             previous_x=mng_info->page.x;
13573             previous_y=mng_info->page.y;
13574           }
13575         else
13576           {
13577             previous_x=0;
13578             previous_y=0;
13579           }
13580         mng_info->page=image->page;
13581         if ((mng_info->page.x !=  previous_x) ||
13582             (mng_info->page.y != previous_y))
13583           {
13584              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
13585              PNGType(chunk,mng_DEFI);
13586              LogPNGChunk(logging,mng_DEFI,12L);
13587              chunk[4]=0; /* object 0 MSB */
13588              chunk[5]=0; /* object 0 LSB */
13589              chunk[6]=0; /* visible  */
13590              chunk[7]=0; /* abstract */
13591              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13592              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13593              (void) WriteBlob(image,16,chunk);
13594              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13595           }
13596       }
13597     }
13598
13599    mng_info->write_mng=write_mng;
13600
13601    if ((int) image->dispose >= 3)
13602      mng_info->framing_mode=3;
13603
13604    if (mng_info->need_fram && mng_info->adjoin &&
13605        ((image->delay != mng_info->delay) ||
13606         (mng_info->framing_mode != mng_info->old_framing_mode)))
13607      {
13608        if (image->delay == mng_info->delay)
13609          {
13610            /*
13611              Write a MNG FRAM chunk with the new framing mode.
13612            */
13613            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
13614            PNGType(chunk,mng_FRAM);
13615            LogPNGChunk(logging,mng_FRAM,1L);
13616            chunk[4]=(unsigned char) mng_info->framing_mode;
13617            (void) WriteBlob(image,5,chunk);
13618            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13619          }
13620        else
13621          {
13622            /*
13623              Write a MNG FRAM chunk with the delay.
13624            */
13625            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13626            PNGType(chunk,mng_FRAM);
13627            LogPNGChunk(logging,mng_FRAM,10L);
13628            chunk[4]=(unsigned char) mng_info->framing_mode;
13629            chunk[5]=0;  /* frame name separator (no name) */
13630            chunk[6]=2;  /* flag for changing default delay */
13631            chunk[7]=0;  /* flag for changing frame timeout */
13632            chunk[8]=0;  /* flag for changing frame clipping */
13633            chunk[9]=0;  /* flag for changing frame sync_id */
13634            PNGLong(chunk+10,(png_uint_32)
13635              ((mng_info->ticks_per_second*
13636              image->delay)/MagickMax(image->ticks_per_second,1)));
13637            (void) WriteBlob(image,14,chunk);
13638            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13639            mng_info->delay=(png_uint_32) image->delay;
13640          }
13641        mng_info->old_framing_mode=mng_info->framing_mode;
13642      }
13643
13644 #if defined(JNG_SUPPORTED)
13645    if (image_info->compression == JPEGCompression)
13646      {
13647        ImageInfo
13648          *write_info;
13649
13650        if (logging != MagickFalse)
13651          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13652            "  Writing JNG object.");
13653        /* To do: specify the desired alpha compression method. */
13654        write_info=CloneImageInfo(image_info);
13655        write_info->compression=UndefinedCompression;
13656        status=WriteOneJNGImage(mng_info,write_info,image,exception);
13657        write_info=DestroyImageInfo(write_info);
13658      }
13659    else
13660 #endif
13661      {
13662        if (logging != MagickFalse)
13663          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13664            "  Writing PNG object.");
13665
13666        mng_info->need_blob = MagickFalse;
13667        mng_info->ping_preserve_colormap = MagickFalse;
13668
13669        /* We don't want any ancillary chunks written */
13670        mng_info->ping_exclude_bKGD=MagickTrue;
13671        mng_info->ping_exclude_cHRM=MagickTrue;
13672        mng_info->ping_exclude_date=MagickTrue;
13673        mng_info->ping_exclude_EXIF=MagickTrue;
13674        mng_info->ping_exclude_gAMA=MagickTrue;
13675        mng_info->ping_exclude_iCCP=MagickTrue;
13676        /* mng_info->ping_exclude_iTXt=MagickTrue; */
13677        mng_info->ping_exclude_oFFs=MagickTrue;
13678        mng_info->ping_exclude_pHYs=MagickTrue;
13679        mng_info->ping_exclude_sRGB=MagickTrue;
13680        mng_info->ping_exclude_tEXt=MagickTrue;
13681        mng_info->ping_exclude_tRNS=MagickTrue;
13682        mng_info->ping_exclude_vpAg=MagickTrue;
13683        mng_info->ping_exclude_zCCP=MagickTrue;
13684        mng_info->ping_exclude_zTXt=MagickTrue;
13685
13686        status=WriteOnePNGImage(mng_info,image_info,image,exception);
13687      }
13688
13689     if (status == MagickFalse)
13690       {
13691         MngInfoFreeStruct(mng_info,&have_mng_structure);
13692         (void) CloseBlob(image);
13693         return(MagickFalse);
13694       }
13695     (void) CatchImageException(image);
13696     if (GetNextImageInList(image) == (Image *) NULL)
13697       break;
13698     image=SyncNextImageInList(image);
13699     status=SetImageProgress(image,SaveImagesTag,scene++,
13700       GetImageListLength(image));
13701
13702     if (status == MagickFalse)
13703       break;
13704
13705   } while (mng_info->adjoin);
13706
13707   if (write_mng)
13708     {
13709       while (GetPreviousImageInList(image) != (Image *) NULL)
13710         image=GetPreviousImageInList(image);
13711       /*
13712         Write the MEND chunk.
13713       */
13714       (void) WriteBlobMSBULong(image,0x00000000L);
13715       PNGType(chunk,mng_MEND);
13716       LogPNGChunk(logging,mng_MEND,0L);
13717       (void) WriteBlob(image,4,chunk);
13718       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13719     }
13720   /*
13721     Relinquish resources.
13722   */
13723   (void) CloseBlob(image);
13724   MngInfoFreeStruct(mng_info,&have_mng_structure);
13725
13726   if (logging != MagickFalse)
13727     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13728
13729   return(MagickTrue);
13730 }
13731 #else /* PNG_LIBPNG_VER > 10011 */
13732
13733 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13734 {
13735   (void) image;
13736   printf("Your PNG library is too old: You have libpng-%s\n",
13737      PNG_LIBPNG_VER_STRING);
13738
13739   ThrowBinaryException(CoderError,"PNG library is too old",
13740      image_info->filename);
13741 }
13742
13743 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13744 {
13745   return(WritePNGImage(image_info,image));
13746 }
13747 #endif /* PNG_LIBPNG_VER > 10011 */
13748 #endif