]> granicus.if.org Git - imagemagick/blob - coders/png.c
Fixed dds:cluster-fit option.
[imagemagick] / coders / png.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            PPPP   N   N   GGGG                              %
7 %                            P   P  NN  N  G                                  %
8 %                            PPPP   N N N  G  GG                              %
9 %                            P      N  NN  G   G                              %
10 %                            P      N   N   GGG                               %
11 %                                                                             %
12 %                                                                             %
13 %              Read/Write Portable Network Graphics Image Format              %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                           Glenn Randers-Pehrson                             %
18 %                               November 1997                                 %
19 %                                                                             %
20 %                                                                             %
21 %  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
22 %  dedicated to making software imaging solutions freely available.           %
23 %                                                                             %
24 %  You may not use this file except in compliance with the License.  You may  %
25 %  obtain a copy of the License at                                            %
26 %                                                                             %
27 %    http://www.imagemagick.org/script/license.php                            %
28 %                                                                             %
29 %  Unless required by applicable law or agreed to in writing, software        %
30 %  distributed under the License is distributed on an "AS IS" BASIS,          %
31 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32 %  See the License for the specific language governing permissions and        %
33 %  limitations under the License.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39
40 \f
41 /*
42   Include declarations.
43 */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
52 #include "MagickCore/color-private.h"
53 #include "MagickCore/colormap.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/histogram.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/layer.h"
65 #include "MagickCore/list.h"
66 #include "MagickCore/log.h"
67 #include "MagickCore/MagickCore.h"
68 #include "MagickCore/memory_.h"
69 #include "MagickCore/module.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/profile.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/static.h"
82 #include "MagickCore/statistic.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/utility.h"
87 #if defined(MAGICKCORE_PNG_DELEGATE)
88
89 /* Suppress libpng pedantic warnings that were added in
90  * libpng-1.2.41 and libpng-1.4.0.  If you are working on
91  * migration to libpng-1.5, remove these defines and then
92  * fix any code that generates warnings.
93  */
94 /* #define PNG_DEPRECATED   Use of this function is deprecated */
95 /* #define PNG_USE_RESULT   The result of this function must be checked */
96 /* #define PNG_NORETURN     This function does not return */
97 /* #define PNG_ALLOCATED    The result of the function is new memory */
98 /* #define PNG_DEPSTRUCT    Access to this struct member is deprecated */
99
100 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
101 #define PNG_PTR_NORETURN
102
103 #include "png.h"
104 #include "zlib.h"
105 \f
106 /* ImageMagick differences */
107 #define first_scene scene
108
109 #if PNG_LIBPNG_VER > 10011
110 /*
111   Optional declarations. Define or undefine them as you like.
112 */
113 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
114
115 /*
116   Features under construction.  Define these to work on them.
117 */
118 #undef MNG_OBJECT_BUFFERS
119 #undef MNG_BASI_SUPPORTED
120 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121 #define MNG_INSERT_LAYERS   /* Troublesome, but seem to work as of 5.4.4 */
122 #if defined(MAGICKCORE_JPEG_DELEGATE)
123 #  define JNG_SUPPORTED /* Not finished as of 5.5.2.  See "To do" comments. */
124 #endif
125 #if !defined(RGBColorMatchExact)
126 #define IsPNGColorEqual(color,target) \
127        (((color).red == (target).red) && \
128         ((color).green == (target).green) && \
129         ((color).blue == (target).blue))
130 #endif
131
132 /* Table of recognized sRGB ICC profiles */
133 struct sRGB_info_struct
134 {
135     png_uint_32 len;
136     png_uint_32 crc;
137     png_byte intent;
138 };
139
140 const struct sRGB_info_struct sRGB_info[] =
141
142     /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
143     { 3048, 0x3b8772b9UL, 0},
144
145     /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
146     { 3052, 0x427ebb21UL, 1},
147
148     /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
149     {60988, 0x306fd8aeUL, 0},
150
151     /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
152      {60960, 0xbbef7812UL, 0},
153
154     /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
155      { 3024, 0x5d5129ceUL, 1},
156
157      /* HP-Microsoft sRGB v2 perceptual */
158      { 3144, 0x182ea552UL, 0},
159
160      /* HP-Microsoft sRGB v2 media-relative */
161      { 3144, 0xf29e526dUL, 1},
162
163      /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
164      {  524, 0xd4938c39UL, 0},
165
166      /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
167      { 3212, 0x034af5a1UL, 0},
168
169      /* Not recognized */
170      {    0, 0x00000000UL, 0},
171 };
172
173 /* Macros for left-bit-replication to ensure that pixels
174  * and PixelInfos all have the same image->depth, and for use
175  * in PNG8 quantization.
176  */
177
178 /* LBR01: Replicate top bit */
179
180 #define LBR01PacketRed(pixelpacket) \
181      (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
182         0 : QuantumRange);
183
184 #define LBR01PacketGreen(pixelpacket) \
185      (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
186         0 : QuantumRange);
187
188 #define LBR01PacketBlue(pixelpacket) \
189      (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
190         0 : QuantumRange);
191
192 #define LBR01PacketAlpha(pixelpacket) \
193      (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
194         0 : QuantumRange);
195
196 #define LBR01PacketRGB(pixelpacket) \
197         { \
198         LBR01PacketRed((pixelpacket)); \
199         LBR01PacketGreen((pixelpacket)); \
200         LBR01PacketBlue((pixelpacket)); \
201         }
202
203 #define LBR01PacketRGBO(pixelpacket) \
204         { \
205         LBR01PacketRGB((pixelpacket)); \
206         LBR01PacketAlpha((pixelpacket)); \
207         }
208
209 #define LBR01PixelRed(pixel) \
210         (SetPixelRed(image, \
211         ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
212         0 : QuantumRange,(pixel)));
213
214 #define LBR01PixelGreen(pixel) \
215         (SetPixelGreen(image, \
216         ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
217         0 : QuantumRange,(pixel)));
218
219 #define LBR01PixelBlue(pixel) \
220         (SetPixelBlue(image, \
221         ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
222         0 : QuantumRange,(pixel)));
223
224 #define LBR01PixelAlpha(pixel) \
225         (SetPixelAlpha(image, \
226         ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
227         0 : QuantumRange,(pixel)));
228
229 #define LBR01PixelRGB(pixel) \
230         { \
231         LBR01PixelRed((pixel)); \
232         LBR01PixelGreen((pixel)); \
233         LBR01PixelBlue((pixel)); \
234         }
235
236 #define LBR01PixelRGBA(pixel) \
237         { \
238         LBR01PixelRGB((pixel)); \
239         LBR01PixelAlpha((pixel)); \
240         }
241
242 /* LBR02: Replicate top 2 bits */
243
244 #define LBR02PacketRed(pixelpacket) \
245    { \
246      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
247      (pixelpacket).red=ScaleCharToQuantum( \
248        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
249    }
250 #define LBR02PacketGreen(pixelpacket) \
251    { \
252      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
253      (pixelpacket).green=ScaleCharToQuantum( \
254        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
255    }
256 #define LBR02PacketBlue(pixelpacket) \
257    { \
258      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
259      (pixelpacket).blue=ScaleCharToQuantum( \
260        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
261    }
262 #define LBR02PacketAlpha(pixelpacket) \
263    { \
264      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
265      (pixelpacket).alpha=ScaleCharToQuantum( \
266        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
267    }
268
269 #define LBR02PacketRGB(pixelpacket) \
270         { \
271         LBR02PacketRed((pixelpacket)); \
272         LBR02PacketGreen((pixelpacket)); \
273         LBR02PacketBlue((pixelpacket)); \
274         }
275
276 #define LBR02PacketRGBO(pixelpacket) \
277         { \
278         LBR02PacketRGB((pixelpacket)); \
279         LBR02PacketAlpha((pixelpacket)); \
280         }
281
282 #define LBR02PixelRed(pixel) \
283    { \
284      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
285        & 0xc0; \
286      SetPixelRed(image, ScaleCharToQuantum( \
287        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
288        (pixel)); \
289    }
290 #define LBR02PixelGreen(pixel) \
291    { \
292      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
293        & 0xc0; \
294      SetPixelGreen(image, ScaleCharToQuantum( \
295        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
296        (pixel)); \
297    }
298 #define LBR02PixelBlue(pixel) \
299    { \
300      unsigned char lbr_bits= \
301        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
302      SetPixelBlue(image, ScaleCharToQuantum( \
303        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
304        (pixel)); \
305    }
306 #define LBR02PixelAlpha(pixel) \
307    { \
308      unsigned char lbr_bits= \
309        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
310      SetPixelAlpha(image, ScaleCharToQuantum( \
311        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
312        (pixel) ); \
313    }
314
315 #define LBR02PixelRGB(pixel) \
316         { \
317         LBR02PixelRed((pixel)); \
318         LBR02PixelGreen((pixel)); \
319         LBR02PixelBlue((pixel)); \
320         }
321
322 #define LBR02PixelRGBA(pixel) \
323         { \
324         LBR02PixelRGB((pixel)); \
325         LBR02PixelAlpha((pixel)); \
326         }
327
328 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
329    PNG8 quantization) */
330
331 #define LBR03PacketRed(pixelpacket) \
332    { \
333      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
334      (pixelpacket).red=ScaleCharToQuantum( \
335        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
336    }
337 #define LBR03PacketGreen(pixelpacket) \
338    { \
339      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
340      (pixelpacket).green=ScaleCharToQuantum( \
341        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
342    }
343 #define LBR03PacketBlue(pixelpacket) \
344    { \
345      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
346      (pixelpacket).blue=ScaleCharToQuantum( \
347        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
348    }
349
350 #define LBR03PacketRGB(pixelpacket) \
351         { \
352         LBR03PacketRed((pixelpacket)); \
353         LBR03PacketGreen((pixelpacket)); \
354         LBR03PacketBlue((pixelpacket)); \
355         }
356
357 #define LBR03PixelRed(pixel) \
358    { \
359      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
360        & 0xe0; \
361      SetPixelRed(image, ScaleCharToQuantum( \
362        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
363    }
364 #define LBR03Green(pixel) \
365    { \
366      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
367        & 0xe0; \
368      SetPixelGreen(image, ScaleCharToQuantum( \
369        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
370    }
371 #define LBR03Blue(pixel) \
372    { \
373      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
374        & 0xe0; \
375      SetPixelBlue(image, ScaleCharToQuantum( \
376        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
377    }
378
379 #define LBR03RGB(pixel) \
380         { \
381         LBR03PixelRed((pixel)); \
382         LBR03Green((pixel)); \
383         LBR03Blue((pixel)); \
384         }
385
386 /* LBR04: Replicate top 4 bits */
387
388 #define LBR04PacketRed(pixelpacket) \
389    { \
390      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
391      (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
392    }
393 #define LBR04PacketGreen(pixelpacket) \
394    { \
395      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
396      (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
397    }
398 #define LBR04PacketBlue(pixelpacket) \
399    { \
400      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
401      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
402    }
403 #define LBR04PacketAlpha(pixelpacket) \
404    { \
405      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
406      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
407    }
408
409 #define LBR04PacketRGB(pixelpacket) \
410         { \
411         LBR04PacketRed((pixelpacket)); \
412         LBR04PacketGreen((pixelpacket)); \
413         LBR04PacketBlue((pixelpacket)); \
414         }
415
416 #define LBR04PacketRGBO(pixelpacket) \
417         { \
418         LBR04PacketRGB((pixelpacket)); \
419         LBR04PacketAlpha((pixelpacket)); \
420         }
421
422 #define LBR04PixelRed(pixel) \
423    { \
424      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
425        & 0xf0; \
426      SetPixelRed(image,\
427        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
428    }
429 #define LBR04PixelGreen(pixel) \
430    { \
431      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
432        & 0xf0; \
433      SetPixelGreen(image,\
434        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
435    }
436 #define LBR04PixelBlue(pixel) \
437    { \
438      unsigned char lbr_bits= \
439        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
440      SetPixelBlue(image,\
441        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
442    }
443 #define LBR04PixelAlpha(pixel) \
444    { \
445      unsigned char lbr_bits= \
446        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
447      SetPixelAlpha(image,\
448        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
449    }
450
451 #define LBR04PixelRGB(pixel) \
452         { \
453         LBR04PixelRed((pixel)); \
454         LBR04PixelGreen((pixel)); \
455         LBR04PixelBlue((pixel)); \
456         }
457
458 #define LBR04PixelRGBA(pixel) \
459         { \
460         LBR04PixelRGB((pixel)); \
461         LBR04PixelAlpha((pixel)); \
462         }
463
464
465 #if MAGICKCORE_QUANTUM_DEPTH > 8
466 /* LBR08: Replicate top 8 bits */
467
468 #define LBR08PacketRed(pixelpacket) \
469    { \
470      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
471      (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
472    }
473 #define LBR08PacketGreen(pixelpacket) \
474    { \
475      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
476      (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
477    }
478 #define LBR08PacketBlue(pixelpacket) \
479    { \
480      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
481      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
482    }
483 #define LBR08PacketAlpha(pixelpacket) \
484    { \
485      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
486      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
487    }
488
489 #define LBR08PacketRGB(pixelpacket) \
490         { \
491         LBR08PacketRed((pixelpacket)); \
492         LBR08PacketGreen((pixelpacket)); \
493         LBR08PacketBlue((pixelpacket)); \
494         }
495
496 #define LBR08PacketRGBO(pixelpacket) \
497         { \
498         LBR08PacketRGB((pixelpacket)); \
499         LBR08PacketAlpha((pixelpacket)); \
500         }
501
502 #define LBR08PixelRed(pixel) \
503    { \
504      unsigned char lbr_bits= \
505        ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
506      SetPixelRed(image,\
507        ScaleCharToQuantum((lbr_bits)), (pixel)); \
508    }
509 #define LBR08PixelGreen(pixel) \
510    { \
511      unsigned char lbr_bits= \
512        ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
513      SetPixelGreen(image,\
514        ScaleCharToQuantum((lbr_bits)), (pixel)); \
515    }
516 #define LBR08PixelBlue(pixel) \
517    { \
518      unsigned char lbr_bits= \
519        ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
520      SetPixelBlue(image,\
521        ScaleCharToQuantum((lbr_bits)), (pixel)); \
522    }
523 #define LBR08PixelAlpha(pixel) \
524    { \
525      unsigned char lbr_bits= \
526        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
527      SetPixelAlpha(image,\
528        ScaleCharToQuantum((lbr_bits)), (pixel)); \
529    }
530
531 #define LBR08PixelRGB(pixel) \
532         { \
533         LBR08PixelRed((pixel)); \
534         LBR08PixelGreen((pixel)); \
535         LBR08PixelBlue((pixel)); \
536         }
537
538 #define LBR08PixelRGBA(pixel) \
539         { \
540         LBR08PixelRGB((pixel)); \
541         LBR08PixelAlpha((pixel)); \
542         }
543 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
544
545
546 #if MAGICKCORE_QUANTUM_DEPTH > 16
547 /* LBR16: Replicate top 16 bits */
548
549 #define LBR16PacketRed(pixelpacket) \
550    { \
551      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
552      (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
553    }
554 #define LBR16PacketGreen(pixelpacket) \
555    { \
556      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
557      (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
558    }
559 #define LBR16PacketBlue(pixelpacket) \
560    { \
561      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
562      (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
563    }
564 #define LBR16PacketAlpha(pixelpacket) \
565    { \
566      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
567      (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
568    }
569
570 #define LBR16PacketRGB(pixelpacket) \
571         { \
572         LBR16PacketRed((pixelpacket)); \
573         LBR16PacketGreen((pixelpacket)); \
574         LBR16PacketBlue((pixelpacket)); \
575         }
576
577 #define LBR16PacketRGBO(pixelpacket) \
578         { \
579         LBR16PacketRGB((pixelpacket)); \
580         LBR16PacketAlpha((pixelpacket)); \
581         }
582
583 #define LBR16PixelRed(pixel) \
584    { \
585      unsigned short lbr_bits= \
586        ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
587      SetPixelRed(image,\
588        ScaleShortToQuantum((lbr_bits)),(pixel)); \
589    }
590 #define LBR16PixelGreen(pixel) \
591    { \
592      unsigned short lbr_bits= \
593        ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
594      SetPixelGreen(image,\
595        ScaleShortToQuantum((lbr_bits)),(pixel)); \
596    }
597 #define LBR16PixelBlue(pixel) \
598    { \
599      unsigned short lbr_bits= \
600        ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
601      SetPixelBlue(image,\
602        ScaleShortToQuantum((lbr_bits)),(pixel)); \
603    }
604 #define LBR16PixelAlpha(pixel) \
605    { \
606      unsigned short lbr_bits= \
607        ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
608      SetPixelAlpha(image,\
609        ScaleShortToQuantum((lbr_bits)),(pixel)); \
610    }
611
612 #define LBR16PixelRGB(pixel) \
613         { \
614         LBR16PixelRed((pixel)); \
615         LBR16PixelGreen((pixel)); \
616         LBR16PixelBlue((pixel)); \
617         }
618
619 #define LBR16PixelRGBA(pixel) \
620         { \
621         LBR16PixelRGB((pixel)); \
622         LBR16PixelAlpha((pixel)); \
623         }
624 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
625
626 /*
627   Establish thread safety.
628   setjmp/longjmp is claimed to be safe on these platforms:
629   setjmp/longjmp is alleged to be unsafe on these platforms:
630 */
631 #ifndef SETJMP_IS_THREAD_SAFE
632 #define PNG_SETJMP_NOT_THREAD_SAFE
633 #endif
634
635 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
636 static SemaphoreInfo
637   *ping_semaphore = (SemaphoreInfo *) NULL;
638 #endif
639
640 /*
641   This temporary until I set up malloc'ed object attributes array.
642   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
643   waste more memory.
644 */
645 #define MNG_MAX_OBJECTS 256
646
647 /*
648   If this not defined, spec is interpreted strictly.  If it is
649   defined, an attempt will be made to recover from some errors,
650   including
651       o global PLTE too short
652 */
653 #undef MNG_LOOSE
654
655 /*
656   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
657   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
658   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
659   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
660   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
661   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
662   will be enabled by default in libpng-1.2.0.
663 */
664 #ifdef PNG_MNG_FEATURES_SUPPORTED
665 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
666 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
667 #  endif
668 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
669 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
670 #  endif
671 #endif
672
673 /*
674   Maximum valid size_t in PNG/MNG chunks is (2^31)-1
675   This macro is only defined in libpng-1.0.3 and later.
676   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
677 */
678 #ifndef PNG_UINT_31_MAX
679 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
680 #endif
681
682 /*
683   Constant strings for known chunk types.  If you need to add a chunk,
684   add a string holding the name here.   To make the code more
685   portable, we use ASCII numbers like this, not characters.
686 */
687
688 static png_byte mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
689 static png_byte mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
690 static png_byte mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
691 static png_byte mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
692 static png_byte mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
693 static png_byte mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
694 static png_byte mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
695 static png_byte mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
696 static png_byte mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
697 static png_byte mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
698 static png_byte mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
699 static png_byte mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
700 static png_byte mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
701 static png_byte mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
702 static png_byte mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
703 static png_byte mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
704 static png_byte mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
705 static png_byte mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
706 static png_byte mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
707 static png_byte mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
708 static png_byte mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
709 static png_byte mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
710 static png_byte mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
711 static png_byte mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
712 static png_byte mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
713 static png_byte mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
714 static png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
715 static png_byte mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
716 static png_byte mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
717 static png_byte mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
718 static png_byte mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
719 static png_byte mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
720 static png_byte mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
721 static png_byte mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
722
723 #if defined(JNG_SUPPORTED)
724 static png_byte mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
725 static png_byte mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
726 static png_byte mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
727 static png_byte mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
728 static png_byte mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
729 static png_byte mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
730 #endif
731
732 /*
733 Other known chunks that are not yet supported by ImageMagick:
734 static png_byte mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
735 static png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
736 static png_byte mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
737 static png_byte mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
738 static png_byte mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
739 static png_byte mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
740 static png_byte mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
741 static png_byte mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
742 */
743
744 typedef struct _MngBox
745 {
746   long
747     left,
748     right,
749     top,
750     bottom;
751 } MngBox;
752
753 typedef struct _MngPair
754 {
755   volatile long
756     a,
757     b;
758 } MngPair;
759
760 #ifdef MNG_OBJECT_BUFFERS
761 typedef struct _MngBuffer
762 {
763
764   size_t
765     height,
766     width;
767
768   Image
769     *image;
770
771   png_color
772     plte[256];
773
774   int
775     reference_count;
776
777   unsigned char
778     alpha_sample_depth,
779     compression_method,
780     color_type,
781     concrete,
782     filter_method,
783     frozen,
784     image_type,
785     interlace_method,
786     pixel_sample_depth,
787     plte_length,
788     sample_depth,
789     viewable;
790 } MngBuffer;
791 #endif
792
793 typedef struct _MngInfo
794 {
795
796 #ifdef MNG_OBJECT_BUFFERS
797   MngBuffer
798     *ob[MNG_MAX_OBJECTS];
799 #endif
800
801   Image *
802     image;
803
804   RectangleInfo
805     page;
806
807   int
808     adjoin,
809 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
810     bytes_in_read_buffer,
811     found_empty_plte,
812 #endif
813     equal_backgrounds,
814     equal_chrms,
815     equal_gammas,
816 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
817     defined(PNG_MNG_FEATURES_SUPPORTED)
818     equal_palettes,
819 #endif
820     equal_physs,
821     equal_srgbs,
822     framing_mode,
823     have_global_bkgd,
824     have_global_chrm,
825     have_global_gama,
826     have_global_phys,
827     have_global_sbit,
828     have_global_srgb,
829     have_saved_bkgd_index,
830     have_write_global_chrm,
831     have_write_global_gama,
832     have_write_global_plte,
833     have_write_global_srgb,
834     need_fram,
835     object_id,
836     old_framing_mode,
837     saved_bkgd_index;
838
839   int
840     new_number_colors;
841
842   ssize_t
843     image_found,
844     loop_count[256],
845     loop_iteration[256],
846     scenes_found,
847     x_off[MNG_MAX_OBJECTS],
848     y_off[MNG_MAX_OBJECTS];
849
850   MngBox
851     clip,
852     frame,
853     image_box,
854     object_clip[MNG_MAX_OBJECTS];
855
856   unsigned char
857     /* These flags could be combined into one byte */
858     exists[MNG_MAX_OBJECTS],
859     frozen[MNG_MAX_OBJECTS],
860     loop_active[256],
861     invisible[MNG_MAX_OBJECTS],
862     viewable[MNG_MAX_OBJECTS];
863
864   MagickOffsetType
865     loop_jump[256];
866
867   png_colorp
868     global_plte;
869
870   png_color_8
871     global_sbit;
872
873   png_byte
874 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
875     read_buffer[8],
876 #endif
877     global_trns[256];
878
879   float
880     global_gamma;
881
882   ChromaticityInfo
883     global_chrm;
884
885   RenderingIntent
886     global_srgb_intent;
887
888   unsigned int
889     delay,
890     global_plte_length,
891     global_trns_length,
892     global_x_pixels_per_unit,
893     global_y_pixels_per_unit,
894     mng_width,
895     mng_height,
896     ticks_per_second;
897
898   MagickBooleanType
899     need_blob;
900
901   unsigned int
902     IsPalette,
903     global_phys_unit_type,
904     basi_warning,
905     clon_warning,
906     dhdr_warning,
907     jhdr_warning,
908     magn_warning,
909     past_warning,
910     phyg_warning,
911     phys_warning,
912     sbit_warning,
913     show_warning,
914     mng_type,
915     write_mng,
916     write_png_colortype,
917     write_png_depth,
918     write_png_compression_level,
919     write_png_compression_strategy,
920     write_png_compression_filter,
921     write_png8,
922     write_png24,
923     write_png32,
924     write_png48,
925     write_png64;
926
927 #ifdef MNG_BASI_SUPPORTED
928   size_t
929     basi_width,
930     basi_height;
931
932   unsigned int
933     basi_depth,
934     basi_color_type,
935     basi_compression_method,
936     basi_filter_type,
937     basi_interlace_method,
938     basi_red,
939     basi_green,
940     basi_blue,
941     basi_alpha,
942     basi_viewable;
943 #endif
944
945   png_uint_16
946     magn_first,
947     magn_last,
948     magn_mb,
949     magn_ml,
950     magn_mr,
951     magn_mt,
952     magn_mx,
953     magn_my,
954     magn_methx,
955     magn_methy;
956
957   PixelInfo
958     mng_global_bkgd;
959
960   /* Added at version 6.6.6-7 */
961   MagickBooleanType
962     ping_exclude_bKGD,
963     ping_exclude_cHRM,
964     ping_exclude_date,
965     ping_exclude_EXIF,
966     ping_exclude_gAMA,
967     ping_exclude_iCCP,
968     /* ping_exclude_iTXt, */
969     ping_exclude_oFFs,
970     ping_exclude_pHYs,
971     ping_exclude_sRGB,
972     ping_exclude_tEXt,
973     ping_exclude_tRNS,
974     ping_exclude_vpAg,
975     ping_exclude_zCCP, /* hex-encoded iCCP */
976     ping_exclude_zTXt,
977     ping_preserve_colormap,
978   /* Added at version 6.8.5-7 */
979     ping_preserve_iCCP;
980
981 } MngInfo;
982 #endif /* VER */
983 \f
984 /*
985   Forward declarations.
986 */
987 static MagickBooleanType
988   WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
989
990 static MagickBooleanType
991   WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
992
993 #if defined(JNG_SUPPORTED)
994 static MagickBooleanType
995   WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
996 #endif
997
998 #if PNG_LIBPNG_VER > 10011
999
1000
1001 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
1002 static MagickBooleanType
1003 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
1004 {
1005     /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
1006      *
1007      * This is true if the high byte and the next highest byte of
1008      * each sample of the image, the colormap, and the background color
1009      * are equal to each other.  We check this by seeing if the samples
1010      * are unchanged when we scale them down to 8 and back up to Quantum.
1011      *
1012      * We don't use the method GetImageDepth() because it doesn't check
1013      * background and doesn't handle PseudoClass specially.
1014      */
1015
1016 #define QuantumToCharToQuantumEqQuantum(quantum) \
1017   ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
1018
1019     MagickBooleanType
1020       ok_to_reduce=MagickFalse;
1021
1022     if (image->depth >= 16)
1023       {
1024
1025         const Quantum
1026           *p;
1027
1028         ok_to_reduce=
1029            QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
1030            QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
1031            QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
1032            MagickTrue : MagickFalse;
1033
1034         if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
1035           {
1036             int indx;
1037
1038             for (indx=0; indx < (ssize_t) image->colors; indx++)
1039               {
1040                 ok_to_reduce=(
1041                    QuantumToCharToQuantumEqQuantum(
1042                    image->colormap[indx].red) &&
1043                    QuantumToCharToQuantumEqQuantum(
1044                    image->colormap[indx].green) &&
1045                    QuantumToCharToQuantumEqQuantum(
1046                    image->colormap[indx].blue)) ?
1047                    MagickTrue : MagickFalse;
1048
1049                 if (ok_to_reduce == MagickFalse)
1050                    break;
1051               }
1052           }
1053
1054         if ((ok_to_reduce != MagickFalse) &&
1055             (image->storage_class != PseudoClass))
1056           {
1057             ssize_t
1058               y;
1059
1060             register ssize_t
1061               x;
1062
1063             for (y=0; y < (ssize_t) image->rows; y++)
1064             {
1065               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1066
1067               if (p == (const Quantum *) NULL)
1068                 {
1069                   ok_to_reduce = MagickFalse;
1070                   break;
1071                 }
1072
1073               for (x=(ssize_t) image->columns-1; x >= 0; x--)
1074               {
1075                 ok_to_reduce=
1076                    QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1077                    QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1078                    QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
1079                    MagickTrue : MagickFalse;
1080
1081                 if (ok_to_reduce == MagickFalse)
1082                   break;
1083
1084                 p+=GetPixelChannels(image);
1085               }
1086               if (x >= 0)
1087                 break;
1088             }
1089           }
1090
1091         if (ok_to_reduce != MagickFalse)
1092           {
1093             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1094                 "    OK to reduce PNG bit depth to 8 without loss of info");
1095           }
1096         else
1097           {
1098             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1099                 "    Not OK to reduce PNG bit depth to 8 without loss of info");
1100           }
1101       }
1102
1103     return ok_to_reduce;
1104 }
1105 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1106
1107 static const char* PngColorTypeToString(const unsigned int color_type)
1108 {
1109   const char
1110     *result = "Unknown";
1111
1112   switch (color_type)
1113     {
1114     case PNG_COLOR_TYPE_GRAY:
1115       result = "Gray";
1116       break;
1117     case PNG_COLOR_TYPE_GRAY_ALPHA:
1118       result = "Gray+Alpha";
1119       break;
1120     case PNG_COLOR_TYPE_PALETTE:
1121       result = "Palette";
1122       break;
1123     case PNG_COLOR_TYPE_RGB:
1124       result = "RGB";
1125       break;
1126     case PNG_COLOR_TYPE_RGB_ALPHA:
1127       result = "RGB+Alpha";
1128       break;
1129     }
1130
1131   return result;
1132 }
1133
1134 static int
1135 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1136 {
1137   switch (intent)
1138   {
1139     case PerceptualIntent:
1140        return 0;
1141
1142     case RelativeIntent:
1143        return 1;
1144
1145     case SaturationIntent:
1146        return 2;
1147
1148     case AbsoluteIntent:
1149        return 3;
1150
1151     default:
1152        return -1;
1153   }
1154 }
1155
1156 static RenderingIntent
1157 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1158 {
1159   switch (ping_intent)
1160   {
1161     case 0:
1162       return PerceptualIntent;
1163
1164     case 1:
1165       return RelativeIntent;
1166
1167     case 2:
1168       return SaturationIntent;
1169
1170     case 3:
1171       return AbsoluteIntent;
1172
1173     default:
1174       return UndefinedIntent;
1175     }
1176 }
1177
1178 static const char *
1179 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1180 {
1181   switch (ping_intent)
1182   {
1183     case 0:
1184       return "Perceptual Intent";
1185
1186     case 1:
1187       return "Relative Intent";
1188
1189     case 2:
1190       return "Saturation Intent";
1191
1192     case 3:
1193       return "Absolute Intent";
1194
1195     default:
1196       return "Undefined Intent";
1197     }
1198 }
1199
1200 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1201 {
1202   if (x > y)
1203     return(x);
1204
1205   return(y);
1206 }
1207
1208 static const char *
1209 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1210 {
1211   switch (ping_colortype)
1212   {
1213     case 0:
1214       return "Grayscale";
1215
1216     case 2:
1217       return "Truecolor";
1218
1219     case 3:
1220       return "Indexed";
1221
1222     case 4:
1223       return "GrayAlpha";
1224
1225     case 6:
1226       return "RGBA";
1227
1228     default:
1229       return "UndefinedColorType";
1230     }
1231 }
1232
1233
1234 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1235 {
1236   if (x < y)
1237     return(x);
1238
1239   return(y);
1240 }
1241 #endif /* PNG_LIBPNG_VER > 10011 */
1242 #endif /* MAGICKCORE_PNG_DELEGATE */
1243 \f
1244 /*
1245 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1246 %                                                                             %
1247 %                                                                             %
1248 %                                                                             %
1249 %   I s M N G                                                                 %
1250 %                                                                             %
1251 %                                                                             %
1252 %                                                                             %
1253 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1254 %
1255 %  IsMNG() returns MagickTrue if the image format type, identified by the
1256 %  magick string, is MNG.
1257 %
1258 %  The format of the IsMNG method is:
1259 %
1260 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1261 %
1262 %  A description of each parameter follows:
1263 %
1264 %    o magick: compare image format pattern against these bytes.
1265 %
1266 %    o length: Specifies the length of the magick string.
1267 %
1268 %
1269 */
1270 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1271 {
1272   if (length < 8)
1273     return(MagickFalse);
1274
1275   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1276     return(MagickTrue);
1277
1278   return(MagickFalse);
1279 }
1280 \f
1281 /*
1282 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1283 %                                                                             %
1284 %                                                                             %
1285 %                                                                             %
1286 %   I s J N G                                                                 %
1287 %                                                                             %
1288 %                                                                             %
1289 %                                                                             %
1290 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1291 %
1292 %  IsJNG() returns MagickTrue if the image format type, identified by the
1293 %  magick string, is JNG.
1294 %
1295 %  The format of the IsJNG method is:
1296 %
1297 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1298 %
1299 %  A description of each parameter follows:
1300 %
1301 %    o magick: compare image format pattern against these bytes.
1302 %
1303 %    o length: Specifies the length of the magick string.
1304 %
1305 %
1306 */
1307 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1308 {
1309   if (length < 8)
1310     return(MagickFalse);
1311
1312   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1313     return(MagickTrue);
1314
1315   return(MagickFalse);
1316 }
1317 \f
1318 /*
1319 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1320 %                                                                             %
1321 %                                                                             %
1322 %                                                                             %
1323 %   I s P N G                                                                 %
1324 %                                                                             %
1325 %                                                                             %
1326 %                                                                             %
1327 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1328 %
1329 %  IsPNG() returns MagickTrue if the image format type, identified by the
1330 %  magick string, is PNG.
1331 %
1332 %  The format of the IsPNG method is:
1333 %
1334 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1335 %
1336 %  A description of each parameter follows:
1337 %
1338 %    o magick: compare image format pattern against these bytes.
1339 %
1340 %    o length: Specifies the length of the magick string.
1341 %
1342 */
1343 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1344 {
1345   if (length < 8)
1346     return(MagickFalse);
1347
1348   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1349     return(MagickTrue);
1350
1351   return(MagickFalse);
1352 }
1353 \f
1354 #if defined(MAGICKCORE_PNG_DELEGATE)
1355 #if defined(__cplusplus) || defined(c_plusplus)
1356 extern "C" {
1357 #endif
1358
1359 #if (PNG_LIBPNG_VER > 10011)
1360 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1361 {
1362   unsigned char
1363     buffer[4];
1364
1365   assert(image != (Image *) NULL);
1366   assert(image->signature == MagickSignature);
1367   buffer[0]=(unsigned char) (value >> 24);
1368   buffer[1]=(unsigned char) (value >> 16);
1369   buffer[2]=(unsigned char) (value >> 8);
1370   buffer[3]=(unsigned char) value;
1371   return((size_t) WriteBlob(image,4,buffer));
1372 }
1373
1374 static void PNGLong(png_bytep p,png_uint_32 value)
1375 {
1376   *p++=(png_byte) ((value >> 24) & 0xff);
1377   *p++=(png_byte) ((value >> 16) & 0xff);
1378   *p++=(png_byte) ((value >> 8) & 0xff);
1379   *p++=(png_byte) (value & 0xff);
1380 }
1381
1382 #if defined(JNG_SUPPORTED)
1383 static void PNGsLong(png_bytep p,png_int_32 value)
1384 {
1385   *p++=(png_byte) ((value >> 24) & 0xff);
1386   *p++=(png_byte) ((value >> 16) & 0xff);
1387   *p++=(png_byte) ((value >> 8) & 0xff);
1388   *p++=(png_byte) (value & 0xff);
1389 }
1390 #endif
1391
1392 static void PNGShort(png_bytep p,png_uint_16 value)
1393 {
1394   *p++=(png_byte) ((value >> 8) & 0xff);
1395   *p++=(png_byte) (value & 0xff);
1396 }
1397
1398 static void PNGType(png_bytep p,png_bytep type)
1399 {
1400   (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1401 }
1402
1403 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1404    size_t length)
1405 {
1406   if (logging != MagickFalse)
1407     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1408       "  Writing %c%c%c%c chunk, length: %.20g",
1409       type[0],type[1],type[2],type[3],(double) length);
1410 }
1411 #endif /* PNG_LIBPNG_VER > 10011 */
1412
1413 #if defined(__cplusplus) || defined(c_plusplus)
1414 }
1415 #endif
1416
1417 #if PNG_LIBPNG_VER > 10011
1418 /*
1419 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1420 %                                                                             %
1421 %                                                                             %
1422 %                                                                             %
1423 %   R e a d P N G I m a g e                                                   %
1424 %                                                                             %
1425 %                                                                             %
1426 %                                                                             %
1427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1428 %
1429 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1430 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1431 %  allocates the memory necessary for the new Image structure and returns a
1432 %  pointer to the new image or set of images.
1433 %
1434 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1435 %
1436 %  The format of the ReadPNGImage method is:
1437 %
1438 %      Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1439 %
1440 %  A description of each parameter follows:
1441 %
1442 %    o image_info: the image info.
1443 %
1444 %    o exception: return any errors or warnings in this structure.
1445 %
1446 %  To do, more or less in chronological order (as of version 5.5.2,
1447 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1448 %
1449 %    Get 16-bit cheap transparency working.
1450 %
1451 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1452 %
1453 %    Preserve all unknown and not-yet-handled known chunks found in input
1454 %    PNG file and copy them into output PNG files according to the PNG
1455 %    copying rules.
1456 %
1457 %    (At this point, PNG encoding should be in full MNG compliance)
1458 %
1459 %    Provide options for choice of background to use when the MNG BACK
1460 %    chunk is not present or is not mandatory (i.e., leave transparent,
1461 %    user specified, MNG BACK, PNG bKGD)
1462 %
1463 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1464 %    efficiently by linking in the duplicate frames.].
1465 %
1466 %    Decode and act on the MHDR simplicity profile (offer option to reject
1467 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1468 %
1469 %    Upgrade to full MNG without Delta-PNG.
1470 %
1471 %        o  BACK [done a while ago except for background image ID]
1472 %        o  MOVE [done 15 May 1999]
1473 %        o  CLIP [done 15 May 1999]
1474 %        o  DISC [done 19 May 1999]
1475 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1476 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1477 %        o  SHOW
1478 %        o  PAST
1479 %        o  BASI
1480 %        o  MNG-level tEXt/iTXt/zTXt
1481 %        o  pHYg
1482 %        o  pHYs
1483 %        o  sBIT
1484 %        o  bKGD
1485 %        o  iTXt (wait for libpng implementation).
1486 %
1487 %    Use the scene signature to discover when an identical scene is
1488 %    being reused, and just point to the original image->exception instead
1489 %    of storing another set of pixels.  This not specific to MNG
1490 %    but could be applied generally.
1491 %
1492 %    Upgrade to full MNG with Delta-PNG.
1493 %
1494 %    JNG tEXt/iTXt/zTXt
1495 %
1496 %    We will not attempt to read files containing the CgBI chunk.
1497 %    They are really Xcode files meant for display on the iPhone.
1498 %    These are not valid PNG files and it is impossible to recover
1499 %    the original PNG from files that have been converted to Xcode-PNG,
1500 %    since irretrievable loss of color data has occurred due to the
1501 %    use of premultiplied alpha.
1502 */
1503
1504 #if defined(__cplusplus) || defined(c_plusplus)
1505 extern "C" {
1506 #endif
1507
1508 /*
1509   This the function that does the actual reading of data.  It is
1510   the same as the one supplied in libpng, except that it receives the
1511   datastream from the ReadBlob() function instead of standard input.
1512 */
1513 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1514 {
1515   Image
1516     *image;
1517
1518   image=(Image *) png_get_io_ptr(png_ptr);
1519   if (length)
1520     {
1521       png_size_t
1522         check;
1523
1524       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1525       if (check != length)
1526         {
1527           char
1528             msg[MaxTextExtent];
1529
1530           (void) FormatLocaleString(msg,MaxTextExtent,
1531             "Expected %.20g bytes; found %.20g bytes",(double) length,
1532             (double) check);
1533           png_warning(png_ptr,msg);
1534           png_error(png_ptr,"Read Exception");
1535         }
1536     }
1537 }
1538
1539 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1540     !defined(PNG_MNG_FEATURES_SUPPORTED)
1541 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1542  * older than libpng-1.0.3a, which was the first to allow the empty
1543  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1544  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1545  * encountered after an empty PLTE, so we have to look ahead for bKGD
1546  * chunks and remove them from the datastream that is passed to libpng,
1547  * and store their contents for later use.
1548  */
1549 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1550 {
1551   MngInfo
1552     *mng_info;
1553
1554   Image
1555     *image;
1556
1557   png_size_t
1558     check;
1559
1560   register ssize_t
1561     i;
1562
1563   i=0;
1564   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1565   image=(Image *) mng_info->image;
1566   while (mng_info->bytes_in_read_buffer && length)
1567   {
1568     data[i]=mng_info->read_buffer[i];
1569     mng_info->bytes_in_read_buffer--;
1570     length--;
1571     i++;
1572   }
1573   if (length)
1574     {
1575       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1576
1577       if (check != length)
1578         png_error(png_ptr,"Read Exception");
1579
1580       if (length == 4)
1581         {
1582           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1583               (data[3] == 0))
1584             {
1585               check=(png_size_t) ReadBlob(image,(size_t) length,
1586                 (char *) mng_info->read_buffer);
1587               mng_info->read_buffer[4]=0;
1588               mng_info->bytes_in_read_buffer=4;
1589               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1590                 mng_info->found_empty_plte=MagickTrue;
1591               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1592                 {
1593                   mng_info->found_empty_plte=MagickFalse;
1594                   mng_info->have_saved_bkgd_index=MagickFalse;
1595                 }
1596             }
1597
1598           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1599               (data[3] == 1))
1600             {
1601               check=(png_size_t) ReadBlob(image,(size_t) length,
1602                 (char *) mng_info->read_buffer);
1603               mng_info->read_buffer[4]=0;
1604               mng_info->bytes_in_read_buffer=4;
1605               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1606                 if (mng_info->found_empty_plte)
1607                   {
1608                     /*
1609                       Skip the bKGD data byte and CRC.
1610                     */
1611                     check=(png_size_t)
1612                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1613                     check=(png_size_t) ReadBlob(image,(size_t) length,
1614                       (char *) mng_info->read_buffer);
1615                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1616                     mng_info->have_saved_bkgd_index=MagickTrue;
1617                     mng_info->bytes_in_read_buffer=0;
1618                   }
1619             }
1620         }
1621     }
1622 }
1623 #endif
1624
1625 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1626 {
1627   Image
1628     *image;
1629
1630   image=(Image *) png_get_io_ptr(png_ptr);
1631   if (length)
1632     {
1633       png_size_t
1634         check;
1635
1636       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1637
1638       if (check != length)
1639         png_error(png_ptr,"WriteBlob Failed");
1640     }
1641 }
1642
1643 static void png_flush_data(png_structp png_ptr)
1644 {
1645   (void) png_ptr;
1646 }
1647
1648 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1649 static int PalettesAreEqual(Image *a,Image *b)
1650 {
1651   ssize_t
1652     i;
1653
1654   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1655     return((int) MagickFalse);
1656
1657   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1658     return((int) MagickFalse);
1659
1660   if (a->colors != b->colors)
1661     return((int) MagickFalse);
1662
1663   for (i=0; i < (ssize_t) a->colors; i++)
1664   {
1665     if ((a->colormap[i].red != b->colormap[i].red) ||
1666         (a->colormap[i].green != b->colormap[i].green) ||
1667         (a->colormap[i].blue != b->colormap[i].blue))
1668       return((int) MagickFalse);
1669   }
1670
1671   return((int) MagickTrue);
1672 }
1673 #endif
1674
1675 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1676 {
1677   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1678       mng_info->exists[i] && !mng_info->frozen[i])
1679     {
1680 #ifdef MNG_OBJECT_BUFFERS
1681       if (mng_info->ob[i] != (MngBuffer *) NULL)
1682         {
1683           if (mng_info->ob[i]->reference_count > 0)
1684             mng_info->ob[i]->reference_count--;
1685
1686           if (mng_info->ob[i]->reference_count == 0)
1687             {
1688               if (mng_info->ob[i]->image != (Image *) NULL)
1689                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1690
1691               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1692             }
1693         }
1694       mng_info->ob[i]=(MngBuffer *) NULL;
1695 #endif
1696       mng_info->exists[i]=MagickFalse;
1697       mng_info->invisible[i]=MagickFalse;
1698       mng_info->viewable[i]=MagickFalse;
1699       mng_info->frozen[i]=MagickFalse;
1700       mng_info->x_off[i]=0;
1701       mng_info->y_off[i]=0;
1702       mng_info->object_clip[i].left=0;
1703       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1704       mng_info->object_clip[i].top=0;
1705       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1706     }
1707 }
1708
1709 static void MngInfoFreeStruct(MngInfo *mng_info,
1710     MagickBooleanType *have_mng_structure)
1711 {
1712   if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1713     {
1714       register ssize_t
1715         i;
1716
1717       for (i=1; i < MNG_MAX_OBJECTS; i++)
1718         MngInfoDiscardObject(mng_info,i);
1719
1720       if (mng_info->global_plte != (png_colorp) NULL)
1721         mng_info->global_plte=(png_colorp)
1722           RelinquishMagickMemory(mng_info->global_plte);
1723
1724       mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1725       *have_mng_structure=MagickFalse;
1726     }
1727 }
1728
1729 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1730 {
1731   MngBox
1732     box;
1733
1734   box=box1;
1735   if (box.left < box2.left)
1736     box.left=box2.left;
1737
1738   if (box.top < box2.top)
1739     box.top=box2.top;
1740
1741   if (box.right > box2.right)
1742     box.right=box2.right;
1743
1744   if (box.bottom > box2.bottom)
1745     box.bottom=box2.bottom;
1746
1747   return box;
1748 }
1749
1750 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1751 {
1752    MngBox
1753       box;
1754
1755   /*
1756     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1757   */
1758   box.left=(ssize_t) ((p[0]  << 24) | (p[1]  << 16) | (p[2]  << 8) | p[3]);
1759   box.right=(ssize_t) ((p[4]  << 24) | (p[5]  << 16) | (p[6]  << 8) | p[7]);
1760   box.top=(ssize_t) ((p[8]  << 24) | (p[9]  << 16) | (p[10] << 8) | p[11]);
1761   box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1762   if (delta_type != 0)
1763     {
1764       box.left+=previous_box.left;
1765       box.right+=previous_box.right;
1766       box.top+=previous_box.top;
1767       box.bottom+=previous_box.bottom;
1768     }
1769
1770   return(box);
1771 }
1772
1773 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1774   unsigned char *p)
1775 {
1776   MngPair
1777     pair;
1778   /*
1779     Read two ssize_ts from CLON, MOVE or PAST chunk
1780   */
1781   pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1782   pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1783
1784   if (delta_type != 0)
1785     {
1786       pair.a+=previous_pair.a;
1787       pair.b+=previous_pair.b;
1788     }
1789
1790   return(pair);
1791 }
1792
1793 static long mng_get_long(unsigned char *p)
1794 {
1795   return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1796 }
1797
1798 typedef struct _PNGErrorInfo
1799 {
1800   Image
1801     *image;
1802
1803   ExceptionInfo
1804     *exception;
1805 } PNGErrorInfo;
1806
1807 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1808 {
1809   ExceptionInfo
1810     *exception;
1811
1812   Image
1813     *image;
1814
1815   PNGErrorInfo
1816     *error_info;
1817
1818   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1819   image=error_info->image;
1820   exception=error_info->exception;
1821
1822   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1823     "  libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1824
1825   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1826     "`%s'",image->filename);
1827
1828 #if (PNG_LIBPNG_VER < 10500)
1829   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1830    * are building with libpng-1.4.x and can be ignored.
1831    */
1832   longjmp(ping->jmpbuf,1);
1833 #else
1834   png_longjmp(ping,1);
1835 #endif
1836 }
1837
1838 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1839 {
1840   ExceptionInfo
1841     *exception;
1842
1843   Image
1844     *image;
1845
1846   PNGErrorInfo
1847     *error_info;
1848
1849   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1850     png_error(ping, message);
1851
1852   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1853   image=error_info->image;
1854   exception=error_info->exception;
1855   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1856     "  libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1857
1858   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1859     message,"`%s'",image->filename);
1860 }
1861
1862 #ifdef PNG_USER_MEM_SUPPORTED
1863 #if PNG_LIBPNG_VER >= 10400
1864 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1865 #else
1866 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1867 #endif
1868 {
1869   (void) png_ptr;
1870   return((png_voidp) AcquireMagickMemory((size_t) size));
1871 }
1872
1873 /*
1874   Free a pointer.  It is removed from the list at the same time.
1875 */
1876 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1877 {
1878   (void) png_ptr;
1879   ptr=RelinquishMagickMemory(ptr);
1880   return((png_free_ptr) NULL);
1881 }
1882 #endif
1883
1884 #if defined(__cplusplus) || defined(c_plusplus)
1885 }
1886 #endif
1887
1888 static int
1889 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1890    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1891 {
1892   register ssize_t
1893     i;
1894
1895   register unsigned char
1896     *dp;
1897
1898   register png_charp
1899     sp;
1900
1901   png_uint_32
1902     length,
1903     nibbles;
1904
1905   StringInfo
1906     *profile;
1907
1908   const unsigned char
1909     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1910                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1911                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1912                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1913                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1914                  13,14,15};
1915
1916   sp=text[ii].text+1;
1917   /* look for newline */
1918   while (*sp != '\n')
1919      sp++;
1920
1921   /* look for length */
1922   while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1923      sp++;
1924
1925   length=(png_uint_32) StringToLong(sp);
1926
1927   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1928        "      length: %lu",(unsigned long) length);
1929
1930   while (*sp != ' ' && *sp != '\n')
1931      sp++;
1932
1933   /* allocate space */
1934   if (length == 0)
1935   {
1936     png_warning(ping,"invalid profile length");
1937     return(MagickFalse);
1938   }
1939
1940   profile=BlobToStringInfo((const void *) NULL,length);
1941
1942   if (profile == (StringInfo *) NULL)
1943   {
1944     png_warning(ping, "unable to copy profile");
1945     return(MagickFalse);
1946   }
1947
1948   /* copy profile, skipping white space and column 1 "=" signs */
1949   dp=GetStringInfoDatum(profile);
1950   nibbles=length*2;
1951
1952   for (i=0; i < (ssize_t) nibbles; i++)
1953   {
1954     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1955     {
1956       if (*sp == '\0')
1957         {
1958           png_warning(ping, "ran out of profile data");
1959           profile=DestroyStringInfo(profile);
1960           return(MagickFalse);
1961         }
1962       sp++;
1963     }
1964
1965     if (i%2 == 0)
1966       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1967
1968     else
1969       (*dp++)+=unhex[(int) *sp++];
1970   }
1971   /*
1972     We have already read "Raw profile type.
1973   */
1974   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1975   profile=DestroyStringInfo(profile);
1976
1977   if (image_info->verbose)
1978     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1979
1980   return MagickTrue;
1981 }
1982
1983 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1984 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1985 {
1986   Image
1987     *image;
1988
1989
1990   /* The unknown chunk structure contains the chunk data:
1991      png_byte name[5];
1992      png_byte *data;
1993      png_size_t size;
1994
1995      Note that libpng has already taken care of the CRC handling.
1996   */
1997
1998   LogMagickEvent(CoderEvent,GetMagickModule(),
1999      " read_vpag_chunk: found %c%c%c%c chunk",
2000        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
2001
2002   if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
2003       chunk->name[2] != 65 ||chunk-> name[3] != 103)
2004     return(0); /* Did not recognize */
2005
2006   /* recognized vpAg */
2007
2008   if (chunk->size != 9)
2009     return(-1); /* Error return */
2010
2011   if (chunk->data[8] != 0)
2012     return(0);  /* ImageMagick requires pixel units */
2013
2014   image=(Image *) png_get_user_chunk_ptr(ping);
2015
2016   image->page.width=(size_t) ((chunk->data[0] << 24) |
2017      (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
2018
2019   image->page.height=(size_t) ((chunk->data[4] << 24) |
2020      (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
2021
2022   /* Return one of the following: */
2023      /* return(-n);  chunk had an error */
2024      /* return(0);  did not recognize */
2025      /* return(n);  success */
2026
2027   return(1);
2028
2029 }
2030 #endif
2031
2032 /*
2033 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2034 %                                                                             %
2035 %                                                                             %
2036 %                                                                             %
2037 %   R e a d O n e P N G I m a g e                                             %
2038 %                                                                             %
2039 %                                                                             %
2040 %                                                                             %
2041 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2042 %
2043 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2044 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
2045 %  necessary for the new Image structure and returns a pointer to the new
2046 %  image.
2047 %
2048 %  The format of the ReadOnePNGImage method is:
2049 %
2050 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2051 %         ExceptionInfo *exception)
2052 %
2053 %  A description of each parameter follows:
2054 %
2055 %    o mng_info: Specifies a pointer to a MngInfo structure.
2056 %
2057 %    o image_info: the image info.
2058 %
2059 %    o exception: return any errors or warnings in this structure.
2060 %
2061 */
2062 static Image *ReadOnePNGImage(MngInfo *mng_info,
2063     const ImageInfo *image_info, ExceptionInfo *exception)
2064 {
2065   /* Read one PNG image */
2066
2067   /* To do: Read the tIME chunk into the date:modify property */
2068   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2069
2070   Image
2071     *image;
2072
2073   char
2074     im_vers[32],
2075     libpng_runv[32],
2076     libpng_vers[32],
2077     zlib_runv[32],
2078     zlib_vers[32];
2079
2080   int
2081     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2082     num_raw_profiles,
2083     num_text,
2084     num_text_total,
2085     num_passes,
2086     number_colors,
2087     pass,
2088     ping_bit_depth,
2089     ping_color_type,
2090     ping_file_depth,
2091     ping_interlace_method,
2092     ping_compression_method,
2093     ping_filter_method,
2094     ping_num_trans,
2095     unit_type;
2096
2097   double
2098     file_gamma;
2099
2100   MagickBooleanType
2101     logging,
2102     ping_found_cHRM,
2103     ping_found_gAMA,
2104     ping_found_iCCP,
2105     ping_found_sRGB,
2106     ping_found_sRGB_cHRM,
2107     ping_preserve_iCCP,
2108     status;
2109
2110   MemoryInfo
2111     *volatile pixel_info;
2112
2113   PixelInfo
2114     transparent_color;
2115
2116   PNGErrorInfo
2117     error_info;
2118
2119   png_bytep
2120      ping_trans_alpha;
2121
2122   png_color_16p
2123      ping_background,
2124      ping_trans_color;
2125
2126   png_info
2127     *end_info,
2128     *ping_info;
2129
2130   png_struct
2131     *ping;
2132
2133   png_textp
2134     text;
2135
2136   png_uint_32
2137     ping_height,
2138     ping_width,
2139     x_resolution,
2140     y_resolution;
2141
2142   QuantumInfo
2143     *quantum_info;
2144
2145   ssize_t
2146     ping_rowbytes,
2147     y;
2148
2149   register unsigned char
2150     *p;
2151
2152   register ssize_t
2153     i,
2154     x;
2155
2156   register Quantum
2157     *q;
2158
2159   size_t
2160     length,
2161     row_offset;
2162
2163   ssize_t
2164     j;
2165
2166   unsigned char
2167     *ping_pixels;
2168
2169 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2170   png_byte unused_chunks[]=
2171   {
2172     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2173     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2174     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2175     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2176     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2177     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2178 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2179                           /* ignore the APNG chunks */
2180      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2181     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2182     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2183 #endif
2184   };
2185 #endif
2186
2187   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2188     "  Enter ReadOnePNGImage()");
2189
2190   /* Define these outside of the following "if logging()" block so they will
2191    * show in debuggers.
2192    */
2193   *im_vers='\0';
2194   (void) ConcatenateMagickString(im_vers,
2195          MagickLibVersionText,32);
2196   (void) ConcatenateMagickString(im_vers,
2197          MagickLibAddendum,32);
2198
2199   *libpng_vers='\0';
2200   (void) ConcatenateMagickString(libpng_vers,
2201          PNG_LIBPNG_VER_STRING,32);
2202   *libpng_runv='\0';
2203   (void) ConcatenateMagickString(libpng_runv,
2204          png_get_libpng_ver(NULL),32);
2205
2206   *zlib_vers='\0';
2207   (void) ConcatenateMagickString(zlib_vers,
2208          ZLIB_VERSION,32);
2209   *zlib_runv='\0';
2210   (void) ConcatenateMagickString(zlib_runv,
2211          zlib_version,32);
2212
2213   if (logging)
2214     {
2215        LogMagickEvent(CoderEvent,GetMagickModule(),"    IM version     = %s",
2216            im_vers);
2217        LogMagickEvent(CoderEvent,GetMagickModule(),"    Libpng version = %s",
2218            libpng_vers);
2219        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2220        {
2221        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2222            libpng_runv);
2223        }
2224        LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
2225            zlib_vers);
2226        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2227        {
2228        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2229            zlib_runv);
2230        }
2231     }
2232
2233 #if (PNG_LIBPNG_VER < 10200)
2234   if (image_info->verbose)
2235     printf("Your PNG library (libpng-%s) is rather old.\n",
2236        PNG_LIBPNG_VER_STRING);
2237 #endif
2238
2239 #if (PNG_LIBPNG_VER >= 10400)
2240 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
2241   if (image_info->verbose)
2242     {
2243       printf("Your PNG library (libpng-%s) is an old beta version.\n",
2244            PNG_LIBPNG_VER_STRING);
2245       printf("Please update it.\n");
2246     }
2247 #  endif
2248 #endif
2249
2250
2251   quantum_info = (QuantumInfo *) NULL;
2252   image=mng_info->image;
2253
2254   if (logging != MagickFalse)
2255   {
2256     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2257       "    Before reading:");
2258
2259     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2260       "      image->alpha_trait=%d",(int) image->alpha_trait);
2261
2262     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2263       "      image->rendering_intent=%d",(int) image->rendering_intent);
2264
2265     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2266       "      image->colorspace=%d",(int) image->colorspace);
2267
2268     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2269       "      image->gamma=%f", image->gamma);
2270   }
2271   intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2272
2273   /* Set to an out-of-range color unless tRNS chunk is present */
2274   transparent_color.red=65537;
2275   transparent_color.green=65537;
2276   transparent_color.blue=65537;
2277   transparent_color.alpha=65537;
2278
2279   number_colors=0;
2280   num_text = 0;
2281   num_text_total = 0;
2282   num_raw_profiles = 0;
2283
2284   ping_found_cHRM = MagickFalse;
2285   ping_found_gAMA = MagickFalse;
2286   ping_found_iCCP = MagickFalse;
2287   ping_found_sRGB = MagickFalse;
2288   ping_found_sRGB_cHRM = MagickFalse;
2289   ping_preserve_iCCP = MagickFalse;
2290
2291   {
2292     const char
2293       *value;
2294
2295     value=GetImageOption(image_info,"png:preserve-iCCP");
2296
2297     if (value == NULL)
2298        value=GetImageArtifact(image,"png:preserve-iCCP");
2299
2300     if (value != NULL)
2301        ping_preserve_iCCP=MagickTrue;
2302   }
2303
2304   /*
2305     Allocate the PNG structures
2306   */
2307 #ifdef PNG_USER_MEM_SUPPORTED
2308  error_info.image=image;
2309  error_info.exception=exception;
2310  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2311    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2312    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2313 #else
2314   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2315     MagickPNGErrorHandler,MagickPNGWarningHandler);
2316 #endif
2317   if (ping == (png_struct *) NULL)
2318     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2319
2320   ping_info=png_create_info_struct(ping);
2321
2322   if (ping_info == (png_info *) NULL)
2323     {
2324       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2325       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2326     }
2327
2328   end_info=png_create_info_struct(ping);
2329
2330   if (end_info == (png_info *) NULL)
2331     {
2332       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2333       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2334     }
2335
2336   pixel_info=(MemoryInfo *) NULL;
2337
2338   if (setjmp(png_jmpbuf(ping)))
2339     {
2340       /*
2341         PNG image is corrupt.
2342       */
2343       png_destroy_read_struct(&ping,&ping_info,&end_info);
2344
2345 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2346       UnlockSemaphoreInfo(ping_semaphore);
2347 #endif
2348
2349       if (pixel_info != (MemoryInfo *) NULL)
2350         pixel_info=RelinquishVirtualMemory(pixel_info);
2351
2352       if (logging != MagickFalse)
2353         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2354           "  exit ReadOnePNGImage() with error.");
2355
2356       if (image != (Image *) NULL)
2357         {
2358           InheritException(exception,exception);
2359           image->columns=0;
2360         }
2361
2362       return(GetFirstImageInList(image));
2363     }
2364
2365   /* {  For navigation to end of SETJMP-protected block.  Within this
2366    *    block, use png_error() instead of Throwing an Exception, to ensure
2367    *    that libpng is able to clean up, and that the semaphore is unlocked.
2368    */
2369
2370 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2371   LockSemaphoreInfo(ping_semaphore);
2372 #endif
2373
2374 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2375   /* Allow benign errors */
2376   png_set_benign_errors(ping, 1);
2377 #endif
2378
2379   /*
2380     Prepare PNG for reading.
2381   */
2382
2383   mng_info->image_found++;
2384   png_set_sig_bytes(ping,8);
2385
2386   if (LocaleCompare(image_info->magick,"MNG") == 0)
2387     {
2388 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2389       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2390       png_set_read_fn(ping,image,png_get_data);
2391 #else
2392 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2393       png_permit_empty_plte(ping,MagickTrue);
2394       png_set_read_fn(ping,image,png_get_data);
2395 #else
2396       mng_info->image=image;
2397       mng_info->bytes_in_read_buffer=0;
2398       mng_info->found_empty_plte=MagickFalse;
2399       mng_info->have_saved_bkgd_index=MagickFalse;
2400       png_set_read_fn(ping,mng_info,mng_get_data);
2401 #endif
2402 #endif
2403     }
2404
2405   else
2406     png_set_read_fn(ping,image,png_get_data);
2407
2408 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2409   /* Ignore unused chunks and all unknown chunks except for vpAg */
2410 #if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2411   png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2412 #else
2413   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2414 #endif
2415   png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2416   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2417      (int)sizeof(unused_chunks)/5);
2418   /* Callback for other unknown chunks */
2419   png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2420 #endif
2421
2422 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2423 #  if (PNG_LIBPNG_VER >= 10400)
2424     /* Limit the size of the chunk storage cache used for sPLT, text,
2425      * and unknown chunks.
2426      */
2427     png_set_chunk_cache_max(ping, 32767);
2428 #  endif
2429 #endif
2430
2431 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2432     /* Disable new libpng-1.5.10 feature */
2433     png_set_check_for_invalid_index (ping, 0);
2434 #endif
2435
2436 #if (PNG_LIBPNG_VER < 10400)
2437 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2438    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2439   /* Disable thread-unsafe features of pnggccrd */
2440   if (png_access_version_number() >= 10200)
2441   {
2442     png_uint_32 mmx_disable_mask=0;
2443     png_uint_32 asm_flags;
2444
2445     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2446                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2447                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2448                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2449     asm_flags=png_get_asm_flags(ping);
2450     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2451   }
2452 #  endif
2453 #endif
2454
2455   png_read_info(ping,ping_info);
2456
2457   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2458                &ping_bit_depth,&ping_color_type,
2459                &ping_interlace_method,&ping_compression_method,
2460                &ping_filter_method);
2461
2462   ping_file_depth = ping_bit_depth;
2463
2464   /* Swap bytes if requested */
2465   if (ping_file_depth == 16)
2466   {
2467      const char
2468        *value;
2469  
2470      value=GetImageOption(image_info,"png:swap-bytes");
2471  
2472      if (value == NULL)
2473         value=GetImageArtifact(image,"png:swap-bytes");
2474  
2475      if (value != NULL)
2476         png_set_swap(ping);
2477   }
2478
2479   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2480   {
2481       char
2482         msg[MaxTextExtent];
2483
2484       (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2485       (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2486
2487       (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2488       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2489   }
2490
2491   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2492                       &ping_trans_color);
2493
2494   (void) png_get_bKGD(ping, ping_info, &ping_background);
2495
2496   if (ping_bit_depth < 8)
2497     {
2498        png_set_packing(ping);
2499        ping_bit_depth = 8;
2500     }
2501
2502   image->depth=ping_bit_depth;
2503   image->depth=GetImageQuantumDepth(image,MagickFalse);
2504   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2505
2506   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2507       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2508     {
2509       image->rendering_intent=UndefinedIntent;
2510       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2511       image->gamma=1.000;
2512       (void) ResetMagickMemory(&image->chromaticity,0,
2513         sizeof(image->chromaticity));
2514     }
2515
2516   if (logging != MagickFalse)
2517     {
2518       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2519         "    PNG width: %.20g, height: %.20g",
2520         (double) ping_width, (double) ping_height);
2521
2522       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2523         "    PNG color_type: %d, bit_depth: %d",
2524         ping_color_type, ping_bit_depth);
2525
2526       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2527         "    PNG compression_method: %d",
2528         ping_compression_method);
2529
2530       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2531         "    PNG interlace_method: %d, filter_method: %d",
2532         ping_interlace_method,ping_filter_method);
2533     }
2534
2535   if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2536     {
2537       ping_found_iCCP=MagickTrue;
2538       if (logging != MagickFalse)
2539         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2540           "    Found PNG iCCP chunk.");
2541     }
2542
2543   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2544     {
2545       ping_found_gAMA=MagickTrue;
2546       if (logging != MagickFalse)
2547         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2548           "    Found PNG gAMA chunk.");
2549     }
2550
2551   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2552     {
2553       ping_found_cHRM=MagickTrue;
2554       if (logging != MagickFalse)
2555         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2556           "    Found PNG cHRM chunk.");
2557     }
2558
2559   if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2560       PNG_INFO_sRGB))
2561     {
2562       ping_found_sRGB=MagickTrue;
2563       if (logging != MagickFalse)
2564         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2565           "    Found PNG sRGB chunk.");
2566     }
2567
2568 #ifdef PNG_READ_iCCP_SUPPORTED
2569   if (ping_found_iCCP !=MagickTrue &&
2570       ping_found_sRGB != MagickTrue &&
2571       png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2572     {
2573       ping_found_iCCP=MagickTrue;
2574       if (logging != MagickFalse)
2575         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2576           "    Found PNG iCCP chunk.");
2577     }
2578
2579   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2580     {
2581       int
2582         compression;
2583
2584 #if (PNG_LIBPNG_VER < 10500)
2585       png_charp
2586         info;
2587 #else
2588       png_bytep
2589         info;
2590 #endif
2591
2592       png_charp
2593         name;
2594
2595       png_uint_32
2596         profile_length;
2597
2598       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2599         &profile_length);
2600
2601       if (profile_length != 0)
2602         {
2603           StringInfo
2604             *profile;
2605
2606           if (logging != MagickFalse)
2607             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2608               "    Reading PNG iCCP chunk.");
2609
2610           profile=BlobToStringInfo(info,profile_length);
2611
2612           if (profile == (StringInfo *) NULL)
2613           {
2614             png_warning(ping, "ICC profile is NULL");
2615             profile=DestroyStringInfo(profile);
2616           }
2617           else
2618           {
2619             if (ping_preserve_iCCP == MagickFalse)
2620             {
2621                  int
2622                    icheck,
2623                    got_crc=0;
2624
2625
2626                  png_uint_32
2627                    length,
2628                    profile_crc=0;
2629
2630                  unsigned char
2631                    *data;
2632
2633                  length=(png_uint_32) GetStringInfoLength(profile);
2634
2635                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2636                  {
2637                    if (length == sRGB_info[icheck].len)
2638                    {
2639                      if (got_crc == 0)
2640                      {
2641                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2642                          "    Got a %lu-byte ICC profile (potentially sRGB)",
2643                          (unsigned long) length);
2644
2645                        data=GetStringInfoDatum(profile);
2646                        profile_crc=crc32(0,data,length);
2647
2648                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2649                            "      with crc=%8x",(unsigned int) profile_crc);
2650                        got_crc++;
2651                      }
2652
2653                      if (profile_crc == sRGB_info[icheck].crc)
2654                      {
2655                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2656                             "      It is sRGB with rendering intent = %s",
2657                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
2658                              sRGB_info[icheck].intent));
2659                         if (image->rendering_intent==UndefinedIntent)
2660                         {
2661                           image->rendering_intent=
2662                           Magick_RenderingIntent_from_PNG_RenderingIntent(
2663                              sRGB_info[icheck].intent);
2664                         }
2665                         break;
2666                      }
2667                    }
2668                  }
2669                  if (sRGB_info[icheck].len == 0)
2670                  {
2671                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2672                         "    Got a %lu-byte ICC profile not recognized as sRGB",
2673                         (unsigned long) length);
2674                     (void) SetImageProfile(image,"icc",profile,exception);
2675                  }
2676             }
2677             else /* Preserve-iCCP */
2678             {
2679                     (void) SetImageProfile(image,"icc",profile,exception);
2680             }
2681
2682             profile=DestroyStringInfo(profile);
2683           }
2684       }
2685     }
2686 #endif
2687
2688 #if defined(PNG_READ_sRGB_SUPPORTED)
2689   {
2690     if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2691         PNG_INFO_sRGB))
2692     {
2693       if (png_get_sRGB(ping,ping_info,&intent))
2694       {
2695         if (image->rendering_intent == UndefinedIntent)
2696           image->rendering_intent=
2697              Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2698
2699         if (logging != MagickFalse)
2700           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2701             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2702       }
2703     }
2704
2705     else if (mng_info->have_global_srgb)
2706       {
2707         if (image->rendering_intent == UndefinedIntent)
2708           image->rendering_intent=
2709             Magick_RenderingIntent_from_PNG_RenderingIntent
2710             (mng_info->global_srgb_intent);
2711       }
2712   }
2713 #endif
2714
2715
2716   {
2717      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2718        if (mng_info->have_global_gama)
2719          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2720
2721      if (png_get_gAMA(ping,ping_info,&file_gamma))
2722        {
2723          image->gamma=(float) file_gamma;
2724          if (logging != MagickFalse)
2725            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2726              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2727        }
2728   }
2729
2730   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2731     {
2732       if (mng_info->have_global_chrm != MagickFalse)
2733         {
2734           (void) png_set_cHRM(ping,ping_info,
2735             mng_info->global_chrm.white_point.x,
2736             mng_info->global_chrm.white_point.y,
2737             mng_info->global_chrm.red_primary.x,
2738             mng_info->global_chrm.red_primary.y,
2739             mng_info->global_chrm.green_primary.x,
2740             mng_info->global_chrm.green_primary.y,
2741             mng_info->global_chrm.blue_primary.x,
2742             mng_info->global_chrm.blue_primary.y);
2743         }
2744     }
2745
2746   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2747     {
2748       (void) png_get_cHRM(ping,ping_info,
2749         &image->chromaticity.white_point.x,
2750         &image->chromaticity.white_point.y,
2751         &image->chromaticity.red_primary.x,
2752         &image->chromaticity.red_primary.y,
2753         &image->chromaticity.green_primary.x,
2754         &image->chromaticity.green_primary.y,
2755         &image->chromaticity.blue_primary.x,
2756         &image->chromaticity.blue_primary.y);
2757
2758        ping_found_cHRM=MagickTrue;
2759
2760        if (image->chromaticity.red_primary.x>0.6399f &&
2761            image->chromaticity.red_primary.x<0.6401f &&
2762            image->chromaticity.red_primary.y>0.3299f &&
2763            image->chromaticity.red_primary.y<0.3301f &&
2764            image->chromaticity.green_primary.x>0.2999f &&
2765            image->chromaticity.green_primary.x<0.3001f &&
2766            image->chromaticity.green_primary.y>0.5999f &&
2767            image->chromaticity.green_primary.y<0.6001f &&
2768            image->chromaticity.blue_primary.x>0.1499f &&
2769            image->chromaticity.blue_primary.x<0.1501f &&
2770            image->chromaticity.blue_primary.y>0.0599f &&
2771            image->chromaticity.blue_primary.y<0.0601f &&
2772            image->chromaticity.white_point.x>0.3126f &&
2773            image->chromaticity.white_point.x<0.3128f &&
2774            image->chromaticity.white_point.y>0.3289f &&
2775            image->chromaticity.white_point.y<0.3291f)
2776           ping_found_sRGB_cHRM=MagickTrue;
2777     }
2778
2779   if (image->rendering_intent != UndefinedIntent)
2780     {
2781       if (ping_found_sRGB != MagickTrue &&
2782           (ping_found_gAMA != MagickTrue ||
2783           (image->gamma > .45 && image->gamma < .46)) &&
2784           (ping_found_cHRM != MagickTrue ||
2785           ping_found_sRGB_cHRM != MagickFalse) &&
2786           ping_found_iCCP != MagickTrue)
2787       {
2788          png_set_sRGB(ping,ping_info,
2789             Magick_RenderingIntent_to_PNG_RenderingIntent
2790             (image->rendering_intent));
2791          file_gamma=1.000f/2.200f;
2792          ping_found_sRGB=MagickTrue;
2793          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2794            "    Setting sRGB as if in input");
2795       }
2796     }
2797
2798 #if defined(PNG_oFFs_SUPPORTED)
2799   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2800     {
2801       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2802       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2803
2804       if (logging != MagickFalse)
2805         if (image->page.x || image->page.y)
2806           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2807             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2808             image->page.x,(double) image->page.y);
2809     }
2810 #endif
2811 #if defined(PNG_pHYs_SUPPORTED)
2812   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2813     {
2814       if (mng_info->have_global_phys)
2815         {
2816           png_set_pHYs(ping,ping_info,
2817                        mng_info->global_x_pixels_per_unit,
2818                        mng_info->global_y_pixels_per_unit,
2819                        mng_info->global_phys_unit_type);
2820         }
2821     }
2822
2823   x_resolution=0;
2824   y_resolution=0;
2825   unit_type=0;
2826   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2827     {
2828       /*
2829         Set image resolution.
2830       */
2831       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2832         &unit_type);
2833       image->resolution.x=(double) x_resolution;
2834       image->resolution.y=(double) y_resolution;
2835
2836       if (unit_type == PNG_RESOLUTION_METER)
2837         {
2838           image->units=PixelsPerCentimeterResolution;
2839           image->resolution.x=(double) x_resolution/100.0;
2840           image->resolution.y=(double) y_resolution/100.0;
2841         }
2842
2843       if (logging != MagickFalse)
2844         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2845           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2846           (double) x_resolution,(double) y_resolution,unit_type);
2847     }
2848 #endif
2849
2850   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2851     {
2852       png_colorp
2853         palette;
2854
2855       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2856
2857       if ((number_colors == 0) &&
2858           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2859         {
2860           if (mng_info->global_plte_length)
2861             {
2862               png_set_PLTE(ping,ping_info,mng_info->global_plte,
2863                 (int) mng_info->global_plte_length);
2864
2865               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2866               {
2867                 if (mng_info->global_trns_length)
2868                   {
2869                     png_warning(ping,
2870                       "global tRNS has more entries than global PLTE");
2871                   }
2872                 else
2873                   {
2874                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
2875                        (int) mng_info->global_trns_length,NULL);
2876                   }
2877                }
2878 #ifdef PNG_READ_bKGD_SUPPORTED
2879               if (
2880 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2881                    mng_info->have_saved_bkgd_index ||
2882 #endif
2883                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2884                     {
2885                       png_color_16
2886                          background;
2887
2888 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2889                       if (mng_info->have_saved_bkgd_index)
2890                         background.index=mng_info->saved_bkgd_index;
2891 #endif
2892                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2893                         background.index=ping_background->index;
2894
2895                       background.red=(png_uint_16)
2896                         mng_info->global_plte[background.index].red;
2897
2898                       background.green=(png_uint_16)
2899                         mng_info->global_plte[background.index].green;
2900
2901                       background.blue=(png_uint_16)
2902                         mng_info->global_plte[background.index].blue;
2903
2904                       background.gray=(png_uint_16)
2905                         mng_info->global_plte[background.index].green;
2906
2907                       png_set_bKGD(ping,ping_info,&background);
2908                     }
2909 #endif
2910                 }
2911               else
2912                 png_error(ping,"No global PLTE in file");
2913             }
2914         }
2915
2916 #ifdef PNG_READ_bKGD_SUPPORTED
2917   if (mng_info->have_global_bkgd &&
2918           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2919       image->background_color=mng_info->mng_global_bkgd;
2920
2921   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2922     {
2923       unsigned int
2924         bkgd_scale;
2925
2926       /*
2927         Set image background color.
2928       */
2929       if (logging != MagickFalse)
2930         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2931           "    Reading PNG bKGD chunk.");
2932
2933       /* Scale background components to 16-bit, then scale
2934        * to quantum depth
2935        */
2936         if (logging != MagickFalse)
2937           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2938             "    raw ping_background=(%d,%d,%d).",ping_background->red,
2939             ping_background->green,ping_background->blue);
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             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2962               "    bkgd_scale=%d.",bkgd_scale);
2963
2964             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2965               "    ping_background=(%d,%d,%d).",ping_background->red,
2966               ping_background->green,ping_background->blue);
2967           }
2968
2969         image->background_color.red=
2970             ScaleShortToQuantum(ping_background->red);
2971
2972         image->background_color.green=
2973             ScaleShortToQuantum(ping_background->green);
2974
2975         image->background_color.blue=
2976           ScaleShortToQuantum(ping_background->blue);
2977
2978         image->background_color.alpha=OpaqueAlpha;
2979
2980         if (logging != MagickFalse)
2981           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2982             "    image->background_color=(%.20g,%.20g,%.20g).",
2983             (double) image->background_color.red,
2984             (double) image->background_color.green,
2985             (double) image->background_color.blue);
2986     }
2987 #endif /* PNG_READ_bKGD_SUPPORTED */
2988
2989   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2990     {
2991       /*
2992         Image has a tRNS chunk.
2993       */
2994       int
2995         max_sample;
2996
2997       size_t
2998         one=1;
2999
3000       if (logging != MagickFalse)
3001         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3002           "    Reading PNG tRNS chunk.");
3003
3004       max_sample = (int) ((one << ping_file_depth) - 1);
3005
3006       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3007           (int)ping_trans_color->gray > max_sample) ||
3008           (ping_color_type == PNG_COLOR_TYPE_RGB &&
3009           ((int)ping_trans_color->red > max_sample ||
3010           (int)ping_trans_color->green > max_sample ||
3011           (int)ping_trans_color->blue > max_sample)))
3012         {
3013           if (logging != MagickFalse)
3014             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3015               "    Ignoring PNG tRNS chunk with out-of-range sample.");
3016           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3017           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3018           image->alpha_trait=UndefinedPixelTrait;
3019         }
3020       else
3021         {
3022           int
3023              scale_to_short;
3024
3025           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3026
3027           /* Scale transparent_color to short */
3028           transparent_color.red= scale_to_short*ping_trans_color->red;
3029           transparent_color.green= scale_to_short*ping_trans_color->green;
3030           transparent_color.blue= scale_to_short*ping_trans_color->blue;
3031           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3032
3033           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3034             {
3035               if (logging != MagickFalse)
3036               {
3037                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3038                   "    Raw tRNS graylevel is %d.",ping_trans_color->gray);
3039
3040                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3041                   "    scaled graylevel is %.20g.",transparent_color.alpha);
3042               }
3043               transparent_color.red=transparent_color.alpha;
3044               transparent_color.green=transparent_color.alpha;
3045               transparent_color.blue=transparent_color.alpha;
3046             }
3047         }
3048     }
3049 #if defined(PNG_READ_sBIT_SUPPORTED)
3050   if (mng_info->have_global_sbit)
3051     {
3052       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3053         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3054     }
3055 #endif
3056   num_passes=png_set_interlace_handling(ping);
3057
3058   png_read_update_info(ping,ping_info);
3059
3060   ping_rowbytes=png_get_rowbytes(ping,ping_info);
3061
3062   /*
3063     Initialize image structure.
3064   */
3065   mng_info->image_box.left=0;
3066   mng_info->image_box.right=(ssize_t) ping_width;
3067   mng_info->image_box.top=0;
3068   mng_info->image_box.bottom=(ssize_t) ping_height;
3069   if (mng_info->mng_type == 0)
3070     {
3071       mng_info->mng_width=ping_width;
3072       mng_info->mng_height=ping_height;
3073       mng_info->frame=mng_info->image_box;
3074       mng_info->clip=mng_info->image_box;
3075     }
3076
3077   else
3078     {
3079       image->page.y=mng_info->y_off[mng_info->object_id];
3080     }
3081
3082   image->compression=ZipCompression;
3083   image->columns=ping_width;
3084   image->rows=ping_height;
3085
3086   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3087       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3088     {
3089       if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
3090           image->gamma == 1.0) && ping_found_sRGB != MagickTrue)
3091         {
3092           /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
3093            * image->colorspace to GRAY, and reset image->chromaticity.
3094            */
3095           image->intensity = Rec709LuminancePixelIntensityMethod;
3096           SetImageColorspace(image,GRAYColorspace,exception);
3097         }
3098     }
3099   
3100   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3101       "  image->colorspace=%d",(int) image->colorspace);
3102
3103   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3104       ((int) ping_bit_depth < 16 &&
3105       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3106     {
3107       size_t
3108         one;
3109
3110       image->storage_class=PseudoClass;
3111       one=1;
3112       image->colors=one << ping_file_depth;
3113 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3114       if (image->colors > 256)
3115         image->colors=256;
3116 #else
3117       if (image->colors > 65536L)
3118         image->colors=65536L;
3119 #endif
3120       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3121         {
3122           png_colorp
3123             palette;
3124
3125           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3126           image->colors=(size_t) number_colors;
3127
3128           if (logging != MagickFalse)
3129             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3130               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3131         }
3132     }
3133
3134   if (image->storage_class == PseudoClass)
3135     {
3136       /*
3137         Initialize image colormap.
3138       */
3139       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3140         png_error(ping,"Memory allocation failed");
3141
3142       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3143         {
3144           png_colorp
3145             palette;
3146
3147           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3148
3149           for (i=0; i < (ssize_t) number_colors; i++)
3150           {
3151             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3152             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3153             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3154           }
3155
3156           for ( ; i < (ssize_t) image->colors; i++)
3157           {
3158             image->colormap[i].red=0;
3159             image->colormap[i].green=0;
3160             image->colormap[i].blue=0;
3161           }
3162         }
3163
3164       else
3165         {
3166           size_t
3167             scale;
3168
3169           scale=(QuantumRange/((1UL << ping_file_depth)-1));
3170
3171           if (scale < 1)
3172              scale=1;
3173
3174           for (i=0; i < (ssize_t) image->colors; i++)
3175           {
3176             image->colormap[i].red=(Quantum) (i*scale);
3177             image->colormap[i].green=(Quantum) (i*scale);
3178             image->colormap[i].blue=(Quantum) (i*scale);
3179           }
3180        }
3181     }
3182
3183    /* Set some properties for reporting by "identify" */
3184     {
3185       char
3186         msg[MaxTextExtent];
3187
3188      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3189         ping_interlace_method in value */
3190
3191      (void) FormatLocaleString(msg,MaxTextExtent,
3192          "%d, %d",(int) ping_width, (int) ping_height);
3193      (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3194
3195      (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
3196      (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3197
3198      (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
3199          (int) ping_color_type,
3200          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3201      (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3202
3203      if (ping_interlace_method == 0)
3204        {
3205          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
3206             (int) ping_interlace_method);
3207        }
3208      else if (ping_interlace_method == 1)
3209        {
3210          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
3211             (int) ping_interlace_method);
3212        }
3213      else
3214        {
3215          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
3216             (int) ping_interlace_method);
3217        }
3218        (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3219
3220      if (number_colors != 0)
3221        {
3222          (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3223             (int) number_colors);
3224          (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3225             exception);
3226        }
3227    }
3228
3229   /*
3230     Read image scanlines.
3231   */
3232   if (image->delay != 0)
3233     mng_info->scenes_found++;
3234
3235   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3236       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3237       (image_info->first_scene+image_info->number_scenes))))
3238     {
3239       /* This happens later in non-ping decodes */
3240       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3241         image->storage_class=DirectClass;
3242
3243       if (logging != MagickFalse)
3244         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3245           "    Skipping PNG image data for scene %.20g",(double)
3246           mng_info->scenes_found-1);
3247       png_destroy_read_struct(&ping,&ping_info,&end_info);
3248
3249 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3250       UnlockSemaphoreInfo(ping_semaphore);
3251 #endif
3252
3253       if (logging != MagickFalse)
3254         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3255           "  exit ReadOnePNGImage().");
3256
3257       return(image);
3258     }
3259
3260   if (logging != MagickFalse)
3261     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3262       "    Reading PNG IDAT chunk(s)");
3263
3264   if (num_passes > 1)
3265     pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3266       sizeof(*ping_pixels));
3267   else
3268     pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3269
3270   if (pixel_info == (MemoryInfo *) NULL)
3271     png_error(ping,"Memory allocation failed");
3272   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3273
3274   if (logging != MagickFalse)
3275     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3276       "    Converting PNG pixels to pixel packets");
3277   /*
3278     Convert PNG pixels to pixel packets.
3279   */
3280   quantum_info=AcquireQuantumInfo(image_info,image);
3281
3282   if (quantum_info == (QuantumInfo *) NULL)
3283      png_error(ping,"Failed to allocate quantum_info");
3284
3285   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3286
3287   {
3288
3289    MagickBooleanType
3290      found_transparent_pixel;
3291
3292   found_transparent_pixel=MagickFalse;
3293
3294   if (image->storage_class == DirectClass)
3295     {
3296       for (pass=0; pass < num_passes; pass++)
3297       {
3298         /*
3299           Convert image to DirectClass pixel packets.
3300         */
3301         image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3302             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3303             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3304             BlendPixelTrait : UndefinedPixelTrait;
3305
3306         for (y=0; y < (ssize_t) image->rows; y++)
3307         {
3308           if (num_passes > 1)
3309             row_offset=ping_rowbytes*y;
3310
3311           else
3312             row_offset=0;
3313
3314           png_read_row(ping,ping_pixels+row_offset,NULL);
3315
3316           if (pass < num_passes-1)
3317             continue;
3318
3319           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3320
3321           if (q == (Quantum *) NULL)
3322             break;
3323
3324           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3325             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3326               GrayQuantum,ping_pixels+row_offset,exception);
3327
3328           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3329             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3330               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3331
3332           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3333             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3334               RGBAQuantum,ping_pixels+row_offset,exception);
3335
3336           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3337             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3338               IndexQuantum,ping_pixels+row_offset,exception);
3339
3340           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3341             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3342               RGBQuantum,ping_pixels+row_offset,exception);
3343
3344           if (found_transparent_pixel == MagickFalse)
3345             {
3346               /* Is there a transparent pixel in the row? */
3347               if (y== 0 && logging != MagickFalse)
3348                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3349                    "    Looking for cheap transparent pixel");
3350
3351               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3352               {
3353                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3354                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3355                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3356                   {
3357                     if (logging != MagickFalse)
3358                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3359                         "    ...got one.");
3360
3361                     found_transparent_pixel = MagickTrue;
3362                     break;
3363                   }
3364                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3365                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3366                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3367                     transparent_color.red &&
3368                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3369                     transparent_color.green &&
3370                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3371                     transparent_color.blue))
3372                   {
3373                     if (logging != MagickFalse)
3374                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3375                         "    ...got one.");
3376                     found_transparent_pixel = MagickTrue;
3377                     break;
3378                   }
3379                 q+=GetPixelChannels(image);
3380               }
3381             }
3382
3383           if ((image->previous == (Image *) NULL) && (num_passes == 1))
3384             {
3385               status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3386                   image->rows);
3387
3388               if (status == MagickFalse)
3389                 break;
3390             }
3391           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3392             break;
3393         }
3394
3395         if ((image->previous == (Image *) NULL) && (num_passes != 1))
3396           {
3397             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3398             if (status == MagickFalse)
3399               break;
3400           }
3401       }
3402     }
3403
3404   else /* image->storage_class != DirectClass */
3405
3406     for (pass=0; pass < num_passes; pass++)
3407     {
3408       Quantum
3409         *quantum_scanline;
3410
3411       register Quantum
3412         *r;
3413
3414       /*
3415         Convert grayscale image to PseudoClass pixel packets.
3416       */
3417       if (logging != MagickFalse)
3418         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3419           "    Converting grayscale pixels to pixel packets");
3420
3421       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3422         BlendPixelTrait : UndefinedPixelTrait;
3423
3424       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3425         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3426         sizeof(*quantum_scanline));
3427
3428       if (quantum_scanline == (Quantum *) NULL)
3429         png_error(ping,"Memory allocation failed");
3430
3431       for (y=0; y < (ssize_t) image->rows; y++)
3432       {
3433         Quantum
3434            alpha;
3435
3436         if (num_passes > 1)
3437           row_offset=ping_rowbytes*y;
3438
3439         else
3440           row_offset=0;
3441
3442         png_read_row(ping,ping_pixels+row_offset,NULL);
3443
3444         if (pass < num_passes-1)
3445           continue;
3446
3447         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3448
3449         if (q == (Quantum *) NULL)
3450           break;
3451
3452         p=ping_pixels+row_offset;
3453         r=quantum_scanline;
3454
3455         switch (ping_bit_depth)
3456         {
3457           case 8:
3458           {
3459
3460             if (ping_color_type == 4)
3461               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3462               {
3463                 *r++=*p++;
3464
3465                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3466
3467                 SetPixelAlpha(image,alpha,q);
3468
3469                 if (alpha != OpaqueAlpha)
3470                   found_transparent_pixel = MagickTrue;
3471
3472                 q+=GetPixelChannels(image);
3473               }
3474
3475             else
3476               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3477                 *r++=*p++;
3478
3479             break;
3480           }
3481
3482           case 16:
3483           {
3484             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3485             {
3486 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3487               unsigned short
3488                 quantum;
3489
3490               if (image->colors > 256)
3491                 quantum=((*p++) << 8);
3492
3493               else
3494                 quantum=0;
3495
3496               quantum|=(*p++);
3497               *r=ScaleShortToQuantum(quantum);
3498               r++;
3499
3500               if (ping_color_type == 4)
3501                 {
3502                   if (image->colors > 256)
3503                     quantum=((*p++) << 8);
3504                   else
3505                     quantum=0;
3506
3507                   quantum|=(*p++);
3508
3509                   alpha=ScaleShortToQuantum(quantum);
3510                   SetPixelAlpha(image,alpha,q);
3511
3512                   if (alpha != OpaqueAlpha)
3513                     found_transparent_pixel = MagickTrue;
3514
3515                   q+=GetPixelChannels(image);
3516                 }
3517
3518 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3519               *r++=(*p++);
3520               p++; /* strip low byte */
3521
3522               if (ping_color_type == 4)
3523                 {
3524                   SetPixelAlpha(image,*p++,q);
3525
3526                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3527                     found_transparent_pixel = MagickTrue;
3528
3529                   p++;
3530                   q+=GetPixelChannels(image);
3531                 }
3532 #endif
3533             }
3534
3535             break;
3536           }
3537
3538           default:
3539             break;
3540         }
3541
3542         /*
3543           Transfer image scanline.
3544         */
3545         r=quantum_scanline;
3546
3547         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3548
3549         if (q == (Quantum *) NULL)
3550           break;
3551         for (x=0; x < (ssize_t) image->columns; x++)
3552         {
3553           SetPixelIndex(image,*r++,q);
3554           q+=GetPixelChannels(image);
3555         }
3556
3557         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3558           break;
3559
3560         if ((image->previous == (Image *) NULL) && (num_passes == 1))
3561           {
3562             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3563               image->rows);
3564
3565             if (status == MagickFalse)
3566               break;
3567           }
3568       }
3569
3570       if ((image->previous == (Image *) NULL) && (num_passes != 1))
3571         {
3572           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3573
3574           if (status == MagickFalse)
3575             break;
3576         }
3577
3578       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3579     }
3580
3581     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3582       UndefinedPixelTrait;
3583
3584     if (logging != MagickFalse)
3585       {
3586         if (found_transparent_pixel != MagickFalse)
3587           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3588             "    Found transparent pixel");
3589         else
3590           {
3591             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3592               "    No transparent pixel was found");
3593
3594             ping_color_type&=0x03;
3595           }
3596       }
3597     }
3598
3599   if (quantum_info != (QuantumInfo *) NULL)
3600     quantum_info=DestroyQuantumInfo(quantum_info);
3601
3602   if (image->storage_class == PseudoClass)
3603     {
3604       PixelTrait
3605         alpha_trait;
3606
3607       alpha_trait=image->alpha_trait;
3608       image->alpha_trait=UndefinedPixelTrait;
3609       (void) SyncImage(image,exception);
3610       image->alpha_trait=alpha_trait;
3611     }
3612
3613   png_read_end(ping,end_info);
3614
3615   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3616       (ssize_t) image_info->first_scene && image->delay != 0)
3617     {
3618       png_destroy_read_struct(&ping,&ping_info,&end_info);
3619       pixel_info=RelinquishVirtualMemory(pixel_info);
3620       image->colors=2;
3621       (void) SetImageBackgroundColor(image,exception);
3622 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3623       UnlockSemaphoreInfo(ping_semaphore);
3624 #endif
3625       if (logging != MagickFalse)
3626         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3627           "  exit ReadOnePNGImage() early.");
3628       return(image);
3629     }
3630
3631   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3632     {
3633       ClassType
3634         storage_class;
3635
3636       /*
3637         Image has a transparent background.
3638       */
3639       storage_class=image->storage_class;
3640       image->alpha_trait=BlendPixelTrait;
3641
3642 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3643
3644       if (storage_class == PseudoClass)
3645         {
3646           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3647             {
3648               for (x=0; x < ping_num_trans; x++)
3649               {
3650                  image->colormap[x].alpha_trait=BlendPixelTrait;
3651                  image->colormap[x].alpha =
3652                    ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3653               }
3654             }
3655
3656           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3657             {
3658               for (x=0; x < (int) image->colors; x++)
3659               {
3660                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3661                      transparent_color.alpha)
3662                  {
3663                     image->colormap[x].alpha_trait=BlendPixelTrait;
3664                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3665                  }
3666               }
3667             }
3668           (void) SyncImage(image,exception);
3669         }
3670
3671 #if 1 /* Should have already been done above, but glennrp problem P10
3672        * needs this.
3673        */
3674       else
3675         {
3676           for (y=0; y < (ssize_t) image->rows; y++)
3677           {
3678             image->storage_class=storage_class;
3679             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3680
3681             if (q == (Quantum *) NULL)
3682               break;
3683
3684
3685             /* Caution: on a Q8 build, this does not distinguish between
3686              * 16-bit colors that differ only in the low byte
3687              */
3688             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3689             {
3690               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3691                   transparent_color.red &&
3692                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3693                   transparent_color.green &&
3694                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3695                   transparent_color.blue)
3696                 {
3697                   SetPixelAlpha(image,TransparentAlpha,q);
3698                 }
3699
3700 #if 0 /* I have not found a case where this is needed. */
3701               else
3702                 {
3703                   SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3704                 }
3705 #endif
3706
3707               q+=GetPixelChannels(image);
3708             }
3709
3710             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3711                break;
3712           }
3713         }
3714 #endif
3715
3716       image->storage_class=DirectClass;
3717     }
3718
3719   for (j = 0; j < 2; j++)
3720   {
3721     if (j == 0)
3722       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3723           MagickTrue : MagickFalse;
3724     else
3725       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3726           MagickTrue : MagickFalse;
3727
3728     if (status != MagickFalse)
3729       for (i=0; i < (ssize_t) num_text; i++)
3730       {
3731         /* Check for a profile */
3732
3733         if (logging != MagickFalse)
3734           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3735             "    Reading PNG text chunk");
3736
3737         if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3738           {
3739             (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3740                (int) i,exception);
3741             num_raw_profiles++;
3742           }
3743
3744         else
3745           {
3746             char
3747               *value;
3748
3749             length=text[i].text_length;
3750             value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3751               sizeof(*value));
3752             if (value == (char *) NULL)
3753               {
3754                 png_error(ping,"Memory allocation failed");
3755                 break;
3756               }
3757             *value='\0';
3758             (void) ConcatenateMagickString(value,text[i].text,length+2);
3759
3760             /* Don't save "density" or "units" property if we have a pHYs
3761              * chunk
3762              */
3763             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3764                 (LocaleCompare(text[i].key,"density") != 0 &&
3765                 LocaleCompare(text[i].key,"units") != 0))
3766                (void) SetImageProperty(image,text[i].key,value,exception);
3767
3768             if (logging != MagickFalse)
3769             {
3770               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3771                 "      length: %lu",(unsigned long) length);
3772               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3773                 "      Keyword: %s",text[i].key);
3774             }
3775
3776             value=DestroyString(value);
3777           }
3778       }
3779       num_text_total += num_text;
3780     }
3781
3782 #ifdef MNG_OBJECT_BUFFERS
3783   /*
3784     Store the object if necessary.
3785   */
3786   if (object_id && !mng_info->frozen[object_id])
3787     {
3788       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3789         {
3790           /*
3791             create a new object buffer.
3792           */
3793           mng_info->ob[object_id]=(MngBuffer *)
3794             AcquireMagickMemory(sizeof(MngBuffer));
3795
3796           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3797             {
3798               mng_info->ob[object_id]->image=(Image *) NULL;
3799               mng_info->ob[object_id]->reference_count=1;
3800             }
3801         }
3802
3803       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3804           mng_info->ob[object_id]->frozen)
3805         {
3806           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3807              png_error(ping,"Memory allocation failed");
3808
3809           if (mng_info->ob[object_id]->frozen)
3810             png_error(ping,"Cannot overwrite frozen MNG object buffer");
3811         }
3812
3813       else
3814         {
3815
3816           if (mng_info->ob[object_id]->image != (Image *) NULL)
3817             mng_info->ob[object_id]->image=DestroyImage
3818                 (mng_info->ob[object_id]->image);
3819
3820           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3821             exception);
3822
3823           if (mng_info->ob[object_id]->image != (Image *) NULL)
3824             mng_info->ob[object_id]->image->file=(FILE *) NULL;
3825
3826           else
3827             png_error(ping, "Cloning image for object buffer failed");
3828
3829           if (ping_width > 250000L || ping_height > 250000L)
3830              png_error(ping,"PNG Image dimensions are too large.");
3831
3832           mng_info->ob[object_id]->width=ping_width;
3833           mng_info->ob[object_id]->height=ping_height;
3834           mng_info->ob[object_id]->color_type=ping_color_type;
3835           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3836           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3837           mng_info->ob[object_id]->compression_method=
3838              ping_compression_method;
3839           mng_info->ob[object_id]->filter_method=ping_filter_method;
3840
3841           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3842             {
3843               png_colorp
3844                 plte;
3845
3846               /*
3847                 Copy the PLTE to the object buffer.
3848               */
3849               png_get_PLTE(ping,ping_info,&plte,&number_colors);
3850               mng_info->ob[object_id]->plte_length=number_colors;
3851
3852               for (i=0; i < number_colors; i++)
3853               {
3854                 mng_info->ob[object_id]->plte[i]=plte[i];
3855               }
3856             }
3857
3858           else
3859               mng_info->ob[object_id]->plte_length=0;
3860         }
3861     }
3862 #endif
3863
3864    /* Set image->alpha_trait to MagickTrue if the input colortype supports
3865     * alpha or if a valid tRNS chunk is present, no matter whether there
3866     * is actual transparency present.
3867     */
3868     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3869         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3870         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3871         BlendPixelTrait : UndefinedPixelTrait;
3872
3873 #if 0  /* I'm not sure what's wrong here but it does not work. */
3874     if (image->alpha_trait == BlendPixelTrait)
3875     {
3876       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3877         (void) SetImageType(image,GrayscaleMatteType,exception);
3878
3879       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3880         (void) SetImageType(image,PaletteMatteType,exception);
3881
3882       else
3883         (void) SetImageType(image,TrueColorMatteType,exception);
3884     }
3885
3886     else
3887     {
3888       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3889         (void) SetImageType(image,GrayscaleType,exception);
3890
3891       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3892         (void) SetImageType(image,PaletteType,exception);
3893
3894       else
3895         (void) SetImageType(image,TrueColorType,exception);
3896     }
3897 #endif
3898
3899    /* Set more properties for identify to retrieve */
3900    {
3901      char
3902        msg[MaxTextExtent];
3903
3904      if (num_text_total != 0)
3905        {
3906          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3907          (void) FormatLocaleString(msg,MaxTextExtent,
3908             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3909          (void) SetImageProperty(image,"png:text",msg,
3910                 exception);
3911        }
3912
3913      if (num_raw_profiles != 0)
3914        {
3915          (void) FormatLocaleString(msg,MaxTextExtent,
3916             "%d were found", num_raw_profiles);
3917          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3918                 exception);
3919        }
3920
3921      if (ping_found_cHRM != MagickFalse)
3922        {
3923          (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3924             "chunk was found (see Chromaticity, above)");
3925          (void) SetImageProperty(image,"png:cHRM",msg,
3926                 exception);
3927        }
3928
3929      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3930        {
3931          (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3932             "chunk was found (see Background color, above)");
3933          (void) SetImageProperty(image,"png:bKGD",msg,
3934                 exception);
3935        }
3936
3937      (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3938         "chunk was found");
3939
3940 #if defined(PNG_iCCP_SUPPORTED)
3941      if (ping_found_iCCP != MagickFalse)
3942         (void) SetImageProperty(image,"png:iCCP",msg,
3943                 exception);
3944 #endif
3945
3946      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3947         (void) SetImageProperty(image,"png:tRNS",msg,
3948                 exception);
3949
3950 #if defined(PNG_sRGB_SUPPORTED)
3951      if (ping_found_sRGB != MagickFalse)
3952        {
3953          (void) FormatLocaleString(msg,MaxTextExtent,
3954             "intent=%d (%s)",
3955             (int) intent,
3956             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3957          (void) SetImageProperty(image,"png:sRGB",msg,
3958                  exception);
3959        }
3960 #endif
3961
3962      if (ping_found_gAMA != MagickFalse)
3963        {
3964          (void) FormatLocaleString(msg,MaxTextExtent,
3965             "gamma=%.8g (See Gamma, above)",
3966             file_gamma);
3967          (void) SetImageProperty(image,"png:gAMA",msg,
3968                 exception);
3969        }
3970
3971 #if defined(PNG_pHYs_SUPPORTED)
3972      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3973        {
3974          (void) FormatLocaleString(msg,MaxTextExtent,
3975             "x_res=%.10g, y_res=%.10g, units=%d",
3976             (double) x_resolution,(double) y_resolution, unit_type);
3977          (void) SetImageProperty(image,"png:pHYs",msg,
3978                 exception);
3979        }
3980 #endif
3981
3982 #if defined(PNG_oFFs_SUPPORTED)
3983      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3984        {
3985          (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3986             (double) image->page.x,(double) image->page.y);
3987          (void) SetImageProperty(image,"png:oFFs",msg,
3988                 exception);
3989        }
3990 #endif
3991
3992      if ((image->page.width != 0 && image->page.width != image->columns) ||
3993          (image->page.height != 0 && image->page.height != image->rows))
3994        {
3995          (void) FormatLocaleString(msg,MaxTextExtent,
3996             "width=%.20g, height=%.20g",
3997             (double) image->page.width,(double) image->page.height);
3998          (void) SetImageProperty(image,"png:vpAg",msg,
3999                 exception);
4000        }
4001    }
4002
4003   /*
4004     Relinquish resources.
4005   */
4006   png_destroy_read_struct(&ping,&ping_info,&end_info);
4007
4008   pixel_info=RelinquishVirtualMemory(pixel_info);
4009
4010   if (logging != MagickFalse)
4011     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4012       "  exit ReadOnePNGImage()");
4013
4014 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
4015   UnlockSemaphoreInfo(ping_semaphore);
4016 #endif
4017
4018   /* }  for navigation to beginning of SETJMP-protected block, revert to
4019    *    Throwing an Exception when an error occurs.
4020    */
4021
4022   return(image);
4023
4024 /* end of reading one PNG image */
4025 }
4026
4027 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4028 {
4029   Image
4030     *image,
4031     *previous;
4032
4033   MagickBooleanType
4034     have_mng_structure,
4035     logging,
4036     status;
4037
4038   MngInfo
4039     *mng_info;
4040
4041   char
4042     magic_number[MaxTextExtent];
4043
4044   ssize_t
4045     count;
4046
4047   /*
4048     Open image file.
4049   */
4050   assert(image_info != (const ImageInfo *) NULL);
4051   assert(image_info->signature == MagickSignature);
4052
4053   if (image_info->debug != MagickFalse)
4054     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4055       image_info->filename);
4056
4057   assert(exception != (ExceptionInfo *) NULL);
4058   assert(exception->signature == MagickSignature);
4059   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4060   image=AcquireImage(image_info,exception);
4061   mng_info=(MngInfo *) NULL;
4062   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4063
4064   if (status == MagickFalse)
4065     ThrowReaderException(FileOpenError,"UnableToOpenFile");
4066
4067   /*
4068     Verify PNG signature.
4069   */
4070   count=ReadBlob(image,8,(unsigned char *) magic_number);
4071
4072   if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4073     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4074
4075   /*
4076     Allocate a MngInfo structure.
4077   */
4078   have_mng_structure=MagickFalse;
4079   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4080
4081   if (mng_info == (MngInfo *) NULL)
4082     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4083
4084   /*
4085     Initialize members of the MngInfo structure.
4086   */
4087   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4088   mng_info->image=image;
4089   have_mng_structure=MagickTrue;
4090
4091   previous=image;
4092   image=ReadOnePNGImage(mng_info,image_info,exception);
4093   MngInfoFreeStruct(mng_info,&have_mng_structure);
4094
4095   if (image == (Image *) NULL)
4096     {
4097       if (previous != (Image *) NULL)
4098         {
4099           if (previous->signature != MagickSignature)
4100             ThrowReaderException(CorruptImageError,"CorruptImage");
4101
4102           (void) CloseBlob(previous);
4103           (void) DestroyImageList(previous);
4104         }
4105
4106       if (logging != MagickFalse)
4107         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4108           "exit ReadPNGImage() with error");
4109
4110       return((Image *) NULL);
4111     }
4112
4113   (void) CloseBlob(image);
4114
4115   if ((image->columns == 0) || (image->rows == 0))
4116     {
4117       if (logging != MagickFalse)
4118         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4119           "exit ReadPNGImage() with error.");
4120
4121       ThrowReaderException(CorruptImageError,"CorruptImage");
4122     }
4123
4124   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4125       ((image->gamma < .45) || (image->gamma > .46)) &&
4126            !(image->chromaticity.red_primary.x>0.6399f &&
4127            image->chromaticity.red_primary.x<0.6401f &&
4128            image->chromaticity.red_primary.y>0.3299f &&
4129            image->chromaticity.red_primary.y<0.3301f &&
4130            image->chromaticity.green_primary.x>0.2999f &&
4131            image->chromaticity.green_primary.x<0.3001f &&
4132            image->chromaticity.green_primary.y>0.5999f &&
4133            image->chromaticity.green_primary.y<0.6001f &&
4134            image->chromaticity.blue_primary.x>0.1499f &&
4135            image->chromaticity.blue_primary.x<0.1501f &&
4136            image->chromaticity.blue_primary.y>0.0599f &&
4137            image->chromaticity.blue_primary.y<0.0601f &&
4138            image->chromaticity.white_point.x>0.3126f &&
4139            image->chromaticity.white_point.x<0.3128f &&
4140            image->chromaticity.white_point.y>0.3289f &&
4141            image->chromaticity.white_point.y<0.3291f))
4142     SetImageColorspace(image,RGBColorspace,exception);
4143
4144   if (logging != MagickFalse)
4145     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4146         "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4147             (double) image->page.width,(double) image->page.height,
4148             (double) image->page.x,(double) image->page.y);
4149
4150   if (logging != MagickFalse)
4151     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4152
4153   return(image);
4154 }
4155
4156
4157
4158 #if defined(JNG_SUPPORTED)
4159 /*
4160 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4161 %                                                                             %
4162 %                                                                             %
4163 %                                                                             %
4164 %   R e a d O n e J N G I m a g e                                             %
4165 %                                                                             %
4166 %                                                                             %
4167 %                                                                             %
4168 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4169 %
4170 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4171 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
4172 %  necessary for the new Image structure and returns a pointer to the new
4173 %  image.
4174 %
4175 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4176 %
4177 %  The format of the ReadOneJNGImage method is:
4178 %
4179 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4180 %         ExceptionInfo *exception)
4181 %
4182 %  A description of each parameter follows:
4183 %
4184 %    o mng_info: Specifies a pointer to a MngInfo structure.
4185 %
4186 %    o image_info: the image info.
4187 %
4188 %    o exception: return any errors or warnings in this structure.
4189 %
4190 */
4191 static Image *ReadOneJNGImage(MngInfo *mng_info,
4192     const ImageInfo *image_info, ExceptionInfo *exception)
4193 {
4194   Image
4195     *alpha_image,
4196     *color_image,
4197     *image,
4198     *jng_image;
4199
4200   ImageInfo
4201     *alpha_image_info,
4202     *color_image_info;
4203
4204   MagickBooleanType
4205     logging;
4206
4207   ssize_t
4208     y;
4209
4210   MagickBooleanType
4211     status;
4212
4213   png_uint_32
4214     jng_height,
4215     jng_width;
4216
4217   png_byte
4218     jng_color_type,
4219     jng_image_sample_depth,
4220     jng_image_compression_method,
4221     jng_image_interlace_method,
4222     jng_alpha_sample_depth,
4223     jng_alpha_compression_method,
4224     jng_alpha_filter_method,
4225     jng_alpha_interlace_method;
4226
4227   register const Quantum
4228     *s;
4229
4230   register ssize_t
4231     i,
4232     x;
4233
4234   register Quantum
4235     *q;
4236
4237   register unsigned char
4238     *p;
4239
4240   unsigned int
4241     read_JSEP,
4242     reading_idat,
4243     skip_to_iend;
4244
4245   size_t
4246     length;
4247
4248   jng_alpha_compression_method=0;
4249   jng_alpha_sample_depth=8;
4250   jng_color_type=0;
4251   jng_height=0;
4252   jng_width=0;
4253   alpha_image=(Image *) NULL;
4254   color_image=(Image *) NULL;
4255   alpha_image_info=(ImageInfo *) NULL;
4256   color_image_info=(ImageInfo *) NULL;
4257
4258   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4259     "  Enter ReadOneJNGImage()");
4260
4261   image=mng_info->image;
4262
4263   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4264     {
4265       /*
4266         Allocate next image structure.
4267       */
4268       if (logging != MagickFalse)
4269         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4270            "  AcquireNextImage()");
4271
4272       AcquireNextImage(image_info,image,exception);
4273
4274       if (GetNextImageInList(image) == (Image *) NULL)
4275         return((Image *) NULL);
4276
4277       image=SyncNextImageInList(image);
4278     }
4279   mng_info->image=image;
4280
4281   /*
4282     Signature bytes have already been read.
4283   */
4284
4285   read_JSEP=MagickFalse;
4286   reading_idat=MagickFalse;
4287   skip_to_iend=MagickFalse;
4288   for (;;)
4289   {
4290     char
4291       type[MaxTextExtent];
4292
4293     unsigned char
4294       *chunk;
4295
4296     unsigned int
4297       count;
4298
4299     /*
4300       Read a new JNG chunk.
4301     */
4302     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4303       2*GetBlobSize(image));
4304
4305     if (status == MagickFalse)
4306       break;
4307
4308     type[0]='\0';
4309     (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4310     length=ReadBlobMSBLong(image);
4311     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4312
4313     if (logging != MagickFalse)
4314       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4315         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4316         type[0],type[1],type[2],type[3],(double) length);
4317
4318     if (length > PNG_UINT_31_MAX || count == 0)
4319       ThrowReaderException(CorruptImageError,"CorruptImage");
4320
4321     p=NULL;
4322     chunk=(unsigned char *) NULL;
4323
4324     if (length)
4325       {
4326         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4327
4328         if (chunk == (unsigned char *) NULL)
4329           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4330
4331         for (i=0; i < (ssize_t) length; i++)
4332           chunk[i]=(unsigned char) ReadBlobByte(image);
4333
4334         p=chunk;
4335       }
4336
4337     (void) ReadBlobMSBLong(image);  /* read crc word */
4338
4339     if (skip_to_iend)
4340       {
4341         if (length)
4342           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4343
4344         continue;
4345       }
4346
4347     if (memcmp(type,mng_JHDR,4) == 0)
4348       {
4349         if (length == 16)
4350           {
4351             jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4352                 (p[2] << 8) | p[3]);
4353             jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4354                 (p[6] << 8) | p[7]);
4355             jng_color_type=p[8];
4356             jng_image_sample_depth=p[9];
4357             jng_image_compression_method=p[10];
4358             jng_image_interlace_method=p[11];
4359
4360             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4361               NoInterlace;
4362
4363             jng_alpha_sample_depth=p[12];
4364             jng_alpha_compression_method=p[13];
4365             jng_alpha_filter_method=p[14];
4366             jng_alpha_interlace_method=p[15];
4367
4368             if (logging != MagickFalse)
4369               {
4370                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4371                   "    jng_width:      %16lu",(unsigned long) jng_width);
4372
4373                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4374                   "    jng_width:      %16lu",(unsigned long) jng_height);
4375
4376                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4377                   "    jng_color_type: %16d",jng_color_type);
4378
4379                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4380                   "    jng_image_sample_depth:      %3d",
4381                   jng_image_sample_depth);
4382
4383                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4384                   "    jng_image_compression_method:%3d",
4385                   jng_image_compression_method);
4386
4387                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4388                   "    jng_image_interlace_method:  %3d",
4389                   jng_image_interlace_method);
4390
4391                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4392                   "    jng_alpha_sample_depth:      %3d",
4393                   jng_alpha_sample_depth);
4394
4395                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4396                   "    jng_alpha_compression_method:%3d",
4397                   jng_alpha_compression_method);
4398
4399                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4400                   "    jng_alpha_filter_method:     %3d",
4401                   jng_alpha_filter_method);
4402
4403                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4404                   "    jng_alpha_interlace_method:  %3d",
4405                   jng_alpha_interlace_method);
4406               }
4407           }
4408
4409         if (length)
4410           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4411
4412         continue;
4413       }
4414
4415
4416     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4417         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4418          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4419       {
4420         /*
4421            o create color_image
4422            o open color_blob, attached to color_image
4423            o if (color type has alpha)
4424                open alpha_blob, attached to alpha_image
4425         */
4426
4427         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4428
4429         if (color_image_info == (ImageInfo *) NULL)
4430           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4431
4432         GetImageInfo(color_image_info);
4433         color_image=AcquireImage(color_image_info,exception);
4434
4435         if (color_image == (Image *) NULL)
4436           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4437
4438         if (logging != MagickFalse)
4439           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4440             "    Creating color_blob.");
4441
4442         (void) AcquireUniqueFilename(color_image->filename);
4443         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4444           exception);
4445
4446         if (status == MagickFalse)
4447           return((Image *) NULL);
4448
4449         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4450           {
4451             alpha_image_info=(ImageInfo *)
4452               AcquireMagickMemory(sizeof(ImageInfo));
4453
4454             if (alpha_image_info == (ImageInfo *) NULL)
4455               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4456
4457             GetImageInfo(alpha_image_info);
4458             alpha_image=AcquireImage(alpha_image_info,exception);
4459
4460             if (alpha_image == (Image *) NULL)
4461               {
4462                 alpha_image=DestroyImage(alpha_image);
4463                 ThrowReaderException(ResourceLimitError,
4464                   "MemoryAllocationFailed");
4465               }
4466
4467             if (logging != MagickFalse)
4468               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4469                 "    Creating alpha_blob.");
4470
4471             (void) AcquireUniqueFilename(alpha_image->filename);
4472             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4473               exception);
4474
4475             if (status == MagickFalse)
4476               return((Image *) NULL);
4477
4478             if (jng_alpha_compression_method == 0)
4479               {
4480                 unsigned char
4481                   data[18];
4482
4483                 if (logging != MagickFalse)
4484                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4485                     "    Writing IHDR chunk to alpha_blob.");
4486
4487                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4488                   "\211PNG\r\n\032\n");
4489
4490                 (void) WriteBlobMSBULong(alpha_image,13L);
4491                 PNGType(data,mng_IHDR);
4492                 LogPNGChunk(logging,mng_IHDR,13L);
4493                 PNGLong(data+4,jng_width);
4494                 PNGLong(data+8,jng_height);
4495                 data[12]=jng_alpha_sample_depth;
4496                 data[13]=0; /* color_type gray */
4497                 data[14]=0; /* compression method 0 */
4498                 data[15]=0; /* filter_method 0 */
4499                 data[16]=0; /* interlace_method 0 */
4500                 (void) WriteBlob(alpha_image,17,data);
4501                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4502               }
4503           }
4504         reading_idat=MagickTrue;
4505       }
4506
4507     if (memcmp(type,mng_JDAT,4) == 0)
4508       {
4509         /* Copy chunk to color_image->blob */
4510
4511         if (logging != MagickFalse)
4512           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4513             "    Copying JDAT chunk data to color_blob.");
4514
4515         (void) WriteBlob(color_image,length,chunk);
4516
4517         if (length)
4518           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4519
4520         continue;
4521       }
4522
4523     if (memcmp(type,mng_IDAT,4) == 0)
4524       {
4525         png_byte
4526            data[5];
4527
4528         /* Copy IDAT header and chunk data to alpha_image->blob */
4529
4530         if (image_info->ping == MagickFalse)
4531           {
4532             if (logging != MagickFalse)
4533               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4534                 "    Copying IDAT chunk data to alpha_blob.");
4535
4536             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4537             PNGType(data,mng_IDAT);
4538             LogPNGChunk(logging,mng_IDAT,length);
4539             (void) WriteBlob(alpha_image,4,data);
4540             (void) WriteBlob(alpha_image,length,chunk);
4541             (void) WriteBlobMSBULong(alpha_image,
4542               crc32(crc32(0,data,4),chunk,(uInt) length));
4543           }
4544
4545         if (length)
4546           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4547
4548         continue;
4549       }
4550
4551     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4552       {
4553         /* Copy chunk data to alpha_image->blob */
4554
4555         if (image_info->ping == MagickFalse)
4556           {
4557             if (logging != MagickFalse)
4558               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4559                 "    Copying JDAA chunk data to alpha_blob.");
4560
4561             (void) WriteBlob(alpha_image,length,chunk);
4562           }
4563
4564         if (length)
4565           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4566
4567         continue;
4568       }
4569
4570     if (memcmp(type,mng_JSEP,4) == 0)
4571       {
4572         read_JSEP=MagickTrue;
4573
4574         if (length)
4575           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4576
4577         continue;
4578       }
4579
4580     if (memcmp(type,mng_bKGD,4) == 0)
4581       {
4582         if (length == 2)
4583           {
4584             image->background_color.red=ScaleCharToQuantum(p[1]);
4585             image->background_color.green=image->background_color.red;
4586             image->background_color.blue=image->background_color.red;
4587           }
4588
4589         if (length == 6)
4590           {
4591             image->background_color.red=ScaleCharToQuantum(p[1]);
4592             image->background_color.green=ScaleCharToQuantum(p[3]);
4593             image->background_color.blue=ScaleCharToQuantum(p[5]);
4594           }
4595
4596         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4597         continue;
4598       }
4599
4600     if (memcmp(type,mng_gAMA,4) == 0)
4601       {
4602         if (length == 4)
4603           image->gamma=((float) mng_get_long(p))*0.00001;
4604
4605         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4606         continue;
4607       }
4608
4609     if (memcmp(type,mng_cHRM,4) == 0)
4610       {
4611         if (length == 32)
4612           {
4613             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4614             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4615             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4616             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4617             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4618             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4619             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4620             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4621           }
4622
4623         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4624         continue;
4625       }
4626
4627     if (memcmp(type,mng_sRGB,4) == 0)
4628       {
4629         if (length == 1)
4630           {
4631             image->rendering_intent=
4632               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4633             image->gamma=1.000f/2.200f;
4634             image->chromaticity.red_primary.x=0.6400f;
4635             image->chromaticity.red_primary.y=0.3300f;
4636             image->chromaticity.green_primary.x=0.3000f;
4637             image->chromaticity.green_primary.y=0.6000f;
4638             image->chromaticity.blue_primary.x=0.1500f;
4639             image->chromaticity.blue_primary.y=0.0600f;
4640             image->chromaticity.white_point.x=0.3127f;
4641             image->chromaticity.white_point.y=0.3290f;
4642           }
4643
4644         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4645         continue;
4646       }
4647
4648     if (memcmp(type,mng_oFFs,4) == 0)
4649       {
4650         if (length > 8)
4651           {
4652             image->page.x=(ssize_t) mng_get_long(p);
4653             image->page.y=(ssize_t) mng_get_long(&p[4]);
4654
4655             if ((int) p[8] != 0)
4656               {
4657                 image->page.x/=10000;
4658                 image->page.y/=10000;
4659               }
4660           }
4661
4662         if (length)
4663           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4664
4665         continue;
4666       }
4667
4668     if (memcmp(type,mng_pHYs,4) == 0)
4669       {
4670         if (length > 8)
4671           {
4672             image->resolution.x=(double) mng_get_long(p);
4673             image->resolution.y=(double) mng_get_long(&p[4]);
4674             if ((int) p[8] == PNG_RESOLUTION_METER)
4675               {
4676                 image->units=PixelsPerCentimeterResolution;
4677                 image->resolution.x=image->resolution.x/100.0f;
4678                 image->resolution.y=image->resolution.y/100.0f;
4679               }
4680           }
4681
4682         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4683         continue;
4684       }
4685
4686 #if 0
4687     if (memcmp(type,mng_iCCP,4) == 0)
4688       {
4689         /* To do: */
4690         if (length)
4691           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4692
4693         continue;
4694       }
4695 #endif
4696
4697     if (length)
4698       chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4699
4700     if (memcmp(type,mng_IEND,4))
4701       continue;
4702
4703     break;
4704   }
4705
4706
4707   /* IEND found */
4708
4709   /*
4710     Finish up reading image data:
4711
4712        o read main image from color_blob.
4713
4714        o close color_blob.
4715
4716        o if (color_type has alpha)
4717             if alpha_encoding is PNG
4718                read secondary image from alpha_blob via ReadPNG
4719             if alpha_encoding is JPEG
4720                read secondary image from alpha_blob via ReadJPEG
4721
4722        o close alpha_blob.
4723
4724        o copy intensity of secondary image into
4725          alpha samples of main image.
4726
4727        o destroy the secondary image.
4728   */
4729
4730   (void) CloseBlob(color_image);
4731
4732   if (logging != MagickFalse)
4733     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4734       "    Reading jng_image from color_blob.");
4735
4736   assert(color_image_info != (ImageInfo *) NULL);
4737   (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4738     color_image->filename);
4739
4740   color_image_info->ping=MagickFalse;   /* To do: avoid this */
4741   jng_image=ReadImage(color_image_info,exception);
4742
4743   if (jng_image == (Image *) NULL)
4744     return((Image *) NULL);
4745
4746   (void) RelinquishUniqueFileResource(color_image->filename);
4747   color_image=DestroyImage(color_image);
4748   color_image_info=DestroyImageInfo(color_image_info);
4749
4750   if (jng_image == (Image *) NULL)
4751     return((Image *) NULL);
4752
4753   if (logging != MagickFalse)
4754     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4755       "    Copying jng_image pixels to main image.");
4756
4757   image->rows=jng_height;
4758   image->columns=jng_width;
4759
4760   for (y=0; y < (ssize_t) image->rows; y++)
4761   {
4762     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4763     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4764     for (x=(ssize_t) image->columns; x != 0; x--)
4765     {
4766       SetPixelRed(image,GetPixelRed(jng_image,s),q);
4767       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4768       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4769       q+=GetPixelChannels(image);
4770       s+=GetPixelChannels(jng_image);
4771     }
4772
4773     if (SyncAuthenticPixels(image,exception) == MagickFalse)
4774       break;
4775   }
4776
4777   jng_image=DestroyImage(jng_image);
4778
4779   if (image_info->ping == MagickFalse)
4780     {
4781      if (jng_color_type >= 12)
4782        {
4783          if (jng_alpha_compression_method == 0)
4784            {
4785              png_byte
4786                data[5];
4787              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4788              PNGType(data,mng_IEND);
4789              LogPNGChunk(logging,mng_IEND,0L);
4790              (void) WriteBlob(alpha_image,4,data);
4791              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4792            }
4793
4794          (void) CloseBlob(alpha_image);
4795
4796          if (logging != MagickFalse)
4797            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4798              "    Reading alpha from alpha_blob.");
4799
4800          (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4801            "%s",alpha_image->filename);
4802
4803          jng_image=ReadImage(alpha_image_info,exception);
4804
4805          if (jng_image != (Image *) NULL)
4806            for (y=0; y < (ssize_t) image->rows; y++)
4807            {
4808              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4809                exception);
4810              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4811
4812              if (image->alpha_trait == BlendPixelTrait)
4813                for (x=(ssize_t) image->columns; x != 0; x--)
4814                {
4815                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4816                   q+=GetPixelChannels(image);
4817                   s+=GetPixelChannels(jng_image);
4818                }
4819
4820              else
4821                for (x=(ssize_t) image->columns; x != 0; x--)
4822                {
4823                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4824                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
4825                     image->alpha_trait=BlendPixelTrait;
4826                   q+=GetPixelChannels(image);
4827                   s+=GetPixelChannels(jng_image);
4828                }
4829
4830              if (SyncAuthenticPixels(image,exception) == MagickFalse)
4831                break;
4832            }
4833          (void) RelinquishUniqueFileResource(alpha_image->filename);
4834          alpha_image=DestroyImage(alpha_image);
4835          alpha_image_info=DestroyImageInfo(alpha_image_info);
4836          if (jng_image != (Image *) NULL)
4837            jng_image=DestroyImage(jng_image);
4838        }
4839     }
4840
4841   /* Read the JNG image.  */
4842
4843   if (mng_info->mng_type == 0)
4844     {
4845       mng_info->mng_width=jng_width;
4846       mng_info->mng_height=jng_height;
4847     }
4848
4849   if (image->page.width == 0 && image->page.height == 0)
4850     {
4851       image->page.width=jng_width;
4852       image->page.height=jng_height;
4853     }
4854
4855   if (image->page.x == 0 && image->page.y == 0)
4856     {
4857       image->page.x=mng_info->x_off[mng_info->object_id];
4858       image->page.y=mng_info->y_off[mng_info->object_id];
4859     }
4860
4861   else
4862     {
4863       image->page.y=mng_info->y_off[mng_info->object_id];
4864     }
4865
4866   mng_info->image_found++;
4867   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4868     2*GetBlobSize(image));
4869
4870   if (logging != MagickFalse)
4871     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4872       "  exit ReadOneJNGImage()");
4873
4874   return(image);
4875 }
4876
4877 /*
4878 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4879 %                                                                             %
4880 %                                                                             %
4881 %                                                                             %
4882 %   R e a d J N G I m a g e                                                   %
4883 %                                                                             %
4884 %                                                                             %
4885 %                                                                             %
4886 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4887 %
4888 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4889 %  (including the 8-byte signature)  and returns it.  It allocates the memory
4890 %  necessary for the new Image structure and returns a pointer to the new
4891 %  image.
4892 %
4893 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4894 %
4895 %  The format of the ReadJNGImage method is:
4896 %
4897 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4898 %         *exception)
4899 %
4900 %  A description of each parameter follows:
4901 %
4902 %    o image_info: the image info.
4903 %
4904 %    o exception: return any errors or warnings in this structure.
4905 %
4906 */
4907
4908 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4909 {
4910   Image
4911     *image,
4912     *previous;
4913
4914   MagickBooleanType
4915     have_mng_structure,
4916     logging,
4917     status;
4918
4919   MngInfo
4920     *mng_info;
4921
4922   char
4923     magic_number[MaxTextExtent];
4924
4925   size_t
4926     count;
4927
4928   /*
4929     Open image file.
4930   */
4931   assert(image_info != (const ImageInfo *) NULL);
4932   assert(image_info->signature == MagickSignature);
4933   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4934   assert(exception != (ExceptionInfo *) NULL);
4935   assert(exception->signature == MagickSignature);
4936   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4937   image=AcquireImage(image_info,exception);
4938   mng_info=(MngInfo *) NULL;
4939   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4940
4941   if (status == MagickFalse)
4942     return((Image *) NULL);
4943
4944   if (LocaleCompare(image_info->magick,"JNG") != 0)
4945     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4946
4947   /* Verify JNG signature.  */
4948
4949   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4950
4951   if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4952     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4953
4954   /* Allocate a MngInfo structure.  */
4955
4956   have_mng_structure=MagickFalse;
4957   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4958
4959   if (mng_info == (MngInfo *) NULL)
4960     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4961
4962   /* Initialize members of the MngInfo structure.  */
4963
4964   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4965   have_mng_structure=MagickTrue;
4966
4967   mng_info->image=image;
4968   previous=image;
4969   image=ReadOneJNGImage(mng_info,image_info,exception);
4970   MngInfoFreeStruct(mng_info,&have_mng_structure);
4971
4972   if (image == (Image *) NULL)
4973     {
4974       if (IsImageObject(previous) != MagickFalse)
4975         {
4976           (void) CloseBlob(previous);
4977           (void) DestroyImageList(previous);
4978         }
4979
4980       if (logging != MagickFalse)
4981         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4982           "exit ReadJNGImage() with error");
4983
4984       return((Image *) NULL);
4985     }
4986   (void) CloseBlob(image);
4987
4988   if (image->columns == 0 || image->rows == 0)
4989     {
4990       if (logging != MagickFalse)
4991         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4992           "exit ReadJNGImage() with error");
4993
4994       ThrowReaderException(CorruptImageError,"CorruptImage");
4995     }
4996
4997   if (logging != MagickFalse)
4998     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4999
5000   return(image);
5001 }
5002 #endif
5003
5004 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5005 {
5006   char
5007     page_geometry[MaxTextExtent];
5008
5009   Image
5010     *image,
5011     *previous;
5012
5013   MagickBooleanType
5014     logging,
5015     have_mng_structure;
5016
5017   volatile int
5018     first_mng_object,
5019     object_id,
5020     term_chunk_found,
5021     skip_to_iend;
5022
5023   volatile ssize_t
5024     image_count=0;
5025
5026   MagickBooleanType
5027     status;
5028
5029   MagickOffsetType
5030     offset;
5031
5032   MngInfo
5033     *mng_info;
5034
5035   MngBox
5036     default_fb,
5037     fb,
5038     previous_fb;
5039
5040 #if defined(MNG_INSERT_LAYERS)
5041   PixelInfo
5042     mng_background_color;
5043 #endif
5044
5045   register unsigned char
5046     *p;
5047
5048   register ssize_t
5049     i;
5050
5051   size_t
5052     count;
5053
5054   ssize_t
5055     loop_level;
5056
5057   volatile short
5058     skipping_loop;
5059
5060 #if defined(MNG_INSERT_LAYERS)
5061   unsigned int
5062     mandatory_back=0;
5063 #endif
5064
5065   volatile unsigned int
5066 #ifdef MNG_OBJECT_BUFFERS
5067     mng_background_object=0,
5068 #endif
5069     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5070
5071   size_t
5072     default_frame_timeout,
5073     frame_timeout,
5074 #if defined(MNG_INSERT_LAYERS)
5075     image_height,
5076     image_width,
5077 #endif
5078     length;
5079
5080   /* These delays are all measured in image ticks_per_second,
5081    * not in MNG ticks_per_second
5082    */
5083   volatile size_t
5084     default_frame_delay,
5085     final_delay,
5086     final_image_delay,
5087     frame_delay,
5088 #if defined(MNG_INSERT_LAYERS)
5089     insert_layers,
5090 #endif
5091     mng_iterations=1,
5092     simplicity=0,
5093     subframe_height=0,
5094     subframe_width=0;
5095
5096   previous_fb.top=0;
5097   previous_fb.bottom=0;
5098   previous_fb.left=0;
5099   previous_fb.right=0;
5100   default_fb.top=0;
5101   default_fb.bottom=0;
5102   default_fb.left=0;
5103   default_fb.right=0;
5104
5105   /* Open image file.  */
5106
5107   assert(image_info != (const ImageInfo *) NULL);
5108   assert(image_info->signature == MagickSignature);
5109   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
5110   assert(exception != (ExceptionInfo *) NULL);
5111   assert(exception->signature == MagickSignature);
5112   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
5113   image=AcquireImage(image_info,exception);
5114   mng_info=(MngInfo *) NULL;
5115   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5116
5117   if (status == MagickFalse)
5118     return((Image *) NULL);
5119
5120   first_mng_object=MagickFalse;
5121   skipping_loop=(-1);
5122   have_mng_structure=MagickFalse;
5123
5124   /* Allocate a MngInfo structure.  */
5125
5126   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
5127
5128   if (mng_info == (MngInfo *) NULL)
5129     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5130
5131   /* Initialize members of the MngInfo structure.  */
5132
5133   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5134   mng_info->image=image;
5135   have_mng_structure=MagickTrue;
5136
5137   if (LocaleCompare(image_info->magick,"MNG") == 0)
5138     {
5139       char
5140         magic_number[MaxTextExtent];
5141
5142       /* Verify MNG signature.  */
5143       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5144       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5145         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5146
5147       /* Initialize some nonzero members of the MngInfo structure.  */
5148       for (i=0; i < MNG_MAX_OBJECTS; i++)
5149       {
5150         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5151         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5152       }
5153       mng_info->exists[0]=MagickTrue;
5154     }
5155
5156   first_mng_object=MagickTrue;
5157   mng_type=0;
5158 #if defined(MNG_INSERT_LAYERS)
5159   insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5160 #endif
5161   default_frame_delay=0;
5162   default_frame_timeout=0;
5163   frame_delay=0;
5164   final_delay=1;
5165   mng_info->ticks_per_second=1UL*image->ticks_per_second;
5166   object_id=0;
5167   skip_to_iend=MagickFalse;
5168   term_chunk_found=MagickFalse;
5169   mng_info->framing_mode=1;
5170 #if defined(MNG_INSERT_LAYERS)
5171   mandatory_back=MagickFalse;
5172 #endif
5173 #if defined(MNG_INSERT_LAYERS)
5174   mng_background_color=image->background_color;
5175 #endif
5176   default_fb=mng_info->frame;
5177   previous_fb=mng_info->frame;
5178   do
5179   {
5180     char
5181       type[MaxTextExtent];
5182
5183     if (LocaleCompare(image_info->magick,"MNG") == 0)
5184       {
5185         unsigned char
5186           *chunk;
5187
5188         /*
5189           Read a new chunk.
5190         */
5191         type[0]='\0';
5192         (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
5193         length=ReadBlobMSBLong(image);
5194         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5195
5196         if (logging != MagickFalse)
5197           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5198            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
5199            type[0],type[1],type[2],type[3],(double) length);
5200
5201         if (length > PNG_UINT_31_MAX)
5202           status=MagickFalse;
5203
5204         if (count == 0)
5205           ThrowReaderException(CorruptImageError,"CorruptImage");
5206
5207         p=NULL;
5208         chunk=(unsigned char *) NULL;
5209
5210         if (length)
5211           {
5212             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5213
5214             if (chunk == (unsigned char *) NULL)
5215               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5216
5217             for (i=0; i < (ssize_t) length; i++)
5218               chunk[i]=(unsigned char) ReadBlobByte(image);
5219
5220             p=chunk;
5221           }
5222
5223         (void) ReadBlobMSBLong(image);  /* read crc word */
5224
5225 #if !defined(JNG_SUPPORTED)
5226         if (memcmp(type,mng_JHDR,4) == 0)
5227           {
5228             skip_to_iend=MagickTrue;
5229
5230             if (mng_info->jhdr_warning == 0)
5231               (void) ThrowMagickException(exception,GetMagickModule(),
5232                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5233
5234             mng_info->jhdr_warning++;
5235           }
5236 #endif
5237         if (memcmp(type,mng_DHDR,4) == 0)
5238           {
5239             skip_to_iend=MagickTrue;
5240
5241             if (mng_info->dhdr_warning == 0)
5242               (void) ThrowMagickException(exception,GetMagickModule(),
5243                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5244
5245             mng_info->dhdr_warning++;
5246           }
5247         if (memcmp(type,mng_MEND,4) == 0)
5248           break;
5249
5250         if (skip_to_iend)
5251           {
5252             if (memcmp(type,mng_IEND,4) == 0)
5253               skip_to_iend=MagickFalse;
5254
5255             if (length)
5256               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5257
5258             if (logging != MagickFalse)
5259               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5260                 "  Skip to IEND.");
5261
5262             continue;
5263           }
5264
5265         if (memcmp(type,mng_MHDR,4) == 0)
5266           {
5267             mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5268                 (p[2] << 8) | p[3]);
5269
5270             mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5271                 (p[6] << 8) | p[7]);
5272
5273             if (logging != MagickFalse)
5274               {
5275                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5276                   "  MNG width: %.20g",(double) mng_info->mng_width);
5277                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5278                   "  MNG height: %.20g",(double) mng_info->mng_height);
5279               }
5280
5281             p+=8;
5282             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5283
5284             if (mng_info->ticks_per_second == 0)
5285               default_frame_delay=0;
5286
5287             else
5288               default_frame_delay=1UL*image->ticks_per_second/
5289                 mng_info->ticks_per_second;
5290
5291             frame_delay=default_frame_delay;
5292             simplicity=0;
5293
5294             if (length > 16)
5295               {
5296                 p+=16;
5297                 simplicity=(size_t) mng_get_long(p);
5298               }
5299
5300             mng_type=1;    /* Full MNG */
5301
5302             if ((simplicity != 0) && ((simplicity | 11) == 11))
5303               mng_type=2; /* LC */
5304
5305             if ((simplicity != 0) && ((simplicity | 9) == 9))
5306               mng_type=3; /* VLC */
5307
5308 #if defined(MNG_INSERT_LAYERS)
5309             if (mng_type != 3)
5310               insert_layers=MagickTrue;
5311 #endif
5312             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5313               {
5314                 /* Allocate next image structure.  */
5315                 AcquireNextImage(image_info,image,exception);
5316
5317                 if (GetNextImageInList(image) == (Image *) NULL)
5318                   return((Image *) NULL);
5319
5320                 image=SyncNextImageInList(image);
5321                 mng_info->image=image;
5322               }
5323
5324             if ((mng_info->mng_width > 65535L) ||
5325                 (mng_info->mng_height > 65535L))
5326               ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5327
5328             (void) FormatLocaleString(page_geometry,MaxTextExtent,
5329               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5330               mng_info->mng_height);
5331
5332             mng_info->frame.left=0;
5333             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5334             mng_info->frame.top=0;
5335             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5336             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5337
5338             for (i=0; i < MNG_MAX_OBJECTS; i++)
5339               mng_info->object_clip[i]=mng_info->frame;
5340
5341             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5342             continue;
5343           }
5344
5345         if (memcmp(type,mng_TERM,4) == 0)
5346           {
5347             int
5348               repeat=0;
5349
5350
5351             if (length)
5352               repeat=p[0];
5353
5354             if (repeat == 3)
5355               {
5356                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5357                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5358
5359                 if (mng_iterations == PNG_UINT_31_MAX)
5360                   mng_iterations=0;
5361
5362                 image->iterations=mng_iterations;
5363                 term_chunk_found=MagickTrue;
5364               }
5365
5366             if (logging != MagickFalse)
5367               {
5368                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5369                   "    repeat=%d",repeat);
5370
5371                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5372                   "    final_delay=%.20g",(double) final_delay);
5373
5374                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5375                   "    image->iterations=%.20g",(double) image->iterations);
5376               }
5377
5378             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5379             continue;
5380           }
5381         if (memcmp(type,mng_DEFI,4) == 0)
5382           {
5383             if (mng_type == 3)
5384               (void) ThrowMagickException(exception,GetMagickModule(),
5385                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5386                 image->filename);
5387
5388             object_id=(p[0] << 8) | p[1];
5389
5390             if (mng_type == 2 && object_id != 0)
5391               (void) ThrowMagickException(exception,GetMagickModule(),
5392                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5393                 image->filename);
5394
5395             if (object_id > MNG_MAX_OBJECTS)
5396               {
5397                 /*
5398                   Instead of using a warning we should allocate a larger
5399                   MngInfo structure and continue.
5400                 */
5401                 (void) ThrowMagickException(exception,GetMagickModule(),
5402                   CoderError,"object id too large","`%s'",image->filename);
5403                 object_id=MNG_MAX_OBJECTS;
5404               }
5405
5406             if (mng_info->exists[object_id])
5407               if (mng_info->frozen[object_id])
5408                 {
5409                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5410                   (void) ThrowMagickException(exception,
5411                     GetMagickModule(),CoderError,
5412                     "DEFI cannot redefine a frozen MNG object","`%s'",
5413                     image->filename);
5414                   continue;
5415                 }
5416
5417             mng_info->exists[object_id]=MagickTrue;
5418
5419             if (length > 2)
5420               mng_info->invisible[object_id]=p[2];
5421
5422             /*
5423               Extract object offset info.
5424             */
5425             if (length > 11)
5426               {
5427                 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5428                     (p[5] << 16) | (p[6] << 8) | p[7]);
5429
5430                 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5431                     (p[9] << 16) | (p[10] << 8) | p[11]);
5432
5433                 if (logging != MagickFalse)
5434                   {
5435                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5436                       "  x_off[%d]: %.20g",object_id,(double)
5437                       mng_info->x_off[object_id]);
5438
5439                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5440                       "  y_off[%d]: %.20g",object_id,(double)
5441                       mng_info->y_off[object_id]);
5442                   }
5443               }
5444
5445             /*
5446               Extract object clipping info.
5447             */
5448             if (length > 27)
5449               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5450                 &p[12]);
5451
5452             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5453             continue;
5454           }
5455         if (memcmp(type,mng_bKGD,4) == 0)
5456           {
5457             mng_info->have_global_bkgd=MagickFalse;
5458
5459             if (length > 5)
5460               {
5461                 mng_info->mng_global_bkgd.red=
5462                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5463
5464                 mng_info->mng_global_bkgd.green=
5465                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5466
5467                 mng_info->mng_global_bkgd.blue=
5468                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5469
5470                 mng_info->have_global_bkgd=MagickTrue;
5471               }
5472
5473             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5474             continue;
5475           }
5476         if (memcmp(type,mng_BACK,4) == 0)
5477           {
5478 #if defined(MNG_INSERT_LAYERS)
5479             if (length > 6)
5480               mandatory_back=p[6];
5481
5482             else
5483               mandatory_back=0;
5484
5485             if (mandatory_back && length > 5)
5486               {
5487                 mng_background_color.red=
5488                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5489
5490                 mng_background_color.green=
5491                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5492
5493                 mng_background_color.blue=
5494                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5495
5496                 mng_background_color.alpha=OpaqueAlpha;
5497               }
5498
5499 #ifdef MNG_OBJECT_BUFFERS
5500             if (length > 8)
5501               mng_background_object=(p[7] << 8) | p[8];
5502 #endif
5503 #endif
5504             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5505             continue;
5506           }
5507
5508         if (memcmp(type,mng_PLTE,4) == 0)
5509           {
5510             /* Read global PLTE.  */
5511
5512             if (length && (length < 769))
5513               {
5514                 if (mng_info->global_plte == (png_colorp) NULL)
5515                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5516                     sizeof(*mng_info->global_plte));
5517
5518                 for (i=0; i < (ssize_t) (length/3); i++)
5519                 {
5520                   mng_info->global_plte[i].red=p[3*i];
5521                   mng_info->global_plte[i].green=p[3*i+1];
5522                   mng_info->global_plte[i].blue=p[3*i+2];
5523                 }
5524
5525                 mng_info->global_plte_length=(unsigned int) (length/3);
5526               }
5527 #ifdef MNG_LOOSE
5528             for ( ; i < 256; i++)
5529             {
5530               mng_info->global_plte[i].red=i;
5531               mng_info->global_plte[i].green=i;
5532               mng_info->global_plte[i].blue=i;
5533             }
5534
5535             if (length)
5536               mng_info->global_plte_length=256;
5537 #endif
5538             else
5539               mng_info->global_plte_length=0;
5540
5541             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5542             continue;
5543           }
5544
5545         if (memcmp(type,mng_tRNS,4) == 0)
5546           {
5547             /* read global tRNS */
5548
5549             if (length < 257)
5550               for (i=0; i < (ssize_t) length; i++)
5551                 mng_info->global_trns[i]=p[i];
5552
5553 #ifdef MNG_LOOSE
5554             for ( ; i < 256; i++)
5555               mng_info->global_trns[i]=255;
5556 #endif
5557             mng_info->global_trns_length=(unsigned int) length;
5558             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5559             continue;
5560           }
5561         if (memcmp(type,mng_gAMA,4) == 0)
5562           {
5563             if (length == 4)
5564               {
5565                 ssize_t
5566                   igamma;
5567
5568                 igamma=mng_get_long(p);
5569                 mng_info->global_gamma=((float) igamma)*0.00001;
5570                 mng_info->have_global_gama=MagickTrue;
5571               }
5572
5573             else
5574               mng_info->have_global_gama=MagickFalse;
5575
5576             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5577             continue;
5578           }
5579
5580         if (memcmp(type,mng_cHRM,4) == 0)
5581           {
5582             /* Read global cHRM */
5583
5584             if (length == 32)
5585               {
5586                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5587                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5588                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5589                 mng_info->global_chrm.red_primary.y=0.00001*
5590                   mng_get_long(&p[12]);
5591                 mng_info->global_chrm.green_primary.x=0.00001*
5592                   mng_get_long(&p[16]);
5593                 mng_info->global_chrm.green_primary.y=0.00001*
5594                   mng_get_long(&p[20]);
5595                 mng_info->global_chrm.blue_primary.x=0.00001*
5596                   mng_get_long(&p[24]);
5597                 mng_info->global_chrm.blue_primary.y=0.00001*
5598                   mng_get_long(&p[28]);
5599                 mng_info->have_global_chrm=MagickTrue;
5600               }
5601             else
5602               mng_info->have_global_chrm=MagickFalse;
5603
5604             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5605             continue;
5606           }
5607
5608         if (memcmp(type,mng_sRGB,4) == 0)
5609           {
5610             /*
5611               Read global sRGB.
5612             */
5613             if (length)
5614               {
5615                 mng_info->global_srgb_intent=
5616                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5617                 mng_info->have_global_srgb=MagickTrue;
5618               }
5619             else
5620               mng_info->have_global_srgb=MagickFalse;
5621
5622             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5623             continue;
5624           }
5625
5626         if (memcmp(type,mng_iCCP,4) == 0)
5627           {
5628             /* To do: */
5629
5630             /*
5631               Read global iCCP.
5632             */
5633             if (length)
5634               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5635
5636             continue;
5637           }
5638
5639         if (memcmp(type,mng_FRAM,4) == 0)
5640           {
5641             if (mng_type == 3)
5642               (void) ThrowMagickException(exception,GetMagickModule(),
5643                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5644                 image->filename);
5645
5646             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5647               image->delay=frame_delay;
5648
5649             frame_delay=default_frame_delay;
5650             frame_timeout=default_frame_timeout;
5651             fb=default_fb;
5652
5653             if (length)
5654               if (p[0])
5655                 mng_info->framing_mode=p[0];
5656
5657             if (logging != MagickFalse)
5658               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5659                 "    Framing_mode=%d",mng_info->framing_mode);
5660
5661             if (length > 6)
5662               {
5663                 /* Note the delay and frame clipping boundaries.  */
5664
5665                 p++; /* framing mode */
5666
5667                 while (*p && ((p-chunk) < (ssize_t) length))
5668                   p++;  /* frame name */
5669
5670                 p++;  /* frame name terminator */
5671
5672                 if ((p-chunk) < (ssize_t) (length-4))
5673                   {
5674                     int
5675                       change_delay,
5676                       change_timeout,
5677                       change_clipping;
5678
5679                     change_delay=(*p++);
5680                     change_timeout=(*p++);
5681                     change_clipping=(*p++);
5682                     p++; /* change_sync */
5683
5684                     if (change_delay)
5685                       {
5686                         frame_delay=1UL*image->ticks_per_second*
5687                           mng_get_long(p);
5688
5689                         if (mng_info->ticks_per_second != 0)
5690                           frame_delay/=mng_info->ticks_per_second;
5691
5692                         else
5693                           frame_delay=PNG_UINT_31_MAX;
5694
5695                         if (change_delay == 2)
5696                           default_frame_delay=frame_delay;
5697
5698                         p+=4;
5699
5700                         if (logging != MagickFalse)
5701                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5702                             "    Framing_delay=%.20g",(double) frame_delay);
5703                       }
5704
5705                     if (change_timeout)
5706                       {
5707                         frame_timeout=1UL*image->ticks_per_second*
5708                           mng_get_long(p);
5709
5710                         if (mng_info->ticks_per_second != 0)
5711                           frame_timeout/=mng_info->ticks_per_second;
5712
5713                         else
5714                           frame_timeout=PNG_UINT_31_MAX;
5715
5716                         if (change_delay == 2)
5717                           default_frame_timeout=frame_timeout;
5718
5719                         p+=4;
5720
5721                         if (logging != MagickFalse)
5722                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5723                             "    Framing_timeout=%.20g",(double) frame_timeout);
5724                       }
5725
5726                     if (change_clipping)
5727                       {
5728                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5729                         p+=17;
5730                         previous_fb=fb;
5731
5732                         if (logging != MagickFalse)
5733                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5734                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5735                             (double) fb.left,(double) fb.right,(double) fb.top,
5736                             (double) fb.bottom);
5737
5738                         if (change_clipping == 2)
5739                           default_fb=fb;
5740                       }
5741                   }
5742               }
5743             mng_info->clip=fb;
5744             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5745
5746             subframe_width=(size_t) (mng_info->clip.right
5747                -mng_info->clip.left);
5748
5749             subframe_height=(size_t) (mng_info->clip.bottom
5750                -mng_info->clip.top);
5751             /*
5752               Insert a background layer behind the frame if framing_mode is 4.
5753             */
5754 #if defined(MNG_INSERT_LAYERS)
5755             if (logging != MagickFalse)
5756               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5757                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
5758                 subframe_width,(double) subframe_height);
5759
5760             if (insert_layers && (mng_info->framing_mode == 4) &&
5761                 (subframe_width) && (subframe_height))
5762               {
5763                 /* Allocate next image structure.  */
5764                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5765                   {
5766                     AcquireNextImage(image_info,image,exception);
5767
5768                     if (GetNextImageInList(image) == (Image *) NULL)
5769                       {
5770                         image=DestroyImageList(image);
5771                         MngInfoFreeStruct(mng_info,&have_mng_structure);
5772                         return((Image *) NULL);
5773                       }
5774
5775                     image=SyncNextImageInList(image);
5776                   }
5777
5778                 mng_info->image=image;
5779
5780                 if (term_chunk_found)
5781                   {
5782                     image->start_loop=MagickTrue;
5783                     image->iterations=mng_iterations;
5784                     term_chunk_found=MagickFalse;
5785                   }
5786
5787                 else
5788                     image->start_loop=MagickFalse;
5789
5790                 image->columns=subframe_width;
5791                 image->rows=subframe_height;
5792                 image->page.width=subframe_width;
5793                 image->page.height=subframe_height;
5794                 image->page.x=mng_info->clip.left;
5795                 image->page.y=mng_info->clip.top;
5796                 image->background_color=mng_background_color;
5797                 image->alpha_trait=UndefinedPixelTrait;
5798                 image->delay=0;
5799                 (void) SetImageBackgroundColor(image,exception);
5800
5801                 if (logging != MagickFalse)
5802                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5803                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5804                     (double) mng_info->clip.left,(double) mng_info->clip.right,
5805                     (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5806               }
5807 #endif
5808             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5809             continue;
5810           }
5811         if (memcmp(type,mng_CLIP,4) == 0)
5812           {
5813             unsigned int
5814               first_object,
5815               last_object;
5816
5817             /*
5818               Read CLIP.
5819             */
5820             first_object=(p[0] << 8) | p[1];
5821             last_object=(p[2] << 8) | p[3];
5822
5823             for (i=(int) first_object; i <= (int) last_object; i++)
5824             {
5825               if (mng_info->exists[i] && !mng_info->frozen[i])
5826                 {
5827                   MngBox
5828                     box;
5829
5830                   box=mng_info->object_clip[i];
5831                   mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5832                 }
5833             }
5834
5835             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5836             continue;
5837           }
5838         if (memcmp(type,mng_SAVE,4) == 0)
5839           {
5840             for (i=1; i < MNG_MAX_OBJECTS; i++)
5841               if (mng_info->exists[i])
5842                 {
5843                  mng_info->frozen[i]=MagickTrue;
5844 #ifdef MNG_OBJECT_BUFFERS
5845                  if (mng_info->ob[i] != (MngBuffer *) NULL)
5846                     mng_info->ob[i]->frozen=MagickTrue;
5847 #endif
5848                 }
5849
5850             if (length)
5851               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5852
5853             continue;
5854           }
5855
5856         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5857           {
5858             /* Read DISC or SEEK.  */
5859
5860             if ((length == 0) || !memcmp(type,mng_SEEK,4))
5861               {
5862                 for (i=1; i < MNG_MAX_OBJECTS; i++)
5863                   MngInfoDiscardObject(mng_info,i);
5864               }
5865
5866             else
5867               {
5868                 register ssize_t
5869                   j;
5870
5871                 for (j=0; j < (ssize_t) length; j+=2)
5872                 {
5873                   i=p[j] << 8 | p[j+1];
5874                   MngInfoDiscardObject(mng_info,i);
5875                 }
5876               }
5877
5878             if (length)
5879               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5880
5881             continue;
5882           }
5883
5884         if (memcmp(type,mng_MOVE,4) == 0)
5885           {
5886             size_t
5887               first_object,
5888               last_object;
5889
5890             /* read MOVE */
5891
5892             first_object=(p[0] << 8) | p[1];
5893             last_object=(p[2] << 8) | p[3];
5894             for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5895             {
5896               if (mng_info->exists[i] && !mng_info->frozen[i])
5897                 {
5898                   MngPair
5899                     new_pair;
5900
5901                   MngPair
5902                     old_pair;
5903
5904                   old_pair.a=mng_info->x_off[i];
5905                   old_pair.b=mng_info->y_off[i];
5906                   new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5907                   mng_info->x_off[i]=new_pair.a;
5908                   mng_info->y_off[i]=new_pair.b;
5909                 }
5910             }
5911
5912             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5913             continue;
5914           }
5915
5916         if (memcmp(type,mng_LOOP,4) == 0)
5917           {
5918             ssize_t loop_iters=1;
5919             loop_level=chunk[0];
5920             mng_info->loop_active[loop_level]=1;  /* mark loop active */
5921
5922             /* Record starting point.  */
5923             loop_iters=mng_get_long(&chunk[1]);
5924
5925             if (logging != MagickFalse)
5926               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5927                 "  LOOP level %.20g has %.20g iterations ",(double) loop_level,
5928                 (double) loop_iters);
5929
5930             if (loop_iters == 0)
5931               skipping_loop=loop_level;
5932
5933             else
5934               {
5935                 mng_info->loop_jump[loop_level]=TellBlob(image);
5936                 mng_info->loop_count[loop_level]=loop_iters;
5937               }
5938
5939             mng_info->loop_iteration[loop_level]=0;
5940             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5941             continue;
5942           }
5943
5944         if (memcmp(type,mng_ENDL,4) == 0)
5945           {
5946             loop_level=chunk[0];
5947
5948             if (skipping_loop > 0)
5949               {
5950                 if (skipping_loop == loop_level)
5951                   {
5952                     /*
5953                       Found end of zero-iteration loop.
5954                     */
5955                     skipping_loop=(-1);
5956                     mng_info->loop_active[loop_level]=0;
5957                   }
5958               }
5959
5960             else
5961               {
5962                 if (mng_info->loop_active[loop_level] == 1)
5963                   {
5964                     mng_info->loop_count[loop_level]--;
5965                     mng_info->loop_iteration[loop_level]++;
5966
5967                     if (logging != MagickFalse)
5968                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5969                         "  ENDL: LOOP level %.20g has %.20g remaining iters ",
5970                         (double) loop_level,(double)
5971                         mng_info->loop_count[loop_level]);
5972
5973                     if (mng_info->loop_count[loop_level] != 0)
5974                       {
5975                         offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5976                           SEEK_SET);
5977
5978                         if (offset < 0)
5979                           ThrowReaderException(CorruptImageError,
5980                             "ImproperImageHeader");
5981                       }
5982
5983                     else
5984                       {
5985                         short
5986                           last_level;
5987
5988                         /*
5989                           Finished loop.
5990                         */
5991                         mng_info->loop_active[loop_level]=0;
5992                         last_level=(-1);
5993                         for (i=0; i < loop_level; i++)
5994                           if (mng_info->loop_active[i] == 1)
5995                             last_level=(short) i;
5996                         loop_level=last_level;
5997                       }
5998                   }
5999               }
6000
6001             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6002             continue;
6003           }
6004
6005         if (memcmp(type,mng_CLON,4) == 0)
6006           {
6007             if (mng_info->clon_warning == 0)
6008               (void) ThrowMagickException(exception,GetMagickModule(),
6009                 CoderError,"CLON is not implemented yet","`%s'",
6010                 image->filename);
6011
6012             mng_info->clon_warning++;
6013           }
6014
6015         if (memcmp(type,mng_MAGN,4) == 0)
6016           {
6017             png_uint_16
6018               magn_first,
6019               magn_last,
6020               magn_mb,
6021               magn_ml,
6022               magn_mr,
6023               magn_mt,
6024               magn_mx,
6025               magn_my,
6026               magn_methx,
6027               magn_methy;
6028
6029             if (length > 1)
6030               magn_first=(p[0] << 8) | p[1];
6031
6032             else
6033               magn_first=0;
6034
6035             if (length > 3)
6036               magn_last=(p[2] << 8) | p[3];
6037
6038             else
6039               magn_last=magn_first;
6040 #ifndef MNG_OBJECT_BUFFERS
6041             if (magn_first || magn_last)
6042               if (mng_info->magn_warning == 0)
6043                 {
6044                   (void) ThrowMagickException(exception,
6045                      GetMagickModule(),CoderError,
6046                      "MAGN is not implemented yet for nonzero objects",
6047                      "`%s'",image->filename);
6048
6049                    mng_info->magn_warning++;
6050                 }
6051 #endif
6052             if (length > 4)
6053               magn_methx=p[4];
6054
6055             else
6056               magn_methx=0;
6057
6058             if (length > 6)
6059               magn_mx=(p[5] << 8) | p[6];
6060
6061             else
6062               magn_mx=1;
6063
6064             if (magn_mx == 0)
6065               magn_mx=1;
6066
6067             if (length > 8)
6068               magn_my=(p[7] << 8) | p[8];
6069
6070             else
6071               magn_my=magn_mx;
6072
6073             if (magn_my == 0)
6074               magn_my=1;
6075
6076             if (length > 10)
6077               magn_ml=(p[9] << 8) | p[10];
6078
6079             else
6080               magn_ml=magn_mx;
6081
6082             if (magn_ml == 0)
6083               magn_ml=1;
6084
6085             if (length > 12)
6086               magn_mr=(p[11] << 8) | p[12];
6087
6088             else
6089               magn_mr=magn_mx;
6090
6091             if (magn_mr == 0)
6092               magn_mr=1;
6093
6094             if (length > 14)
6095               magn_mt=(p[13] << 8) | p[14];
6096
6097             else
6098               magn_mt=magn_my;
6099
6100             if (magn_mt == 0)
6101               magn_mt=1;
6102
6103             if (length > 16)
6104               magn_mb=(p[15] << 8) | p[16];
6105
6106             else
6107               magn_mb=magn_my;
6108
6109             if (magn_mb == 0)
6110               magn_mb=1;
6111
6112             if (length > 17)
6113               magn_methy=p[17];
6114
6115             else
6116               magn_methy=magn_methx;
6117
6118
6119             if (magn_methx > 5 || magn_methy > 5)
6120               if (mng_info->magn_warning == 0)
6121                 {
6122                   (void) ThrowMagickException(exception,
6123                      GetMagickModule(),CoderError,
6124                      "Unknown MAGN method in MNG datastream","`%s'",
6125                      image->filename);
6126
6127                    mng_info->magn_warning++;
6128                 }
6129 #ifdef MNG_OBJECT_BUFFERS
6130           /* Magnify existing objects in the range magn_first to magn_last */
6131 #endif
6132             if (magn_first == 0 || magn_last == 0)
6133               {
6134                 /* Save the magnification factors for object 0 */
6135                 mng_info->magn_mb=magn_mb;
6136                 mng_info->magn_ml=magn_ml;
6137                 mng_info->magn_mr=magn_mr;
6138                 mng_info->magn_mt=magn_mt;
6139                 mng_info->magn_mx=magn_mx;
6140                 mng_info->magn_my=magn_my;
6141                 mng_info->magn_methx=magn_methx;
6142                 mng_info->magn_methy=magn_methy;
6143               }
6144           }
6145
6146         if (memcmp(type,mng_PAST,4) == 0)
6147           {
6148             if (mng_info->past_warning == 0)
6149               (void) ThrowMagickException(exception,GetMagickModule(),
6150                 CoderError,"PAST is not implemented yet","`%s'",
6151                 image->filename);
6152
6153             mng_info->past_warning++;
6154           }
6155
6156         if (memcmp(type,mng_SHOW,4) == 0)
6157           {
6158             if (mng_info->show_warning == 0)
6159               (void) ThrowMagickException(exception,GetMagickModule(),
6160                 CoderError,"SHOW is not implemented yet","`%s'",
6161                 image->filename);
6162
6163             mng_info->show_warning++;
6164           }
6165
6166         if (memcmp(type,mng_sBIT,4) == 0)
6167           {
6168             if (length < 4)
6169               mng_info->have_global_sbit=MagickFalse;
6170
6171             else
6172               {
6173                 mng_info->global_sbit.gray=p[0];
6174                 mng_info->global_sbit.red=p[0];
6175                 mng_info->global_sbit.green=p[1];
6176                 mng_info->global_sbit.blue=p[2];
6177                 mng_info->global_sbit.alpha=p[3];
6178                 mng_info->have_global_sbit=MagickTrue;
6179              }
6180           }
6181         if (memcmp(type,mng_pHYs,4) == 0)
6182           {
6183             if (length > 8)
6184               {
6185                 mng_info->global_x_pixels_per_unit=
6186                     (size_t) mng_get_long(p);
6187                 mng_info->global_y_pixels_per_unit=
6188                     (size_t) mng_get_long(&p[4]);
6189                 mng_info->global_phys_unit_type=p[8];
6190                 mng_info->have_global_phys=MagickTrue;
6191               }
6192
6193             else
6194               mng_info->have_global_phys=MagickFalse;
6195           }
6196         if (memcmp(type,mng_pHYg,4) == 0)
6197           {
6198             if (mng_info->phyg_warning == 0)
6199               (void) ThrowMagickException(exception,GetMagickModule(),
6200                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6201
6202             mng_info->phyg_warning++;
6203           }
6204         if (memcmp(type,mng_BASI,4) == 0)
6205           {
6206             skip_to_iend=MagickTrue;
6207
6208             if (mng_info->basi_warning == 0)
6209               (void) ThrowMagickException(exception,GetMagickModule(),
6210                 CoderError,"BASI is not implemented yet","`%s'",
6211                 image->filename);
6212
6213             mng_info->basi_warning++;
6214 #ifdef MNG_BASI_SUPPORTED
6215             basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6216                (p[2] << 8) | p[3]);
6217             basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6218                (p[6] << 8) | p[7]);
6219             basi_color_type=p[8];
6220             basi_compression_method=p[9];
6221             basi_filter_type=p[10];
6222             basi_interlace_method=p[11];
6223             if (length > 11)
6224               basi_red=(p[12] << 8) & p[13];
6225
6226             else
6227               basi_red=0;
6228
6229             if (length > 13)
6230               basi_green=(p[14] << 8) & p[15];
6231
6232             else
6233               basi_green=0;
6234
6235             if (length > 15)
6236               basi_blue=(p[16] << 8) & p[17];
6237
6238             else
6239               basi_blue=0;
6240
6241             if (length > 17)
6242               basi_alpha=(p[18] << 8) & p[19];
6243
6244             else
6245               {
6246                 if (basi_sample_depth == 16)
6247                   basi_alpha=65535L;
6248                 else
6249                   basi_alpha=255;
6250               }
6251
6252             if (length > 19)
6253               basi_viewable=p[20];
6254
6255             else
6256               basi_viewable=0;
6257
6258 #endif
6259             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6260             continue;
6261           }
6262
6263         if (memcmp(type,mng_IHDR,4)
6264 #if defined(JNG_SUPPORTED)
6265             && memcmp(type,mng_JHDR,4)
6266 #endif
6267             )
6268           {
6269             /* Not an IHDR or JHDR chunk */
6270             if (length)
6271               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6272
6273             continue;
6274           }
6275 /* Process IHDR */
6276         if (logging != MagickFalse)
6277           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6278             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6279
6280         mng_info->exists[object_id]=MagickTrue;
6281         mng_info->viewable[object_id]=MagickTrue;
6282
6283         if (mng_info->invisible[object_id])
6284           {
6285             if (logging != MagickFalse)
6286               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6287                 "  Skipping invisible object");
6288
6289             skip_to_iend=MagickTrue;
6290             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6291             continue;
6292           }
6293 #if defined(MNG_INSERT_LAYERS)
6294         if (length < 8)
6295           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6296
6297         image_width=(size_t) mng_get_long(p);
6298         image_height=(size_t) mng_get_long(&p[4]);
6299 #endif
6300         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6301
6302         /*
6303           Insert a transparent background layer behind the entire animation
6304           if it is not full screen.
6305         */
6306 #if defined(MNG_INSERT_LAYERS)
6307         if (insert_layers && mng_type && first_mng_object)
6308           {
6309             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6310                 (image_width < mng_info->mng_width) ||
6311                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6312                 (image_height < mng_info->mng_height) ||
6313                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6314               {
6315                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6316                   {
6317                     /*
6318                       Allocate next image structure.
6319                     */
6320                     AcquireNextImage(image_info,image,exception);
6321
6322                     if (GetNextImageInList(image) == (Image *) NULL)
6323                       {
6324                         image=DestroyImageList(image);
6325                         MngInfoFreeStruct(mng_info,&have_mng_structure);
6326                         return((Image *) NULL);
6327                       }
6328
6329                     image=SyncNextImageInList(image);
6330                   }
6331                 mng_info->image=image;
6332
6333                 if (term_chunk_found)
6334                   {
6335                     image->start_loop=MagickTrue;
6336                     image->iterations=mng_iterations;
6337                     term_chunk_found=MagickFalse;
6338                   }
6339
6340                 else
6341                     image->start_loop=MagickFalse;
6342
6343                 /* Make a background rectangle.  */
6344
6345                 image->delay=0;
6346                 image->columns=mng_info->mng_width;
6347                 image->rows=mng_info->mng_height;
6348                 image->page.width=mng_info->mng_width;
6349                 image->page.height=mng_info->mng_height;
6350                 image->page.x=0;
6351                 image->page.y=0;
6352                 image->background_color=mng_background_color;
6353                 (void) SetImageBackgroundColor(image,exception);
6354                 if (logging != MagickFalse)
6355                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6356                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6357                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6358               }
6359           }
6360         /*
6361           Insert a background layer behind the upcoming image if
6362           framing_mode is 3, and we haven't already inserted one.
6363         */
6364         if (insert_layers && (mng_info->framing_mode == 3) &&
6365                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6366                 (simplicity & 0x08)))
6367           {
6368             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6369             {
6370               /*
6371                 Allocate next image structure.
6372               */
6373               AcquireNextImage(image_info,image,exception);
6374
6375               if (GetNextImageInList(image) == (Image *) NULL)
6376                 {
6377                   image=DestroyImageList(image);
6378                   MngInfoFreeStruct(mng_info,&have_mng_structure);
6379                   return((Image *) NULL);
6380                 }
6381
6382               image=SyncNextImageInList(image);
6383             }
6384
6385             mng_info->image=image;
6386
6387             if (term_chunk_found)
6388               {
6389                 image->start_loop=MagickTrue;
6390                 image->iterations=mng_iterations;
6391                 term_chunk_found=MagickFalse;
6392               }
6393
6394             else
6395                 image->start_loop=MagickFalse;
6396
6397             image->delay=0;
6398             image->columns=subframe_width;
6399             image->rows=subframe_height;
6400             image->page.width=subframe_width;
6401             image->page.height=subframe_height;
6402             image->page.x=mng_info->clip.left;
6403             image->page.y=mng_info->clip.top;
6404             image->background_color=mng_background_color;
6405             image->alpha_trait=UndefinedPixelTrait;
6406             (void) SetImageBackgroundColor(image,exception);
6407
6408             if (logging != MagickFalse)
6409               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6410                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6411                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6412                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6413           }
6414 #endif /* MNG_INSERT_LAYERS */
6415         first_mng_object=MagickFalse;
6416
6417         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6418           {
6419             /*
6420               Allocate next image structure.
6421             */
6422             AcquireNextImage(image_info,image,exception);
6423
6424             if (GetNextImageInList(image) == (Image *) NULL)
6425               {
6426                 image=DestroyImageList(image);
6427                 MngInfoFreeStruct(mng_info,&have_mng_structure);
6428                 return((Image *) NULL);
6429               }
6430
6431             image=SyncNextImageInList(image);
6432           }
6433         mng_info->image=image;
6434         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6435           GetBlobSize(image));
6436
6437         if (status == MagickFalse)
6438           break;
6439
6440         if (term_chunk_found)
6441           {
6442             image->start_loop=MagickTrue;
6443             term_chunk_found=MagickFalse;
6444           }
6445
6446         else
6447             image->start_loop=MagickFalse;
6448
6449         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6450           {
6451             image->delay=frame_delay;
6452             frame_delay=default_frame_delay;
6453           }
6454
6455         else
6456           image->delay=0;
6457
6458         image->page.width=mng_info->mng_width;
6459         image->page.height=mng_info->mng_height;
6460         image->page.x=mng_info->x_off[object_id];
6461         image->page.y=mng_info->y_off[object_id];
6462         image->iterations=mng_iterations;
6463
6464         /*
6465           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6466         */
6467
6468         if (logging != MagickFalse)
6469           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6470             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6471             type[2],type[3]);
6472
6473         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6474
6475         if (offset < 0)
6476           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6477       }
6478
6479     previous=image;
6480     mng_info->image=image;
6481     mng_info->mng_type=mng_type;
6482     mng_info->object_id=object_id;
6483
6484     if (memcmp(type,mng_IHDR,4) == 0)
6485       image=ReadOnePNGImage(mng_info,image_info,exception);
6486
6487 #if defined(JNG_SUPPORTED)
6488     else
6489       image=ReadOneJNGImage(mng_info,image_info,exception);
6490 #endif
6491
6492     if (image == (Image *) NULL)
6493       {
6494         if (IsImageObject(previous) != MagickFalse)
6495           {
6496             (void) DestroyImageList(previous);
6497             (void) CloseBlob(previous);
6498           }
6499
6500         MngInfoFreeStruct(mng_info,&have_mng_structure);
6501         return((Image *) NULL);
6502       }
6503
6504     if (image->columns == 0 || image->rows == 0)
6505       {
6506         (void) CloseBlob(image);
6507         image=DestroyImageList(image);
6508         MngInfoFreeStruct(mng_info,&have_mng_structure);
6509         return((Image *) NULL);
6510       }
6511
6512     mng_info->image=image;
6513
6514     if (mng_type)
6515       {
6516         MngBox
6517           crop_box;
6518
6519         if (mng_info->magn_methx || mng_info->magn_methy)
6520           {
6521             png_uint_32
6522                magnified_height,
6523                magnified_width;
6524
6525             if (logging != MagickFalse)
6526               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6527                 "  Processing MNG MAGN chunk");
6528
6529             if (mng_info->magn_methx == 1)
6530               {
6531                 magnified_width=mng_info->magn_ml;
6532
6533                 if (image->columns > 1)
6534                    magnified_width += mng_info->magn_mr;
6535
6536                 if (image->columns > 2)
6537                    magnified_width += (png_uint_32)
6538                       ((image->columns-2)*(mng_info->magn_mx));
6539               }
6540
6541             else
6542               {
6543                 magnified_width=(png_uint_32) image->columns;
6544
6545                 if (image->columns > 1)
6546                    magnified_width += mng_info->magn_ml-1;
6547
6548                 if (image->columns > 2)
6549                    magnified_width += mng_info->magn_mr-1;
6550
6551                 if (image->columns > 3)
6552                    magnified_width += (png_uint_32)
6553                       ((image->columns-3)*(mng_info->magn_mx-1));
6554               }
6555
6556             if (mng_info->magn_methy == 1)
6557               {
6558                 magnified_height=mng_info->magn_mt;
6559
6560                 if (image->rows > 1)
6561                    magnified_height += mng_info->magn_mb;
6562
6563                 if (image->rows > 2)
6564                    magnified_height += (png_uint_32)
6565                       ((image->rows-2)*(mng_info->magn_my));
6566               }
6567
6568             else
6569               {
6570                 magnified_height=(png_uint_32) image->rows;
6571
6572                 if (image->rows > 1)
6573                    magnified_height += mng_info->magn_mt-1;
6574
6575                 if (image->rows > 2)
6576                    magnified_height += mng_info->magn_mb-1;
6577
6578                 if (image->rows > 3)
6579                    magnified_height += (png_uint_32)
6580                       ((image->rows-3)*(mng_info->magn_my-1));
6581               }
6582
6583             if (magnified_height > image->rows ||
6584                 magnified_width > image->columns)
6585               {
6586                 Image
6587                   *large_image;
6588
6589                 int
6590                   yy;
6591
6592                 Quantum
6593                   *next,
6594                   *prev;
6595
6596                 png_uint_16
6597                   magn_methx,
6598                   magn_methy;
6599
6600                 ssize_t
6601                   m,
6602                   y;
6603
6604                 register Quantum
6605                   *n,
6606                   *q;
6607
6608                 register ssize_t
6609                   x;
6610
6611                 /* Allocate next image structure.  */
6612
6613                 if (logging != MagickFalse)
6614                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6615                     "    Allocate magnified image");
6616
6617                 AcquireNextImage(image_info,image,exception);
6618
6619                 if (GetNextImageInList(image) == (Image *) NULL)
6620                   {
6621                     image=DestroyImageList(image);
6622                     MngInfoFreeStruct(mng_info,&have_mng_structure);
6623                     return((Image *) NULL);
6624                   }
6625
6626                 large_image=SyncNextImageInList(image);
6627
6628                 large_image->columns=magnified_width;
6629                 large_image->rows=magnified_height;
6630
6631                 magn_methx=mng_info->magn_methx;
6632                 magn_methy=mng_info->magn_methy;
6633
6634 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6635 #define QM unsigned short
6636                 if (magn_methx != 1 || magn_methy != 1)
6637                   {
6638                   /*
6639                      Scale pixels to unsigned shorts to prevent
6640                      overflow of intermediate values of interpolations
6641                   */
6642                      for (y=0; y < (ssize_t) image->rows; y++)
6643                      {
6644                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6645                           exception);
6646
6647                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6648                        {
6649                           SetPixelRed(image,ScaleQuantumToShort(
6650                             GetPixelRed(image,q)),q);
6651                           SetPixelGreen(image,ScaleQuantumToShort(
6652                             GetPixelGreen(image,q)),q);
6653                           SetPixelBlue(image,ScaleQuantumToShort(
6654                             GetPixelBlue(image,q)),q);
6655                           SetPixelAlpha(image,ScaleQuantumToShort(
6656                             GetPixelAlpha(image,q)),q);
6657                           q+=GetPixelChannels(image);
6658                        }
6659
6660                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6661                          break;
6662                      }
6663                   }
6664 #else
6665 #define QM Quantum
6666 #endif
6667
6668                 if (image->alpha_trait == BlendPixelTrait)
6669                    (void) SetImageBackgroundColor(large_image,exception);
6670
6671                 else
6672                   {
6673                     large_image->background_color.alpha=OpaqueAlpha;
6674                     (void) SetImageBackgroundColor(large_image,exception);
6675
6676                     if (magn_methx == 4)
6677                       magn_methx=2;
6678
6679                     if (magn_methx == 5)
6680                       magn_methx=3;
6681
6682                     if (magn_methy == 4)
6683                       magn_methy=2;
6684
6685                     if (magn_methy == 5)
6686                       magn_methy=3;
6687                   }
6688
6689                 /* magnify the rows into the right side of the large image */
6690
6691                 if (logging != MagickFalse)
6692                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6693                     "    Magnify the rows to %.20g",(double) large_image->rows);
6694                 m=(ssize_t) mng_info->magn_mt;
6695                 yy=0;
6696                 length=(size_t) image->columns*GetPixelChannels(image);
6697                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6698                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6699
6700                 if ((prev == (Quantum *) NULL) ||
6701                     (next == (Quantum *) NULL))
6702                   {
6703                      image=DestroyImageList(image);
6704                      MngInfoFreeStruct(mng_info,&have_mng_structure);
6705                      ThrowReaderException(ResourceLimitError,
6706                        "MemoryAllocationFailed");
6707                   }
6708
6709                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6710                 (void) CopyMagickMemory(next,n,length);
6711
6712                 for (y=0; y < (ssize_t) image->rows; y++)
6713                 {
6714                   if (y == 0)
6715                     m=(ssize_t) mng_info->magn_mt;
6716
6717                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6718                     m=(ssize_t) mng_info->magn_mb;
6719
6720                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6721                     m=(ssize_t) mng_info->magn_mb;
6722
6723                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6724                     m=1;
6725
6726                   else
6727                     m=(ssize_t) mng_info->magn_my;
6728
6729                   n=prev;
6730                   prev=next;
6731                   next=n;
6732
6733                   if (y < (ssize_t) image->rows-1)
6734                     {
6735                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6736                           exception);
6737                       (void) CopyMagickMemory(next,n,length);
6738                     }
6739
6740                   for (i=0; i < m; i++, yy++)
6741                   {
6742                     register Quantum
6743                       *pixels;
6744
6745                     assert(yy < (ssize_t) large_image->rows);
6746                     pixels=prev;
6747                     n=next;
6748                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6749                       1,exception);
6750                     q+=(large_image->columns-image->columns)*
6751                       GetPixelChannels(large_image);
6752
6753                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
6754                     {
6755                       /* To do: get color as function of indexes[x] */
6756                       /*
6757                       if (image->storage_class == PseudoClass)
6758                         {
6759                         }
6760                       */
6761
6762                       if (magn_methy <= 1)
6763                         {
6764                           /* replicate previous */
6765                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6766                           SetPixelGreen(large_image,GetPixelGreen(image,
6767                              pixels),q);
6768                           SetPixelBlue(large_image,GetPixelBlue(image,
6769                              pixels),q);
6770                           SetPixelAlpha(large_image,GetPixelAlpha(image,
6771                              pixels),q);
6772                         }
6773
6774                       else if (magn_methy == 2 || magn_methy == 4)
6775                         {
6776                           if (i == 0)
6777                             {
6778                               SetPixelRed(large_image,GetPixelRed(image,
6779                                  pixels),q);
6780                               SetPixelGreen(large_image,GetPixelGreen(image,
6781                                  pixels),q);
6782                               SetPixelBlue(large_image,GetPixelBlue(image,
6783                                  pixels),q);
6784                               SetPixelAlpha(large_image,GetPixelAlpha(image,
6785                                  pixels),q);
6786                             }
6787
6788                           else
6789                             {
6790                               /* Interpolate */
6791                               SetPixelRed(large_image,((QM) (((ssize_t)
6792                                  (2*i*(GetPixelRed(image,n)
6793                                  -GetPixelRed(image,pixels)+m))/
6794                                  ((ssize_t) (m*2))
6795                                  +GetPixelRed(image,pixels)))),q);
6796                               SetPixelGreen(large_image,((QM) (((ssize_t)
6797                                  (2*i*(GetPixelGreen(image,n)
6798                                  -GetPixelGreen(image,pixels)+m))/
6799                                  ((ssize_t) (m*2))
6800                                  +GetPixelGreen(image,pixels)))),q);
6801                               SetPixelBlue(large_image,((QM) (((ssize_t)
6802                                  (2*i*(GetPixelBlue(image,n)
6803                                  -GetPixelBlue(image,pixels)+m))/
6804                                  ((ssize_t) (m*2))
6805                                  +GetPixelBlue(image,pixels)))),q);
6806
6807                               if (image->alpha_trait == BlendPixelTrait)
6808                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
6809                                     (2*i*(GetPixelAlpha(image,n)
6810                                     -GetPixelAlpha(image,pixels)+m))
6811                                     /((ssize_t) (m*2))+
6812                                    GetPixelAlpha(image,pixels)))),q);
6813                             }
6814
6815                           if (magn_methy == 4)
6816                             {
6817                               /* Replicate nearest */
6818                               if (i <= ((m+1) << 1))
6819                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6820                                     pixels),q);
6821                               else
6822                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6823                                     n),q);
6824                             }
6825                         }
6826
6827                       else /* if (magn_methy == 3 || magn_methy == 5) */
6828                         {
6829                           /* Replicate nearest */
6830                           if (i <= ((m+1) << 1))
6831                           {
6832                              SetPixelRed(large_image,GetPixelRed(image,
6833                                     pixels),q);
6834                              SetPixelGreen(large_image,GetPixelGreen(image,
6835                                     pixels),q);
6836                              SetPixelBlue(large_image,GetPixelBlue(image,
6837                                     pixels),q);
6838                              SetPixelAlpha(large_image,GetPixelAlpha(image,
6839                                     pixels),q);
6840                           }
6841
6842                           else
6843                           {
6844                              SetPixelRed(large_image,GetPixelRed(image,n),q);
6845                              SetPixelGreen(large_image,GetPixelGreen(image,n),
6846                                     q);
6847                              SetPixelBlue(large_image,GetPixelBlue(image,n),
6848                                     q);
6849                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6850                                     q);
6851                           }
6852
6853                           if (magn_methy == 5)
6854                             {
6855                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6856                                  (GetPixelAlpha(image,n)
6857                                  -GetPixelAlpha(image,pixels))
6858                                  +m))/((ssize_t) (m*2))
6859                                  +GetPixelAlpha(image,pixels)),q);
6860                             }
6861                         }
6862                       n+=GetPixelChannels(image);
6863                       q+=GetPixelChannels(large_image);
6864                       pixels+=GetPixelChannels(image);
6865                     } /* x */
6866
6867                     if (SyncAuthenticPixels(large_image,exception) == 0)
6868                       break;
6869
6870                   } /* i */
6871                 } /* y */
6872
6873                 prev=(Quantum *) RelinquishMagickMemory(prev);
6874                 next=(Quantum *) RelinquishMagickMemory(next);
6875
6876                 length=image->columns;
6877
6878                 if (logging != MagickFalse)
6879                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6880                     "    Delete original image");
6881
6882                 DeleteImageFromList(&image);
6883
6884                 image=large_image;
6885
6886                 mng_info->image=image;
6887
6888                 /* magnify the columns */
6889                 if (logging != MagickFalse)
6890                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6891                     "    Magnify the columns to %.20g",(double) image->columns);
6892
6893                 for (y=0; y < (ssize_t) image->rows; y++)
6894                 {
6895                   register Quantum
6896                     *pixels;
6897
6898                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6899                   pixels=q+(image->columns-length)*GetPixelChannels(image);
6900                   n=pixels+GetPixelChannels(image);
6901
6902                   for (x=(ssize_t) (image->columns-length);
6903                     x < (ssize_t) image->columns; x++)
6904                   {
6905                     /* To do: Rewrite using Get/Set***PixelChannel() */
6906
6907                     if (x == (ssize_t) (image->columns-length))
6908                       m=(ssize_t) mng_info->magn_ml;
6909
6910                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6911                       m=(ssize_t) mng_info->magn_mr;
6912
6913                     else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6914                       m=(ssize_t) mng_info->magn_mr;
6915
6916                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6917                       m=1;
6918
6919                     else
6920                       m=(ssize_t) mng_info->magn_mx;
6921
6922                     for (i=0; i < m; i++)
6923                     {
6924                       if (magn_methx <= 1)
6925                         {
6926                           /* replicate previous */
6927                           SetPixelRed(image,GetPixelRed(image,pixels),q);
6928                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6929                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6930                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6931                         }
6932
6933                       else if (magn_methx == 2 || magn_methx == 4)
6934                         {
6935                           if (i == 0)
6936                           {
6937                             SetPixelRed(image,GetPixelRed(image,pixels),q);
6938                             SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6939                             SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6940                             SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6941                           }
6942
6943                           /* To do: Rewrite using Get/Set***PixelChannel() */
6944                           else
6945                             {
6946                               /* Interpolate */
6947                               SetPixelRed(image,(QM) ((2*i*(
6948                                  GetPixelRed(image,n)
6949                                  -GetPixelRed(image,pixels))+m)
6950                                  /((ssize_t) (m*2))+
6951                                  GetPixelRed(image,pixels)),q);
6952
6953                               SetPixelGreen(image,(QM) ((2*i*(
6954                                  GetPixelGreen(image,n)
6955                                  -GetPixelGreen(image,pixels))+m)
6956                                  /((ssize_t) (m*2))+
6957                                  GetPixelGreen(image,pixels)),q);
6958
6959                               SetPixelBlue(image,(QM) ((2*i*(
6960                                  GetPixelBlue(image,n)
6961                                  -GetPixelBlue(image,pixels))+m)
6962                                  /((ssize_t) (m*2))+
6963                                  GetPixelBlue(image,pixels)),q);
6964                               if (image->alpha_trait == BlendPixelTrait)
6965                                  SetPixelAlpha(image,(QM) ((2*i*(
6966                                    GetPixelAlpha(image,n)
6967                                    -GetPixelAlpha(image,pixels))+m)
6968                                    /((ssize_t) (m*2))+
6969                                    GetPixelAlpha(image,pixels)),q);
6970                             }
6971
6972                           if (magn_methx == 4)
6973                             {
6974                               /* Replicate nearest */
6975                               if (i <= ((m+1) << 1))
6976                               {
6977                                  SetPixelAlpha(image,
6978                                    GetPixelAlpha(image,pixels)+0,q);
6979                               }
6980                               else
6981                               {
6982                                  SetPixelAlpha(image,
6983                                    GetPixelAlpha(image,n)+0,q);
6984                               }
6985                             }
6986                         }
6987
6988                       else /* if (magn_methx == 3 || magn_methx == 5) */
6989                         {
6990                           /* Replicate nearest */
6991                           if (i <= ((m+1) << 1))
6992                           {
6993                              SetPixelRed(image,GetPixelRed(image,pixels),q);
6994                              SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6995                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6996                              SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6997                           }
6998
6999                           else
7000                           {
7001                              SetPixelRed(image,GetPixelRed(image,n),q);
7002                              SetPixelGreen(image,GetPixelGreen(image,n),q);
7003                              SetPixelBlue(image,GetPixelBlue(image,n),q);
7004                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7005                           }
7006
7007                           if (magn_methx == 5)
7008                             {
7009                               /* Interpolate */
7010                               SetPixelAlpha(image,
7011                                  (QM) ((2*i*( GetPixelAlpha(image,n)
7012                                  -GetPixelAlpha(image,pixels))+m)/
7013                                  ((ssize_t) (m*2))
7014                                  +GetPixelAlpha(image,pixels)),q);
7015                             }
7016                         }
7017                       q+=GetPixelChannels(image);
7018                     }
7019                     n+=GetPixelChannels(image);
7020                     p+=GetPixelChannels(image);
7021                   }
7022
7023                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
7024                     break;
7025                 }
7026 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7027               if (magn_methx != 1 || magn_methy != 1)
7028                 {
7029                 /*
7030                    Rescale pixels to Quantum
7031                 */
7032                    for (y=0; y < (ssize_t) image->rows; y++)
7033                    {
7034                      q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7035
7036                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
7037                      {
7038                         SetPixelRed(image,ScaleShortToQuantum(
7039                           GetPixelRed(image,q)),q);
7040                         SetPixelGreen(image,ScaleShortToQuantum(
7041                           GetPixelGreen(image,q)),q);
7042                         SetPixelBlue(image,ScaleShortToQuantum(
7043                           GetPixelBlue(image,q)),q);
7044                         SetPixelAlpha(image,ScaleShortToQuantum(
7045                           GetPixelAlpha(image,q)),q);
7046                         q+=GetPixelChannels(image);
7047                      }
7048
7049                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
7050                        break;
7051                    }
7052                 }
7053 #endif
7054                 if (logging != MagickFalse)
7055                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7056                     "  Finished MAGN processing");
7057               }
7058           }
7059
7060         /*
7061           Crop_box is with respect to the upper left corner of the MNG.
7062         */
7063         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7064         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7065         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7066         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7067         crop_box=mng_minimum_box(crop_box,mng_info->clip);
7068         crop_box=mng_minimum_box(crop_box,mng_info->frame);
7069         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7070         if ((crop_box.left != (mng_info->image_box.left
7071             +mng_info->x_off[object_id])) ||
7072             (crop_box.right != (mng_info->image_box.right
7073             +mng_info->x_off[object_id])) ||
7074             (crop_box.top != (mng_info->image_box.top
7075             +mng_info->y_off[object_id])) ||
7076             (crop_box.bottom != (mng_info->image_box.bottom
7077             +mng_info->y_off[object_id])))
7078           {
7079             if (logging != MagickFalse)
7080               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7081                 "  Crop the PNG image");
7082
7083             if ((crop_box.left < crop_box.right) &&
7084                 (crop_box.top < crop_box.bottom))
7085               {
7086                 Image
7087                   *im;
7088
7089                 RectangleInfo
7090                   crop_info;
7091
7092                 /*
7093                   Crop_info is with respect to the upper left corner of
7094                   the image.
7095                 */
7096                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7097                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7098                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7099                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7100                 image->page.width=image->columns;
7101                 image->page.height=image->rows;
7102                 image->page.x=0;
7103                 image->page.y=0;
7104                 im=CropImage(image,&crop_info,exception);
7105
7106                 if (im != (Image *) NULL)
7107                   {
7108                     image->columns=im->columns;
7109                     image->rows=im->rows;
7110                     im=DestroyImage(im);
7111                     image->page.width=image->columns;
7112                     image->page.height=image->rows;
7113                     image->page.x=crop_box.left;
7114                     image->page.y=crop_box.top;
7115                   }
7116               }
7117
7118             else
7119               {
7120                 /*
7121                   No pixels in crop area.  The MNG spec still requires
7122                   a layer, though, so make a single transparent pixel in
7123                   the top left corner.
7124                 */
7125                 image->columns=1;
7126                 image->rows=1;
7127                 image->colors=2;
7128                 (void) SetImageBackgroundColor(image,exception);
7129                 image->page.width=1;
7130                 image->page.height=1;
7131                 image->page.x=0;
7132                 image->page.y=0;
7133               }
7134           }
7135 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7136         image=mng_info->image;
7137 #endif
7138       }
7139
7140 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7141       /* PNG does not handle depths greater than 16 so reduce it even
7142        * if lossy.
7143        */
7144       if (image->depth > 16)
7145          image->depth=16;
7146 #endif
7147
7148 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7149       if (image->depth > 8)
7150         {
7151           /* To do: fill low byte properly */
7152           image->depth=16;
7153         }
7154
7155       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7156          image->depth = 8;
7157 #endif
7158
7159       if (image_info->number_scenes != 0)
7160         {
7161           if (mng_info->scenes_found >
7162              (ssize_t) (image_info->first_scene+image_info->number_scenes))
7163             break;
7164         }
7165
7166       if (logging != MagickFalse)
7167         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7168           "  Finished reading image datastream.");
7169
7170   } while (LocaleCompare(image_info->magick,"MNG") == 0);
7171
7172   (void) CloseBlob(image);
7173
7174   if (logging != MagickFalse)
7175     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7176       "  Finished reading all image datastreams.");
7177
7178 #if defined(MNG_INSERT_LAYERS)
7179   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7180        (mng_info->mng_height))
7181     {
7182       /*
7183         Insert a background layer if nothing else was found.
7184       */
7185       if (logging != MagickFalse)
7186         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7187           "  No images found.  Inserting a background layer.");
7188
7189       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7190         {
7191           /*
7192             Allocate next image structure.
7193           */
7194           AcquireNextImage(image_info,image,exception);
7195           if (GetNextImageInList(image) == (Image *) NULL)
7196             {
7197               image=DestroyImageList(image);
7198               MngInfoFreeStruct(mng_info,&have_mng_structure);
7199
7200               if (logging != MagickFalse)
7201                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7202                   "  Allocation failed, returning NULL.");
7203
7204               return((Image *) NULL);
7205             }
7206           image=SyncNextImageInList(image);
7207         }
7208       image->columns=mng_info->mng_width;
7209       image->rows=mng_info->mng_height;
7210       image->page.width=mng_info->mng_width;
7211       image->page.height=mng_info->mng_height;
7212       image->page.x=0;
7213       image->page.y=0;
7214       image->background_color=mng_background_color;
7215       image->alpha_trait=UndefinedPixelTrait;
7216
7217       if (image_info->ping == MagickFalse)
7218         (void) SetImageBackgroundColor(image,exception);
7219
7220       mng_info->image_found++;
7221     }
7222 #endif
7223   image->iterations=mng_iterations;
7224
7225   if (mng_iterations == 1)
7226     image->start_loop=MagickTrue;
7227
7228   while (GetPreviousImageInList(image) != (Image *) NULL)
7229   {
7230     image_count++;
7231     if (image_count > 10*mng_info->image_found)
7232       {
7233         if (logging != MagickFalse)
7234           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7235
7236         (void) ThrowMagickException(exception,GetMagickModule(),
7237           CoderError,"Linked list is corrupted, beginning of list not found",
7238           "`%s'",image_info->filename);
7239
7240         return((Image *) NULL);
7241       }
7242
7243     image=GetPreviousImageInList(image);
7244
7245     if (GetNextImageInList(image) == (Image *) NULL)
7246       {
7247         if (logging != MagickFalse)
7248           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7249
7250         (void) ThrowMagickException(exception,GetMagickModule(),
7251           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7252           image_info->filename);
7253       }
7254   }
7255
7256   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7257              GetNextImageInList(image) ==
7258      (Image *) NULL)
7259     {
7260       if (logging != MagickFalse)
7261         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7262             "  First image null");
7263
7264       (void) ThrowMagickException(exception,GetMagickModule(),
7265         CoderError,"image->next for first image is NULL but shouldn't be.",
7266         "`%s'",image_info->filename);
7267     }
7268
7269   if (mng_info->image_found == 0)
7270     {
7271       if (logging != MagickFalse)
7272         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7273           "  No visible images found.");
7274
7275       (void) ThrowMagickException(exception,GetMagickModule(),
7276         CoderError,"No visible images in file","`%s'",image_info->filename);
7277
7278       if (image != (Image *) NULL)
7279         image=DestroyImageList(image);
7280
7281       MngInfoFreeStruct(mng_info,&have_mng_structure);
7282       return((Image *) NULL);
7283     }
7284
7285   if (mng_info->ticks_per_second)
7286     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7287             final_delay/mng_info->ticks_per_second;
7288
7289   else
7290     image->start_loop=MagickTrue;
7291
7292   /* Find final nonzero image delay */
7293   final_image_delay=0;
7294
7295   while (GetNextImageInList(image) != (Image *) NULL)
7296     {
7297       if (image->delay)
7298         final_image_delay=image->delay;
7299
7300       image=GetNextImageInList(image);
7301     }
7302
7303   if (final_delay < final_image_delay)
7304     final_delay=final_image_delay;
7305
7306   image->delay=final_delay;
7307
7308   if (logging != MagickFalse)
7309       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7310         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7311         (double) final_delay);
7312
7313   if (logging != MagickFalse)
7314     {
7315       int
7316         scene;
7317
7318       scene=0;
7319       image=GetFirstImageInList(image);
7320
7321       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7322         "  Before coalesce:");
7323
7324       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7325         "    scene 0 delay=%.20g",(double) image->delay);
7326
7327       while (GetNextImageInList(image) != (Image *) NULL)
7328       {
7329         image=GetNextImageInList(image);
7330         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7331           "    scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7332       }
7333     }
7334
7335   image=GetFirstImageInList(image);
7336 #ifdef MNG_COALESCE_LAYERS
7337   if (insert_layers)
7338     {
7339       Image
7340         *next_image,
7341         *next;
7342
7343       size_t
7344         scene;
7345
7346       if (logging != MagickFalse)
7347         (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Coalesce Images");
7348
7349       scene=image->scene;
7350       next_image=CoalesceImages(image,exception);
7351
7352       if (next_image == (Image *) NULL)
7353         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7354
7355       image=DestroyImageList(image);
7356       image=next_image;
7357
7358       for (next=image; next != (Image *) NULL; next=next_image)
7359       {
7360          next->page.width=mng_info->mng_width;
7361          next->page.height=mng_info->mng_height;
7362          next->page.x=0;
7363          next->page.y=0;
7364          next->scene=scene++;
7365          next_image=GetNextImageInList(next);
7366
7367          if (next_image == (Image *) NULL)
7368            break;
7369
7370          if (next->delay == 0)
7371            {
7372              scene--;
7373              next_image->previous=GetPreviousImageInList(next);
7374              if (GetPreviousImageInList(next) == (Image *) NULL)
7375                image=next_image;
7376              else
7377                next->previous->next=next_image;
7378              next=DestroyImage(next);
7379            }
7380       }
7381     }
7382 #endif
7383
7384   while (GetNextImageInList(image) != (Image *) NULL)
7385       image=GetNextImageInList(image);
7386
7387   image->dispose=BackgroundDispose;
7388
7389   if (logging != MagickFalse)
7390     {
7391       int
7392         scene;
7393
7394       scene=0;
7395       image=GetFirstImageInList(image);
7396
7397       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7398         "  After coalesce:");
7399
7400       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7401         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7402         (double) image->dispose);
7403
7404       while (GetNextImageInList(image) != (Image *) NULL)
7405       {
7406         image=GetNextImageInList(image);
7407
7408         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7409           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7410           (double) image->delay,(double) image->dispose);
7411       }
7412    }
7413
7414   image=GetFirstImageInList(image);
7415   MngInfoFreeStruct(mng_info,&have_mng_structure);
7416   have_mng_structure=MagickFalse;
7417
7418   if (logging != MagickFalse)
7419     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7420
7421   return(GetFirstImageInList(image));
7422 }
7423 #else /* PNG_LIBPNG_VER > 10011 */
7424 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7425 {
7426   printf("Your PNG library is too old: You have libpng-%s\n",
7427      PNG_LIBPNG_VER_STRING);
7428
7429   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7430     "PNG library is too old","`%s'",image_info->filename);
7431
7432   return(Image *) NULL;
7433 }
7434
7435 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7436 {
7437   return(ReadPNGImage(image_info,exception));
7438 }
7439 #endif /* PNG_LIBPNG_VER > 10011 */
7440 #endif
7441 \f
7442 /*
7443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7444 %                                                                             %
7445 %                                                                             %
7446 %                                                                             %
7447 %   R e g i s t e r P N G I m a g e                                           %
7448 %                                                                             %
7449 %                                                                             %
7450 %                                                                             %
7451 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7452 %
7453 %  RegisterPNGImage() adds properties for the PNG image format to
7454 %  the list of supported formats.  The properties include the image format
7455 %  tag, a method to read and/or write the format, whether the format
7456 %  supports the saving of more than one frame to the same file or blob,
7457 %  whether the format supports native in-memory I/O, and a brief
7458 %  description of the format.
7459 %
7460 %  The format of the RegisterPNGImage method is:
7461 %
7462 %      size_t RegisterPNGImage(void)
7463 %
7464 */
7465 ModuleExport size_t RegisterPNGImage(void)
7466 {
7467   char
7468     version[MaxTextExtent];
7469
7470   MagickInfo
7471     *entry;
7472
7473   static const char
7474     *PNGNote=
7475     {
7476       "See http://www.libpng.org/ for details about the PNG format."
7477     },
7478
7479     *JNGNote=
7480     {
7481       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7482       "format."
7483     },
7484
7485     *MNGNote=
7486     {
7487       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7488       "format."
7489     };
7490
7491   *version='\0';
7492
7493 #if defined(PNG_LIBPNG_VER_STRING)
7494   (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7495   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7496
7497   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7498     {
7499       (void) ConcatenateMagickString(version,",",MaxTextExtent);
7500       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7501             MaxTextExtent);
7502     }
7503 #endif
7504
7505   entry=SetMagickInfo("MNG");
7506   entry->seekable_stream=MagickTrue;  /* To do: eliminate this. */
7507
7508 #if defined(MAGICKCORE_PNG_DELEGATE)
7509   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7510   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7511 #endif
7512
7513   entry->magick=(IsImageFormatHandler *) IsMNG;
7514   entry->description=ConstantString("Multiple-image Network Graphics");
7515
7516   if (*version != '\0')
7517     entry->version=ConstantString(version);
7518
7519   entry->mime_type=ConstantString("video/x-mng");
7520   entry->module=ConstantString("PNG");
7521   entry->note=ConstantString(MNGNote);
7522   (void) RegisterMagickInfo(entry);
7523
7524   entry=SetMagickInfo("PNG");
7525
7526 #if defined(MAGICKCORE_PNG_DELEGATE)
7527   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7528   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7529 #endif
7530
7531   entry->magick=(IsImageFormatHandler *) IsPNG;
7532   entry->adjoin=MagickFalse;
7533   entry->description=ConstantString("Portable Network Graphics");
7534   entry->mime_type=ConstantString("image/png");
7535   entry->module=ConstantString("PNG");
7536
7537   if (*version != '\0')
7538     entry->version=ConstantString(version);
7539
7540   entry->note=ConstantString(PNGNote);
7541   (void) RegisterMagickInfo(entry);
7542
7543   entry=SetMagickInfo("PNG8");
7544
7545 #if defined(MAGICKCORE_PNG_DELEGATE)
7546   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7547   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7548 #endif
7549
7550   entry->magick=(IsImageFormatHandler *) IsPNG;
7551   entry->adjoin=MagickFalse;
7552   entry->description=ConstantString(
7553             "8-bit indexed with optional binary transparency");
7554   entry->mime_type=ConstantString("image/png");
7555   entry->module=ConstantString("PNG");
7556   (void) RegisterMagickInfo(entry);
7557
7558   entry=SetMagickInfo("PNG24");
7559   *version='\0';
7560
7561 #if defined(ZLIB_VERSION)
7562   (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7563   (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7564
7565   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7566     {
7567       (void) ConcatenateMagickString(version,",",MaxTextExtent);
7568       (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7569     }
7570 #endif
7571
7572   if (*version != '\0')
7573     entry->version=ConstantString(version);
7574
7575 #if defined(MAGICKCORE_PNG_DELEGATE)
7576   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7577   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7578 #endif
7579
7580   entry->magick=(IsImageFormatHandler *) IsPNG;
7581   entry->adjoin=MagickFalse;
7582   entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
7583   entry->mime_type=ConstantString("image/png");
7584   entry->module=ConstantString("PNG");
7585   (void) RegisterMagickInfo(entry);
7586
7587   entry=SetMagickInfo("PNG32");
7588
7589 #if defined(MAGICKCORE_PNG_DELEGATE)
7590   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7591   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7592 #endif
7593
7594   entry->magick=(IsImageFormatHandler *) IsPNG;
7595   entry->adjoin=MagickFalse;
7596   entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7597   entry->mime_type=ConstantString("image/png");
7598   entry->module=ConstantString("PNG");
7599   (void) RegisterMagickInfo(entry);
7600
7601   entry=SetMagickInfo("PNG48");
7602
7603 #if defined(MAGICKCORE_PNG_DELEGATE)
7604   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7605   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7606 #endif
7607
7608   entry->magick=(IsImageFormatHandler *) IsPNG;
7609   entry->adjoin=MagickFalse;
7610   entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7611   entry->mime_type=ConstantString("image/png");
7612   entry->module=ConstantString("PNG");
7613   (void) RegisterMagickInfo(entry);
7614
7615   entry=SetMagickInfo("PNG64");
7616
7617 #if defined(MAGICKCORE_PNG_DELEGATE)
7618   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7619   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7620 #endif
7621
7622   entry->magick=(IsImageFormatHandler *) IsPNG;
7623   entry->adjoin=MagickFalse;
7624   entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7625   entry->mime_type=ConstantString("image/png");
7626   entry->module=ConstantString("PNG");
7627   (void) RegisterMagickInfo(entry);
7628
7629   entry=SetMagickInfo("PNG00");
7630
7631 #if defined(MAGICKCORE_PNG_DELEGATE)
7632   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7633   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7634 #endif
7635
7636   entry->magick=(IsImageFormatHandler *) IsPNG;
7637   entry->adjoin=MagickFalse;
7638   entry->description=ConstantString(
7639     "PNG inheriting bit-depth and color-type from original");
7640   entry->mime_type=ConstantString("image/png");
7641   entry->module=ConstantString("PNG");
7642   (void) RegisterMagickInfo(entry);
7643
7644   entry=SetMagickInfo("JNG");
7645
7646 #if defined(JNG_SUPPORTED)
7647 #if defined(MAGICKCORE_PNG_DELEGATE)
7648   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7649   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7650 #endif
7651 #endif
7652
7653   entry->magick=(IsImageFormatHandler *) IsJNG;
7654   entry->adjoin=MagickFalse;
7655   entry->description=ConstantString("JPEG Network Graphics");
7656   entry->mime_type=ConstantString("image/x-jng");
7657   entry->module=ConstantString("PNG");
7658   entry->note=ConstantString(JNGNote);
7659   (void) RegisterMagickInfo(entry);
7660
7661 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7662   ping_semaphore=AcquireSemaphoreInfo();
7663 #endif
7664
7665   return(MagickImageCoderSignature);
7666 }
7667 \f
7668 /*
7669 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7670 %                                                                             %
7671 %                                                                             %
7672 %                                                                             %
7673 %   U n r e g i s t e r P N G I m a g e                                       %
7674 %                                                                             %
7675 %                                                                             %
7676 %                                                                             %
7677 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7678 %
7679 %  UnregisterPNGImage() removes format registrations made by the
7680 %  PNG module from the list of supported formats.
7681 %
7682 %  The format of the UnregisterPNGImage method is:
7683 %
7684 %      UnregisterPNGImage(void)
7685 %
7686 */
7687 ModuleExport void UnregisterPNGImage(void)
7688 {
7689   (void) UnregisterMagickInfo("MNG");
7690   (void) UnregisterMagickInfo("PNG");
7691   (void) UnregisterMagickInfo("PNG8");
7692   (void) UnregisterMagickInfo("PNG24");
7693   (void) UnregisterMagickInfo("PNG32");
7694   (void) UnregisterMagickInfo("PNG48");
7695   (void) UnregisterMagickInfo("PNG64");
7696   (void) UnregisterMagickInfo("PNG00");
7697   (void) UnregisterMagickInfo("JNG");
7698
7699 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7700   if (ping_semaphore != (SemaphoreInfo *) NULL)
7701     RelinquishSemaphoreInfo(&ping_semaphore);
7702 #endif
7703 }
7704 \f
7705 #if defined(MAGICKCORE_PNG_DELEGATE)
7706 #if PNG_LIBPNG_VER > 10011
7707 /*
7708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7709 %                                                                             %
7710 %                                                                             %
7711 %                                                                             %
7712 %   W r i t e M N G I m a g e                                                 %
7713 %                                                                             %
7714 %                                                                             %
7715 %                                                                             %
7716 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7717 %
7718 %  WriteMNGImage() writes an image in the Portable Network Graphics
7719 %  Group's "Multiple-image Network Graphics" encoded image format.
7720 %
7721 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
7722 %
7723 %  The format of the WriteMNGImage method is:
7724 %
7725 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7726 %        Image *image,ExceptionInfo *exception)
7727 %
7728 %  A description of each parameter follows.
7729 %
7730 %    o image_info: the image info.
7731 %
7732 %    o image:  The image.
7733 %
7734 %    o exception: return any errors or warnings in this structure.
7735 %
7736 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7737 %    "To do" under ReadPNGImage):
7738 %
7739 %    Preserve all unknown and not-yet-handled known chunks found in input
7740 %    PNG file and copy them  into output PNG files according to the PNG
7741 %    copying rules.
7742 %
7743 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
7744 %
7745 %    Improve selection of color type (use indexed-colour or indexed-colour
7746 %    with tRNS when 256 or fewer unique RGBA values are present).
7747 %
7748 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7749 %    This will be complicated if we limit ourselves to generating MNG-LC
7750 %    files.  For now we ignore disposal method 3 and simply overlay the next
7751 %    image on it.
7752 %
7753 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
7754 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
7755 %    [mostly done 15 June 1999 but still need to take care of tRNS]
7756 %
7757 %    Check for identical sRGB and replace with a global sRGB (and remove
7758 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7759 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7760 %    local gAMA/cHRM with local sRGB if appropriate).
7761 %
7762 %    Check for identical sBIT chunks and write global ones.
7763 %
7764 %    Provide option to skip writing the signature tEXt chunks.
7765 %
7766 %    Use signatures to detect identical objects and reuse the first
7767 %    instance of such objects instead of writing duplicate objects.
7768 %
7769 %    Use a smaller-than-32k value of compression window size when
7770 %    appropriate.
7771 %
7772 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
7773 %    ancillary text chunks and save profiles.
7774 %
7775 %    Provide an option to force LC files (to ensure exact framing rate)
7776 %    instead of VLC.
7777 %
7778 %    Provide an option to force VLC files instead of LC, even when offsets
7779 %    are present.  This will involve expanding the embedded images with a
7780 %    transparent region at the top and/or left.
7781 */
7782
7783 static void
7784 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7785    png_info *ping_info, unsigned char *profile_type, unsigned char
7786    *profile_description, unsigned char *profile_data, png_uint_32 length)
7787 {
7788    png_textp
7789      text;
7790
7791    register ssize_t
7792      i;
7793
7794    unsigned char
7795      *sp;
7796
7797    png_charp
7798      dp;
7799
7800    png_uint_32
7801      allocated_length,
7802      description_length;
7803
7804    unsigned char
7805      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7806
7807    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7808       return;
7809
7810    if (image_info->verbose)
7811      {
7812        (void) printf("writing raw profile: type=%s, length=%.20g\n",
7813          (char *) profile_type, (double) length);
7814      }
7815
7816 #if PNG_LIBPNG_VER >= 10400
7817    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7818 #else
7819    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7820 #endif
7821    description_length=(png_uint_32) strlen((const char *) profile_description);
7822    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7823       + description_length);
7824 #if PNG_LIBPNG_VER >= 10400
7825    text[0].text=(png_charp) png_malloc(ping,
7826       (png_alloc_size_t) allocated_length);
7827    text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7828 #else
7829    text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7830    text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7831 #endif
7832    text[0].key[0]='\0';
7833    (void) ConcatenateMagickString(text[0].key,
7834       "Raw profile type ",MaxTextExtent);
7835    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7836    sp=profile_data;
7837    dp=text[0].text;
7838    *dp++='\n';
7839    (void) CopyMagickString(dp,(const char *) profile_description,
7840      allocated_length);
7841    dp+=description_length;
7842    *dp++='\n';
7843    (void) FormatLocaleString(dp,allocated_length-
7844      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7845    dp+=8;
7846
7847    for (i=0; i < (ssize_t) length; i++)
7848    {
7849      if (i%36 == 0)
7850        *dp++='\n';
7851      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7852      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7853    }
7854
7855    *dp++='\n';
7856    *dp='\0';
7857    text[0].text_length=(png_size_t) (dp-text[0].text);
7858    text[0].compression=image_info->compression == NoCompression ||
7859      (image_info->compression == UndefinedCompression &&
7860      text[0].text_length < 128) ? -1 : 0;
7861
7862    if (text[0].text_length <= allocated_length)
7863      png_set_text(ping,ping_info,text,1);
7864
7865    png_free(ping,text[0].text);
7866    png_free(ping,text[0].key);
7867    png_free(ping,text);
7868 }
7869
7870 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7871   const char *string, MagickBooleanType logging)
7872 {
7873   char
7874     *name;
7875
7876   const StringInfo
7877     *profile;
7878
7879   unsigned char
7880     *data;
7881
7882   png_uint_32 length;
7883
7884   ResetImageProfileIterator(image);
7885
7886   for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7887   {
7888     profile=GetImageProfile(image,name);
7889
7890     if (profile != (const StringInfo *) NULL)
7891       {
7892         StringInfo
7893           *ping_profile;
7894
7895         if (LocaleNCompare(name,string,11) == 0)
7896           {
7897             if (logging != MagickFalse)
7898                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7899                    "  Found %s profile",name);
7900
7901             ping_profile=CloneStringInfo(profile);
7902             data=GetStringInfoDatum(ping_profile),
7903             length=(png_uint_32) GetStringInfoLength(ping_profile);
7904             data[4]=data[3];
7905             data[3]=data[2];
7906             data[2]=data[1];
7907             data[1]=data[0];
7908             (void) WriteBlobMSBULong(image,length-5);  /* data length */
7909             (void) WriteBlob(image,length-1,data+1);
7910             (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7911             ping_profile=DestroyStringInfo(ping_profile);
7912           }
7913       }
7914
7915       name=GetNextImageProfile(image);
7916    }
7917
7918    return(MagickTrue);
7919 }
7920
7921
7922 /* Write one PNG image */
7923 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7924   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7925 {
7926   char
7927     im_vers[32],
7928     libpng_runv[32],
7929     libpng_vers[32],
7930     zlib_runv[32],
7931     zlib_vers[32];
7932
7933   Image
7934     *image;
7935
7936   ImageInfo
7937     *image_info;
7938
7939   char
7940     s[2];
7941
7942   const char
7943     *name,
7944     *property,
7945     *value;
7946
7947   const StringInfo
7948     *profile;
7949
7950   int
7951     num_passes,
7952     pass;
7953
7954   png_byte
7955      ping_trans_alpha[256];
7956
7957   png_color
7958      palette[257];
7959
7960   png_color_16
7961     ping_background,
7962     ping_trans_color;
7963
7964   png_info
7965     *ping_info;
7966
7967   png_struct
7968     *ping;
7969
7970   png_uint_32
7971     ping_height,
7972     ping_width;
7973
7974   ssize_t
7975     y;
7976
7977   MagickBooleanType
7978     image_matte,
7979     logging,
7980     matte,
7981
7982     ping_have_blob,
7983     ping_have_cheap_transparency,
7984     ping_have_color,
7985     ping_have_non_bw,
7986     ping_have_PLTE,
7987     ping_have_bKGD,
7988     ping_have_iCCP,
7989     ping_have_pHYs,
7990     ping_have_sRGB,
7991     ping_have_tRNS,
7992
7993     ping_exclude_bKGD,
7994     ping_exclude_cHRM,
7995     ping_exclude_date,
7996     /* ping_exclude_EXIF, */
7997     ping_exclude_gAMA,
7998     ping_exclude_iCCP,
7999     /* ping_exclude_iTXt, */
8000     ping_exclude_oFFs,
8001     ping_exclude_pHYs,
8002     ping_exclude_sRGB,
8003     ping_exclude_tEXt,
8004     /* ping_exclude_tRNS, */
8005     ping_exclude_vpAg,
8006     ping_exclude_zCCP, /* hex-encoded iCCP */
8007     ping_exclude_zTXt,
8008
8009     ping_preserve_colormap,
8010     ping_preserve_iCCP,
8011     ping_need_colortype_warning,
8012
8013     status,
8014     tried_332,
8015     tried_333,
8016     tried_444;
8017
8018   MemoryInfo
8019     *volatile pixel_info;
8020
8021   QuantumInfo
8022     *quantum_info;
8023
8024   PNGErrorInfo
8025     error_info;
8026
8027   register ssize_t
8028     i,
8029     x;
8030
8031   unsigned char
8032     *ping_pixels;
8033
8034   volatile int
8035     image_colors,
8036     ping_bit_depth,
8037     ping_color_type,
8038     ping_interlace_method,
8039     ping_compression_method,
8040     ping_filter_method,
8041     ping_num_trans;
8042
8043   volatile size_t
8044     image_depth,
8045     old_bit_depth;
8046
8047   size_t
8048     quality,
8049     rowbytes,
8050     save_image_depth;
8051
8052   int
8053     j,
8054     number_colors,
8055     number_opaque,
8056     number_semitransparent,
8057     number_transparent,
8058     ping_pHYs_unit_type;
8059
8060   png_uint_32
8061     ping_pHYs_x_resolution,
8062     ping_pHYs_y_resolution;
8063
8064   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8065     "  Enter WriteOnePNGImage()");
8066
8067   image = CloneImage(IMimage,0,0,MagickFalse,exception);
8068   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8069   if (image_info == (ImageInfo *) NULL)
8070      ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8071
8072   /* Define these outside of the following "if logging()" block so they will
8073    * show in debuggers.
8074    */
8075   *im_vers='\0';
8076   (void) ConcatenateMagickString(im_vers,
8077          MagickLibVersionText,MaxTextExtent);
8078   (void) ConcatenateMagickString(im_vers,
8079          MagickLibAddendum,MaxTextExtent);
8080
8081   *libpng_vers='\0';
8082   (void) ConcatenateMagickString(libpng_vers,
8083          PNG_LIBPNG_VER_STRING,32);
8084   *libpng_runv='\0';
8085   (void) ConcatenateMagickString(libpng_runv,
8086          png_get_libpng_ver(NULL),32);
8087
8088   *zlib_vers='\0';
8089   (void) ConcatenateMagickString(zlib_vers,
8090          ZLIB_VERSION,32);
8091   *zlib_runv='\0';
8092   (void) ConcatenateMagickString(zlib_runv,
8093          zlib_version,32);
8094
8095   if (logging)
8096     {
8097        LogMagickEvent(CoderEvent,GetMagickModule(),"    IM version     = %s",
8098            im_vers);
8099        LogMagickEvent(CoderEvent,GetMagickModule(),"    Libpng version = %s",
8100            libpng_vers);
8101        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8102        {
8103        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8104            libpng_runv);
8105        }
8106        LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
8107            zlib_vers);
8108        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8109        {
8110        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8111            zlib_runv);
8112        }
8113     }
8114
8115   /* Initialize some stuff */
8116   ping_bit_depth=0,
8117   ping_color_type=0,
8118   ping_interlace_method=0,
8119   ping_compression_method=0,
8120   ping_filter_method=0,
8121   ping_num_trans = 0;
8122
8123   ping_background.red = 0;
8124   ping_background.green = 0;
8125   ping_background.blue = 0;
8126   ping_background.gray = 0;
8127   ping_background.index = 0;
8128
8129   ping_trans_color.red=0;
8130   ping_trans_color.green=0;
8131   ping_trans_color.blue=0;
8132   ping_trans_color.gray=0;
8133
8134   ping_pHYs_unit_type = 0;
8135   ping_pHYs_x_resolution = 0;
8136   ping_pHYs_y_resolution = 0;
8137
8138   ping_have_blob=MagickFalse;
8139   ping_have_cheap_transparency=MagickFalse;
8140   ping_have_color=MagickTrue;
8141   ping_have_non_bw=MagickTrue;
8142   ping_have_PLTE=MagickFalse;
8143   ping_have_bKGD=MagickFalse;
8144   ping_have_iCCP=MagickFalse;
8145   ping_have_pHYs=MagickFalse;
8146   ping_have_sRGB=MagickFalse;
8147   ping_have_tRNS=MagickFalse;
8148
8149   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8150   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8151   ping_exclude_date=mng_info->ping_exclude_date;
8152   /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8153   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8154   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8155   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8156   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8157   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8158   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8159   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8160   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8161   ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8162   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8163   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8164
8165   ping_preserve_colormap = mng_info->ping_preserve_colormap;
8166   ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8167   ping_need_colortype_warning = MagickFalse;
8168
8169   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8170    * i.e., eliminate the ICC profile and set image->rendering_intent.
8171    * Note that this will not involve any changes to the actual pixels
8172    * but merely passes information to applications that read the resulting
8173    * PNG image.
8174    *
8175    * To do: recognize other variants of the sRGB profile, using the CRC to
8176    * verify all recognized variants including the 7 already known.
8177    *
8178    * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8179    *
8180    * Use something other than image->rendering_intent to record the fact
8181    * that the sRGB profile was found.
8182    *
8183    * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8184    * profile.  Record the Blackpoint Compensation, if any.
8185    */
8186    if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8187    {
8188       char
8189         *name;
8190
8191       const StringInfo
8192         *profile;
8193
8194       ResetImageProfileIterator(image);
8195       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8196       {
8197         profile=GetImageProfile(image,name);
8198
8199         if (profile != (StringInfo *) NULL)
8200           {
8201             if ((LocaleCompare(name,"ICC") == 0) ||
8202                 (LocaleCompare(name,"ICM") == 0))
8203
8204              {
8205                  int
8206                    icheck,
8207                    got_crc=0;
8208
8209
8210                  png_uint_32
8211                    length,
8212                    profile_crc=0;
8213
8214                  unsigned char
8215                    *data;
8216
8217                  length=(png_uint_32) GetStringInfoLength(profile);
8218
8219                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8220                  {
8221                    if (length == sRGB_info[icheck].len)
8222                    {
8223                      if (got_crc == 0)
8224                      {
8225                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8226                          "    Got a %lu-byte ICC profile (potentially sRGB)",
8227                          (unsigned long) length);
8228
8229                        data=GetStringInfoDatum(profile);
8230                        profile_crc=crc32(0,data,length);
8231
8232                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8233                            "      with crc=%8x",(unsigned int) profile_crc);
8234                        got_crc++;
8235                      }
8236
8237                      if (profile_crc == sRGB_info[icheck].crc)
8238                      {
8239                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8240                             "      It is sRGB with rendering intent = %s",
8241                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
8242                              sRGB_info[icheck].intent));
8243                         if (image->rendering_intent==UndefinedIntent)
8244                         {
8245                           image->rendering_intent=
8246                           Magick_RenderingIntent_from_PNG_RenderingIntent(
8247                              sRGB_info[icheck].intent);
8248                         }
8249                         ping_exclude_iCCP = MagickTrue;
8250                         ping_exclude_zCCP = MagickTrue;
8251                         ping_have_sRGB = MagickTrue;
8252                         break;
8253                      }
8254                    }
8255                  }
8256                  if (sRGB_info[icheck].len == 0)
8257                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8258                         "    Got a %lu-byte ICC profile not recognized as sRGB",
8259                         (unsigned long) length);
8260               }
8261           }
8262         name=GetNextImageProfile(image);
8263       }
8264   }
8265
8266   number_opaque = 0;
8267   number_semitransparent = 0;
8268   number_transparent = 0;
8269
8270   if (logging != MagickFalse)
8271     {
8272       if (image->storage_class == UndefinedClass)
8273           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8274           "    storage_class=UndefinedClass");
8275       if (image->storage_class == DirectClass)
8276           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8277           "    storage_class=DirectClass");
8278       if (image->storage_class == PseudoClass)
8279           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8280           "    storage_class=PseudoClass");
8281     }
8282
8283   if (image->storage_class == PseudoClass &&
8284      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8285      mng_info->write_png48 || mng_info->write_png64 ||
8286      (mng_info->write_png_colortype != 1 &&
8287      mng_info->write_png_colortype != 5)))
8288     {
8289       (void) SyncImage(image,exception);
8290       image->storage_class = DirectClass;
8291     }
8292
8293   if (ping_preserve_colormap == MagickFalse)
8294     {
8295       if (image->storage_class != PseudoClass && image->colormap != NULL)
8296         {
8297           /* Free the bogus colormap; it can cause trouble later */
8298            if (logging != MagickFalse)
8299               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8300               "    Freeing bogus colormap");
8301            (void) RelinquishMagickMemory(image->colormap);
8302            image->colormap=NULL;
8303         }
8304     }
8305
8306   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8307     (void) TransformImageColorspace(image,sRGBColorspace,exception);
8308
8309   /*
8310     Sometimes we get PseudoClass images whose RGB values don't match
8311     the colors in the colormap.  This code syncs the RGB values.
8312   */
8313   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8314      (void) SyncImage(image,exception);
8315
8316 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8317   if (image->depth > 8)
8318     {
8319       if (logging != MagickFalse)
8320         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8321           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8322
8323       image->depth=8;
8324     }
8325 #endif
8326
8327   /* Respect the -depth option */
8328   if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
8329     {
8330        register Quantum
8331          *r;
8332
8333        if (image->depth > 8)
8334          {
8335 #if MAGICKCORE_QUANTUM_DEPTH > 16
8336            /* Scale to 16-bit */
8337            LBR16PacketRGBO(image->background_color);
8338
8339            for (y=0; y < (ssize_t) image->rows; y++)
8340            {
8341              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8342
8343              if (r == (Quantum *) NULL)
8344                break;
8345
8346              for (x=0; x < (ssize_t) image->columns; x++)
8347              {
8348                 LBR16PixelRGBA(r);
8349                 r+=GetPixelChannels(image);
8350              }
8351
8352              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8353                 break;
8354            }
8355
8356            if (image->storage_class == PseudoClass && image->colormap != NULL)
8357            {
8358              for (i=0; i < (ssize_t) image->colors; i++)
8359              {
8360                LBR16PacketRGBO(image->colormap[i]);
8361              }
8362            }
8363 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
8364          }
8365
8366        else if (image->depth > 4)
8367          {
8368 #if MAGICKCORE_QUANTUM_DEPTH > 8
8369            /* Scale to 8-bit */
8370            LBR08PacketRGBO(image->background_color);
8371
8372            for (y=0; y < (ssize_t) image->rows; y++)
8373            {
8374              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8375
8376              if (r == (Quantum *) NULL)
8377                break;
8378
8379              for (x=0; x < (ssize_t) image->columns; x++)
8380              {
8381                 LBR08PixelRGBA(r);
8382                 r+=GetPixelChannels(image);
8383              }
8384
8385              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8386                 break;
8387            }
8388
8389            if (image->storage_class == PseudoClass && image->colormap != NULL)
8390            {
8391              for (i=0; i < (ssize_t) image->colors; i++)
8392              {
8393                LBR08PacketRGBO(image->colormap[i]);
8394              }
8395            }
8396 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
8397          }
8398        else
8399          if (image->depth > 2)
8400          {
8401            /* Scale to 4-bit */
8402            LBR04PacketRGBO(image->background_color);
8403
8404            for (y=0; y < (ssize_t) image->rows; y++)
8405            {
8406              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8407
8408              if (r == (Quantum *) NULL)
8409                break;
8410
8411              for (x=0; x < (ssize_t) image->columns; x++)
8412              {
8413                 LBR04PixelRGBA(r);
8414                 r+=GetPixelChannels(image);
8415              }
8416
8417              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8418                 break;
8419            }
8420
8421            if (image->storage_class == PseudoClass && image->colormap != NULL)
8422            {
8423              for (i=0; i < (ssize_t) image->colors; i++)
8424              {
8425                LBR04PacketRGBO(image->colormap[i]);
8426              }
8427            }
8428          }
8429
8430        else if (image->depth > 1)
8431          {
8432            /* Scale to 2-bit */
8433            LBR02PacketRGBO(image->background_color);
8434
8435            for (y=0; y < (ssize_t) image->rows; y++)
8436            {
8437              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8438
8439              if (r == (Quantum *) NULL)
8440                break;
8441
8442              for (x=0; x < (ssize_t) image->columns; x++)
8443              {
8444                 LBR02PixelRGBA(r);
8445                 r+=GetPixelChannels(image);
8446              }
8447
8448              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8449                 break;
8450            }
8451
8452            if (image->storage_class == PseudoClass && image->colormap != NULL)
8453            {
8454              for (i=0; i < (ssize_t) image->colors; i++)
8455              {
8456                LBR02PacketRGBO(image->colormap[i]);
8457              }
8458            }
8459          }
8460        else
8461          {
8462            /* Scale to 1-bit */
8463            LBR01PacketRGBO(image->background_color);
8464
8465            for (y=0; y < (ssize_t) image->rows; y++)
8466            {
8467              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8468
8469              if (r == (Quantum *) NULL)
8470                break;
8471
8472              for (x=0; x < (ssize_t) image->columns; x++)
8473              {
8474                 LBR01PixelRGBA(r);
8475                 r+=GetPixelChannels(image);
8476              }
8477
8478              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8479                 break;
8480            }
8481
8482            if (image->storage_class == PseudoClass && image->colormap != NULL)
8483            {
8484              for (i=0; i < (ssize_t) image->colors; i++)
8485              {
8486                LBR01PacketRGBO(image->colormap[i]);
8487              }
8488            }
8489          }
8490     }
8491
8492   /* To do: set to next higher multiple of 8 */
8493   if (image->depth < 8)
8494      image->depth=8;
8495
8496 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8497   /* PNG does not handle depths greater than 16 so reduce it even
8498    * if lossy
8499    */
8500   if (image->depth > 8)
8501       image->depth=16;
8502 #endif
8503
8504 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8505   if (image->depth > 8)
8506     {
8507       /* To do: fill low byte properly */
8508       image->depth=16;
8509     }
8510
8511   if (image->depth == 16 && mng_info->write_png_depth != 16)
8512     if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8513       image->depth = 8;
8514 #endif
8515
8516   if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
8517      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8518      mng_info->write_png_colortype < 4 &&
8519      image->alpha_trait != BlendPixelTrait)))
8520   {
8521      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8522       * are not going to need the result.
8523       */
8524      image_colors = (int) image->colors;
8525      number_opaque = (int) image->colors;
8526      if (mng_info->write_png_colortype == 1 ||
8527         mng_info->write_png_colortype == 5)
8528        ping_have_color=MagickFalse;
8529      else
8530        ping_have_color=MagickTrue;
8531      ping_have_non_bw=MagickFalse;
8532
8533      if (image->alpha_trait == BlendPixelTrait)
8534        {
8535          number_transparent = 2;
8536          number_semitransparent = 1;
8537        }
8538
8539      else
8540        {
8541          number_transparent = 0;
8542          number_semitransparent = 0;
8543        }
8544   }
8545
8546   else
8547   {
8548   /* BUILD_PALETTE
8549    *
8550    * Normally we run this just once, but in the case of writing PNG8
8551    * we reduce the transparency to binary and run again, then if there
8552    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8553    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8554    * palette.  Then (To do) we take care of a final reduction that is only
8555    * needed if there are still 256 colors present and one of them has both
8556    * transparent and opaque instances.
8557    */
8558
8559   tried_332 = MagickFalse;
8560   tried_333 = MagickFalse;
8561   tried_444 = MagickFalse;
8562
8563   for (j=0; j<6; j++)
8564   {
8565     /*
8566      * Sometimes we get DirectClass images that have 256 colors or fewer.
8567      * This code will build a colormap.
8568      *
8569      * Also, sometimes we get PseudoClass images with an out-of-date
8570      * colormap.  This code will replace the colormap with a new one.
8571      * Sometimes we get PseudoClass images that have more than 256 colors.
8572      * This code will delete the colormap and change the image to
8573      * DirectClass.
8574      *
8575      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8576      * even though it sometimes contains left-over non-opaque values.
8577      *
8578      * Also we gather some information (number of opaque, transparent,
8579      * and semitransparent pixels, and whether the image has any non-gray
8580      * pixels or only black-and-white pixels) that we might need later.
8581      *
8582      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8583      * we need to check for bogus non-opaque values, at least.
8584      */
8585
8586    int
8587      n;
8588
8589    PixelInfo
8590      opaque[260],
8591      semitransparent[260],
8592      transparent[260];
8593
8594    register const Quantum
8595      *s;
8596
8597    register Quantum
8598      *q,
8599      *r;
8600
8601    if (logging != MagickFalse)
8602      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8603          "    Enter BUILD_PALETTE:");
8604
8605    if (logging != MagickFalse)
8606      {
8607        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8608              "      image->columns=%.20g",(double) image->columns);
8609        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8610              "      image->rows=%.20g",(double) image->rows);
8611        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8612              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
8613        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8614              "      image->depth=%.20g",(double) image->depth);
8615
8616        if (image->storage_class == PseudoClass && image->colormap != NULL)
8617        {
8618          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8619              "      Original colormap:");
8620          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8621              "        i    (red,green,blue,alpha)");
8622
8623          for (i=0; i < 256; i++)
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          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8635          {
8636            if (i > 255)
8637              {
8638                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8639                    "        %d    (%d,%d,%d,%d)",
8640                     (int) i,
8641                     (int) image->colormap[i].red,
8642                     (int) image->colormap[i].green,
8643                     (int) image->colormap[i].blue,
8644                     (int) image->colormap[i].alpha);
8645              }
8646          }
8647        }
8648
8649        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8650            "      image->colors=%d",(int) image->colors);
8651
8652        if (image->colors == 0)
8653          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8654              "        (zero means unknown)");
8655
8656        if (ping_preserve_colormap == MagickFalse)
8657          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8658               "      Regenerate the colormap");
8659      }
8660
8661      image_colors=0;
8662      number_opaque = 0;
8663      number_semitransparent = 0;
8664      number_transparent = 0;
8665
8666      for (y=0; y < (ssize_t) image->rows; y++)
8667      {
8668        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8669
8670        if (q == (Quantum *) NULL)
8671          break;
8672
8673        for (x=0; x < (ssize_t) image->columns; x++)
8674        {
8675            if (image->alpha_trait != BlendPixelTrait ||
8676               GetPixelAlpha(image,q) == OpaqueAlpha)
8677              {
8678                if (number_opaque < 259)
8679                  {
8680                    if (number_opaque == 0)
8681                      {
8682                        GetPixelInfoPixel(image, q, opaque);
8683                        opaque[0].alpha=OpaqueAlpha;
8684                        number_opaque=1;
8685                      }
8686
8687                    for (i=0; i< (ssize_t) number_opaque; i++)
8688                      {
8689                        if (IsPixelEquivalent(image,q, opaque+i))
8690                          break;
8691                      }
8692
8693                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
8694                      {
8695                        number_opaque++;
8696                        GetPixelInfoPixel(image, q, opaque+i);
8697                        opaque[i].alpha=OpaqueAlpha;
8698                      }
8699                  }
8700              }
8701            else if (GetPixelAlpha(image,q) == TransparentAlpha)
8702              {
8703                if (number_transparent < 259)
8704                  {
8705                    if (number_transparent == 0)
8706                      {
8707                        GetPixelInfoPixel(image, q, transparent);
8708                        ping_trans_color.red=(unsigned short)
8709                          GetPixelRed(image,q);
8710                        ping_trans_color.green=(unsigned short)
8711                          GetPixelGreen(image,q);
8712                        ping_trans_color.blue=(unsigned short)
8713                          GetPixelBlue(image,q);
8714                        ping_trans_color.gray=(unsigned short)
8715                          GetPixelGray(image,q);
8716                        number_transparent = 1;
8717                      }
8718
8719                    for (i=0; i< (ssize_t) number_transparent; i++)
8720                      {
8721                        if (IsPixelEquivalent(image,q, transparent+i))
8722                          break;
8723                      }
8724
8725                    if (i ==  (ssize_t) number_transparent &&
8726                        number_transparent < 259)
8727                      {
8728                        number_transparent++;
8729                        GetPixelInfoPixel(image,q,transparent+i);
8730                      }
8731                  }
8732              }
8733            else
8734              {
8735                if (number_semitransparent < 259)
8736                  {
8737                    if (number_semitransparent == 0)
8738                      {
8739                        GetPixelInfoPixel(image,q,semitransparent);
8740                        number_semitransparent = 1;
8741                      }
8742
8743                    for (i=0; i< (ssize_t) number_semitransparent; i++)
8744                      {
8745                        if (IsPixelEquivalent(image,q, semitransparent+i)
8746                            && GetPixelAlpha(image,q) ==
8747                            semitransparent[i].alpha)
8748                          break;
8749                      }
8750
8751                    if (i ==  (ssize_t) number_semitransparent &&
8752                        number_semitransparent < 259)
8753                      {
8754                        number_semitransparent++;
8755                        GetPixelInfoPixel(image, q, semitransparent+i);
8756                      }
8757                  }
8758              }
8759            q+=GetPixelChannels(image);
8760         }
8761      }
8762
8763      if (mng_info->write_png8 == MagickFalse &&
8764          ping_exclude_bKGD == MagickFalse)
8765        {
8766          /* Add the background color to the palette, if it
8767           * isn't already there.
8768           */
8769           if (logging != MagickFalse)
8770             {
8771               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8772                   "      Check colormap for background (%d,%d,%d)",
8773                   (int) image->background_color.red,
8774                   (int) image->background_color.green,
8775                   (int) image->background_color.blue);
8776             }
8777           for (i=0; i<number_opaque; i++)
8778           {
8779              if (opaque[i].red == image->background_color.red &&
8780                  opaque[i].green == image->background_color.green &&
8781                  opaque[i].blue == image->background_color.blue)
8782                break;
8783           }
8784           if (number_opaque < 259 && i == number_opaque)
8785             {
8786                opaque[i] = image->background_color;
8787                ping_background.index = i;
8788                number_opaque++;
8789                if (logging != MagickFalse)
8790                  {
8791                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8792                        "      background_color index is %d",(int) i);
8793                  }
8794
8795             }
8796           else if (logging != MagickFalse)
8797               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8798                   "      No room in the colormap to add background color");
8799        }
8800
8801      image_colors=number_opaque+number_transparent+number_semitransparent;
8802
8803      if (logging != MagickFalse)
8804        {
8805          if (image_colors > 256)
8806             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8807                   "      image has more than 256 colors");
8808
8809          else
8810             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8811                   "      image has %d colors",image_colors);
8812        }
8813
8814      if (ping_preserve_colormap != MagickFalse)
8815        break;
8816
8817      if (mng_info->write_png_colortype != 7) /* We won't need this info */
8818        {
8819          ping_have_color=MagickFalse;
8820          ping_have_non_bw=MagickFalse;
8821
8822          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8823          {
8824            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8825               "incompatible colorspace");
8826            ping_have_color=MagickTrue;
8827            ping_have_non_bw=MagickTrue;
8828          }
8829
8830          if(image_colors > 256)
8831            {
8832              for (y=0; y < (ssize_t) image->rows; y++)
8833              {
8834                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8835
8836                if (q == (Quantum *) NULL)
8837                  break;
8838
8839                s=q;
8840                for (x=0; x < (ssize_t) image->columns; x++)
8841                {
8842                  if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8843                      GetPixelRed(image,s) != GetPixelBlue(image,s))
8844                    {
8845                       ping_have_color=MagickTrue;
8846                       ping_have_non_bw=MagickTrue;
8847                       break;
8848                    }
8849                  s+=GetPixelChannels(image);
8850                }
8851
8852                if (ping_have_color != MagickFalse)
8853                  break;
8854
8855                /* Worst case is black-and-white; we are looking at every
8856                 * pixel twice.
8857                 */
8858
8859                if (ping_have_non_bw == MagickFalse)
8860                  {
8861                    s=q;
8862                    for (x=0; x < (ssize_t) image->columns; x++)
8863                    {
8864                      if (GetPixelRed(image,s) != 0 &&
8865                          GetPixelRed(image,s) != QuantumRange)
8866                        {
8867                          ping_have_non_bw=MagickTrue;
8868                          break;
8869                        }
8870                      s+=GetPixelChannels(image);
8871                    }
8872                }
8873              }
8874            }
8875        }
8876
8877      if (image_colors < 257)
8878        {
8879          PixelInfo
8880            colormap[260];
8881
8882          /*
8883           * Initialize image colormap.
8884           */
8885
8886          if (logging != MagickFalse)
8887             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8888                   "      Sort the new colormap");
8889
8890         /* Sort palette, transparent first */;
8891
8892          n = 0;
8893
8894          for (i=0; i<number_transparent; i++)
8895             colormap[n++] = transparent[i];
8896
8897          for (i=0; i<number_semitransparent; i++)
8898             colormap[n++] = semitransparent[i];
8899
8900          for (i=0; i<number_opaque; i++)
8901             colormap[n++] = opaque[i];
8902
8903          ping_background.index +=
8904            (number_transparent + number_semitransparent);
8905
8906          /* image_colors < 257; search the colormap instead of the pixels
8907           * to get ping_have_color and ping_have_non_bw
8908           */
8909          for (i=0; i<n; i++)
8910          {
8911            if (ping_have_color == MagickFalse)
8912              {
8913                 if (colormap[i].red != colormap[i].green ||
8914                     colormap[i].red != colormap[i].blue)
8915                   {
8916                      ping_have_color=MagickTrue;
8917                      ping_have_non_bw=MagickTrue;
8918                      break;
8919                   }
8920               }
8921
8922            if (ping_have_non_bw == MagickFalse)
8923              {
8924                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8925                    ping_have_non_bw=MagickTrue;
8926              }
8927           }
8928
8929         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8930             (number_transparent == 0 && number_semitransparent == 0)) &&
8931             (((mng_info->write_png_colortype-1) ==
8932             PNG_COLOR_TYPE_PALETTE) ||
8933             (mng_info->write_png_colortype == 0)))
8934           {
8935             if (logging != MagickFalse)
8936               {
8937                 if (n !=  (ssize_t) image_colors)
8938                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8939                    "   image_colors (%d) and n (%d)  don't match",
8940                    image_colors, n);
8941
8942                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8943                    "      AcquireImageColormap");
8944               }
8945
8946             image->colors = image_colors;
8947
8948             if (AcquireImageColormap(image,image_colors,exception) ==
8949                 MagickFalse)
8950                ThrowWriterException(ResourceLimitError,
8951                    "MemoryAllocationFailed");
8952
8953             for (i=0; i< (ssize_t) image_colors; i++)
8954                image->colormap[i] = colormap[i];
8955
8956             if (logging != MagickFalse)
8957               {
8958                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8959                       "      image->colors=%d (%d)",
8960                       (int) image->colors, image_colors);
8961
8962                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8963                       "      Update the pixel indexes");
8964               }
8965
8966             /* Sync the pixel indices with the new colormap */
8967
8968             for (y=0; y < (ssize_t) image->rows; y++)
8969             {
8970               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8971
8972               if (q == (Quantum *) NULL)
8973                 break;
8974
8975               for (x=0; x < (ssize_t) image->columns; x++)
8976               {
8977                 for (i=0; i< (ssize_t) image_colors; i++)
8978                 {
8979                   if ((image->alpha_trait != BlendPixelTrait ||
8980                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8981                       image->colormap[i].red == GetPixelRed(image,q) &&
8982                       image->colormap[i].green == GetPixelGreen(image,q) &&
8983                       image->colormap[i].blue == GetPixelBlue(image,q))
8984                   {
8985                     SetPixelIndex(image,i,q);
8986                     break;
8987                   }
8988                 }
8989                 q+=GetPixelChannels(image);
8990               }
8991
8992               if (SyncAuthenticPixels(image,exception) == MagickFalse)
8993                  break;
8994             }
8995           }
8996        }
8997
8998      if (logging != MagickFalse)
8999        {
9000          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9001             "      image->colors=%d", (int) image->colors);
9002
9003          if (image->colormap != NULL)
9004            {
9005              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9006                  "       i     (red,green,blue,alpha)");
9007
9008              for (i=0; i < (ssize_t) image->colors; i++)
9009              {
9010                if (i < 300 || i >= (ssize_t) image->colors - 10)
9011                  {
9012                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9013                        "       %d     (%d,%d,%d,%d)",
9014                         (int) i,
9015                         (int) image->colormap[i].red,
9016                         (int) image->colormap[i].green,
9017                         (int) image->colormap[i].blue,
9018                         (int) image->colormap[i].alpha);
9019                  }
9020              }
9021            }
9022
9023            if (number_transparent < 257)
9024              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9025                    "      number_transparent     = %d",
9026                    number_transparent);
9027            else
9028
9029              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9030                    "      number_transparent     > 256");
9031
9032            if (number_opaque < 257)
9033              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9034                    "      number_opaque          = %d",
9035                    number_opaque);
9036
9037            else
9038              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9039                    "      number_opaque          > 256");
9040
9041            if (number_semitransparent < 257)
9042              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9043                    "      number_semitransparent = %d",
9044                    number_semitransparent);
9045
9046            else
9047              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9048                    "      number_semitransparent > 256");
9049
9050            if (ping_have_non_bw == MagickFalse)
9051               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9052                     "      All pixels and the background are black or white");
9053
9054            else if (ping_have_color == MagickFalse)
9055               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9056                     "      All pixels and the background are gray");
9057
9058            else
9059               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9060                     "      At least one pixel or the background is non-gray");
9061
9062            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9063                "    Exit BUILD_PALETTE:");
9064        }
9065
9066    if (mng_info->write_png8 == MagickFalse)
9067       break;
9068
9069    /* Make any reductions necessary for the PNG8 format */
9070     if (image_colors <= 256 &&
9071         image_colors != 0 && image->colormap != NULL &&
9072         number_semitransparent == 0 &&
9073         number_transparent <= 1)
9074       break;
9075
9076     /* PNG8 can't have semitransparent colors so we threshold the
9077      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9078      * transparent color so if more than one is transparent we merge
9079      * them into image->background_color.
9080      */
9081     if (number_semitransparent != 0 || number_transparent > 1)
9082       {
9083         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9084             "    Thresholding the alpha channel to binary");
9085
9086         for (y=0; y < (ssize_t) image->rows; y++)
9087         {
9088           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9089
9090           if (r == (Quantum *) NULL)
9091             break;
9092
9093           for (x=0; x < (ssize_t) image->columns; x++)
9094           {
9095               if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9096                 {
9097                   SetPixelInfoPixel(image,&image->background_color,r);
9098                   SetPixelAlpha(image,TransparentAlpha,r);
9099                 }
9100               else
9101                   SetPixelAlpha(image,OpaqueAlpha,r);
9102               r+=GetPixelChannels(image);
9103           }
9104
9105           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9106              break;
9107
9108           if (image_colors != 0 && image_colors <= 256 &&
9109              image->colormap != NULL)
9110             for (i=0; i<image_colors; i++)
9111                 image->colormap[i].alpha =
9112                     (image->colormap[i].alpha > TransparentAlpha/2 ?
9113                     TransparentAlpha : OpaqueAlpha);
9114         }
9115       continue;
9116     }
9117
9118     /* PNG8 can't have more than 256 colors so we quantize the pixels and
9119      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
9120      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9121      * colors or less.
9122      */
9123     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9124       {
9125         if (logging != MagickFalse)
9126            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9127                "    Quantizing the background color to 4-4-4");
9128
9129         tried_444 = MagickTrue;
9130
9131         LBR04PacketRGB(image->background_color);
9132
9133         if (logging != MagickFalse)
9134           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9135               "    Quantizing the pixel colors to 4-4-4");
9136
9137         if (image->colormap == NULL)
9138         {
9139           for (y=0; y < (ssize_t) image->rows; y++)
9140           {
9141             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9142
9143             if (r == (Quantum *) NULL)
9144               break;
9145
9146             for (x=0; x < (ssize_t) image->columns; x++)
9147             {
9148               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9149                   LBR04PixelRGB(r);
9150               r+=GetPixelChannels(image);
9151             }
9152
9153             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9154                break;
9155           }
9156         }
9157
9158         else /* Should not reach this; colormap already exists and
9159                 must be <= 256 */
9160         {
9161           if (logging != MagickFalse)
9162               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9163               "    Quantizing the colormap to 4-4-4");
9164
9165           for (i=0; i<image_colors; i++)
9166           {
9167             LBR04PacketRGB(image->colormap[i]);
9168           }
9169         }
9170         continue;
9171       }
9172
9173     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9174       {
9175         if (logging != MagickFalse)
9176            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9177                "    Quantizing the background color to 3-3-3");
9178
9179         tried_333 = MagickTrue;
9180
9181         LBR03PacketRGB(image->background_color);
9182
9183         if (logging != MagickFalse)
9184           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9185               "    Quantizing the pixel colors to 3-3-3-1");
9186
9187         if (image->colormap == NULL)
9188         {
9189           for (y=0; y < (ssize_t) image->rows; y++)
9190           {
9191             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9192
9193             if (r == (Quantum *) NULL)
9194               break;
9195
9196             for (x=0; x < (ssize_t) image->columns; x++)
9197             {
9198               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9199                   LBR03RGB(r);
9200               r+=GetPixelChannels(image);
9201             }
9202
9203             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9204                break;
9205           }
9206         }
9207
9208         else /* Should not reach this; colormap already exists and
9209                 must be <= 256 */
9210         {
9211           if (logging != MagickFalse)
9212               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9213               "    Quantizing the colormap to 3-3-3-1");
9214           for (i=0; i<image_colors; i++)
9215           {
9216               LBR03PacketRGB(image->colormap[i]);
9217           }
9218         }
9219         continue;
9220       }
9221
9222     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9223       {
9224         if (logging != MagickFalse)
9225            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9226                "    Quantizing the background color to 3-3-2");
9227
9228         tried_332 = MagickTrue;
9229
9230         /* Red and green were already done so we only quantize the blue
9231          * channel
9232          */
9233
9234         LBR02PacketBlue(image->background_color);
9235
9236         if (logging != MagickFalse)
9237           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9238               "    Quantizing the pixel colors to 3-3-2-1");
9239
9240         if (image->colormap == NULL)
9241         {
9242           for (y=0; y < (ssize_t) image->rows; y++)
9243           {
9244             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9245
9246             if (r == (Quantum *) NULL)
9247               break;
9248
9249             for (x=0; x < (ssize_t) image->columns; x++)
9250             {
9251               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9252                   LBR02PixelBlue(r);
9253               r+=GetPixelChannels(image);
9254             }
9255
9256             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9257                break;
9258           }
9259         }
9260
9261         else /* Should not reach this; colormap already exists and
9262                 must be <= 256 */
9263         {
9264           if (logging != MagickFalse)
9265               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9266               "    Quantizing the colormap to 3-3-2-1");
9267           for (i=0; i<image_colors; i++)
9268           {
9269               LBR02PacketBlue(image->colormap[i]);
9270           }
9271       }
9272       continue;
9273     }
9274
9275     if (image_colors == 0 || image_colors > 256)
9276     {
9277       /* Take care of special case with 256 opaque colors + 1 transparent
9278        * color.  We don't need to quantize to 2-3-2-1; we only need to
9279        * eliminate one color, so we'll merge the two darkest red
9280        * colors (0x49, 0, 0) -> (0x24, 0, 0).
9281        */
9282       if (logging != MagickFalse)
9283         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9284             "    Merging two dark red background colors to 3-3-2-1");
9285
9286       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9287           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9288           ScaleQuantumToChar(image->background_color.blue) == 0x00)
9289       {
9290          image->background_color.red=ScaleCharToQuantum(0x24);
9291       }
9292
9293       if (logging != MagickFalse)
9294         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9295             "    Merging two dark red pixel colors to 3-3-2-1");
9296
9297       if (image->colormap == NULL)
9298       {
9299         for (y=0; y < (ssize_t) image->rows; y++)
9300         {
9301           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9302
9303           if (r == (Quantum *) NULL)
9304             break;
9305
9306           for (x=0; x < (ssize_t) image->columns; x++)
9307           {
9308             if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9309                 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9310                 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9311                 GetPixelAlpha(image,r) == OpaqueAlpha)
9312               {
9313                 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9314               }
9315             r+=GetPixelChannels(image);
9316           }
9317
9318           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9319              break;
9320
9321         }
9322       }
9323
9324       else
9325       {
9326          for (i=0; i<image_colors; i++)
9327          {
9328             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9329                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9330                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9331             {
9332                image->colormap[i].red=ScaleCharToQuantum(0x24);
9333             }
9334          }
9335       }
9336     }
9337   }
9338   }
9339   /* END OF BUILD_PALETTE */
9340
9341   /* If we are excluding the tRNS chunk and there is transparency,
9342    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9343    * PNG.
9344    */
9345   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9346      (number_transparent != 0 || number_semitransparent != 0))
9347     {
9348       unsigned int colortype=mng_info->write_png_colortype;
9349
9350       if (ping_have_color == MagickFalse)
9351         mng_info->write_png_colortype = 5;
9352
9353       else
9354         mng_info->write_png_colortype = 7;
9355
9356       if (colortype != 0 &&
9357          mng_info->write_png_colortype != colortype)
9358         ping_need_colortype_warning=MagickTrue;
9359
9360     }
9361
9362   /* See if cheap transparency is possible.  It is only possible
9363    * when there is a single transparent color, no semitransparent
9364    * color, and no opaque color that has the same RGB components
9365    * as the transparent color.  We only need this information if
9366    * we are writing a PNG with colortype 0 or 2, and we have not
9367    * excluded the tRNS chunk.
9368    */
9369   if (number_transparent == 1 &&
9370       mng_info->write_png_colortype < 4)
9371     {
9372        ping_have_cheap_transparency = MagickTrue;
9373
9374        if (number_semitransparent != 0)
9375          ping_have_cheap_transparency = MagickFalse;
9376
9377        else if (image_colors == 0 || image_colors > 256 ||
9378            image->colormap == NULL)
9379          {
9380            register const Quantum
9381              *q;
9382
9383            for (y=0; y < (ssize_t) image->rows; y++)
9384            {
9385              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9386
9387              if (q == (Quantum *) NULL)
9388                break;
9389
9390              for (x=0; x < (ssize_t) image->columns; x++)
9391              {
9392                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9393                      (unsigned short) GetPixelRed(image,q) ==
9394                                      ping_trans_color.red &&
9395                      (unsigned short) GetPixelGreen(image,q) ==
9396                                      ping_trans_color.green &&
9397                      (unsigned short) GetPixelBlue(image,q) ==
9398                                      ping_trans_color.blue)
9399                    {
9400                      ping_have_cheap_transparency = MagickFalse;
9401                      break;
9402                    }
9403
9404                  q+=GetPixelChannels(image);
9405              }
9406
9407              if (ping_have_cheap_transparency == MagickFalse)
9408                 break;
9409            }
9410          }
9411        else
9412          {
9413             /* Assuming that image->colormap[0] is the one transparent color
9414              * and that all others are opaque.
9415              */
9416             if (image_colors > 1)
9417               for (i=1; i<image_colors; i++)
9418                 if (image->colormap[i].red == image->colormap[0].red &&
9419                     image->colormap[i].green == image->colormap[0].green &&
9420                     image->colormap[i].blue == image->colormap[0].blue)
9421                   {
9422                      ping_have_cheap_transparency = MagickFalse;
9423                      break;
9424                   }
9425          }
9426
9427        if (logging != MagickFalse)
9428          {
9429            if (ping_have_cheap_transparency == MagickFalse)
9430              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9431                  "   Cheap transparency is not possible.");
9432
9433            else
9434              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9435                  "   Cheap transparency is possible.");
9436          }
9437      }
9438   else
9439     ping_have_cheap_transparency = MagickFalse;
9440
9441   image_depth=image->depth;
9442
9443   quantum_info = (QuantumInfo *) NULL;
9444   number_colors=0;
9445   image_colors=(int) image->colors;
9446   image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
9447
9448   mng_info->IsPalette=image->storage_class == PseudoClass &&
9449     image_colors <= 256 && image->colormap != NULL;
9450
9451   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9452      (image->colors == 0 || image->colormap == NULL))
9453     {
9454       image_info=DestroyImageInfo(image_info);
9455       image=DestroyImage(image);
9456       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9457           "Cannot write PNG8 or color-type 3; colormap is NULL",
9458           "`%s'",IMimage->filename);
9459       return(MagickFalse);
9460     }
9461
9462   /*
9463     Allocate the PNG structures
9464   */
9465 #ifdef PNG_USER_MEM_SUPPORTED
9466  error_info.image=image;
9467  error_info.exception=exception;
9468   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9469     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9470     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9471
9472 #else
9473   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9474     MagickPNGErrorHandler,MagickPNGWarningHandler);
9475
9476 #endif
9477   if (ping == (png_struct *) NULL)
9478     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9479
9480   ping_info=png_create_info_struct(ping);
9481
9482   if (ping_info == (png_info *) NULL)
9483     {
9484       png_destroy_write_struct(&ping,(png_info **) NULL);
9485       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9486     }
9487
9488   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9489   pixel_info=(MemoryInfo *) NULL;
9490
9491   if (setjmp(png_jmpbuf(ping)))
9492     {
9493       /*
9494         PNG write failed.
9495       */
9496 #ifdef PNG_DEBUG
9497      if (image_info->verbose)
9498         (void) printf("PNG write has failed.\n");
9499 #endif
9500       png_destroy_write_struct(&ping,&ping_info);
9501 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9502       UnlockSemaphoreInfo(ping_semaphore);
9503 #endif
9504
9505       if (pixel_info != (MemoryInfo *) NULL)
9506         pixel_info=RelinquishVirtualMemory(pixel_info);
9507
9508       if (quantum_info != (QuantumInfo *) NULL)
9509         quantum_info=DestroyQuantumInfo(quantum_info);
9510
9511       if (ping_have_blob != MagickFalse)
9512           (void) CloseBlob(image);
9513       image_info=DestroyImageInfo(image_info);
9514       image=DestroyImage(image);
9515       return(MagickFalse);
9516     }
9517
9518   /* {  For navigation to end of SETJMP-protected block.  Within this
9519    *    block, use png_error() instead of Throwing an Exception, to ensure
9520    *    that libpng is able to clean up, and that the semaphore is unlocked.
9521    */
9522
9523 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9524   LockSemaphoreInfo(ping_semaphore);
9525 #endif
9526
9527 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9528   /* Allow benign errors */
9529   png_set_benign_errors(ping, 1);
9530 #endif
9531
9532   /*
9533     Prepare PNG for writing.
9534   */
9535
9536 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9537   if (mng_info->write_mng)
9538   {
9539      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9540 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9541      /* Disable new libpng-1.5.10 feature when writing a MNG because
9542       * zero-length PLTE is OK
9543       */
9544      png_set_check_for_invalid_index (ping, 0);
9545 # endif
9546   }
9547
9548 #else
9549 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9550   if (mng_info->write_mng)
9551      png_permit_empty_plte(ping,MagickTrue);
9552
9553 # endif
9554 #endif
9555
9556   x=0;
9557
9558   ping_width=(png_uint_32) image->columns;
9559   ping_height=(png_uint_32) image->rows;
9560
9561   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9562      image_depth=8;
9563
9564   if (mng_info->write_png48 || mng_info->write_png64)
9565      image_depth=16;
9566
9567   if (mng_info->write_png_depth != 0)
9568      image_depth=mng_info->write_png_depth;
9569
9570   /* Adjust requested depth to next higher valid depth if necessary */
9571   if (image_depth > 8)
9572      image_depth=16;
9573
9574   if ((image_depth > 4) && (image_depth < 8))
9575      image_depth=8;
9576
9577   if (image_depth == 3)
9578      image_depth=4;
9579
9580   if (logging != MagickFalse)
9581     {
9582      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9583         "    width=%.20g",(double) ping_width);
9584      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9585         "    height=%.20g",(double) ping_height);
9586      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9587         "    image_matte=%.20g",(double) image->alpha_trait);
9588      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9589         "    image->depth=%.20g",(double) image->depth);
9590      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9591         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
9592     }
9593
9594   save_image_depth=image_depth;
9595   ping_bit_depth=(png_byte) save_image_depth;
9596
9597
9598 #if defined(PNG_pHYs_SUPPORTED)
9599   if (ping_exclude_pHYs == MagickFalse)
9600   {
9601   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9602       (!mng_info->write_mng || !mng_info->equal_physs))
9603     {
9604       if (logging != MagickFalse)
9605         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9606             "    Setting up pHYs chunk");
9607
9608       if (image->units == PixelsPerInchResolution)
9609         {
9610           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9611           ping_pHYs_x_resolution=
9612              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9613           ping_pHYs_y_resolution=
9614              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9615         }
9616
9617       else if (image->units == PixelsPerCentimeterResolution)
9618         {
9619           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9620           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9621           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9622         }
9623
9624       else
9625         {
9626           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9627           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9628           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9629         }
9630
9631       if (logging != MagickFalse)
9632         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9633           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9634           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9635           (int) ping_pHYs_unit_type);
9636        ping_have_pHYs = MagickTrue;
9637     }
9638   }
9639 #endif
9640
9641   if (ping_exclude_bKGD == MagickFalse)
9642   {
9643   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9644     {
9645        unsigned int
9646          mask;
9647
9648        mask=0xffff;
9649        if (ping_bit_depth == 8)
9650           mask=0x00ff;
9651
9652        if (ping_bit_depth == 4)
9653           mask=0x000f;
9654
9655        if (ping_bit_depth == 2)
9656           mask=0x0003;
9657
9658        if (ping_bit_depth == 1)
9659           mask=0x0001;
9660
9661        ping_background.red=(png_uint_16)
9662          (ScaleQuantumToShort(image->background_color.red) & mask);
9663
9664        ping_background.green=(png_uint_16)
9665          (ScaleQuantumToShort(image->background_color.green) & mask);
9666
9667        ping_background.blue=(png_uint_16)
9668          (ScaleQuantumToShort(image->background_color.blue) & mask);
9669
9670        ping_background.gray=(png_uint_16) ping_background.green;
9671     }
9672
9673   if (logging != MagickFalse)
9674     {
9675       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9676           "    Setting up bKGD chunk (1)");
9677       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9678           "      background_color index is %d",
9679           (int) ping_background.index);
9680
9681       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9682           "    ping_bit_depth=%d",ping_bit_depth);
9683     }
9684
9685   ping_have_bKGD = MagickTrue;
9686   }
9687
9688   /*
9689     Select the color type.
9690   */
9691   matte=image_matte;
9692   old_bit_depth=0;
9693
9694   if (mng_info->IsPalette && mng_info->write_png8)
9695     {
9696       /* To do: make this a function cause it's used twice, except
9697          for reducing the sample depth from 8. */
9698
9699       number_colors=image_colors;
9700
9701       ping_have_tRNS=MagickFalse;
9702
9703       /*
9704         Set image palette.
9705       */
9706       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9707
9708       if (logging != MagickFalse)
9709         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9710             "  Setting up PLTE chunk with %d colors (%d)",
9711             number_colors, image_colors);
9712
9713       for (i=0; i < (ssize_t) number_colors; i++)
9714       {
9715         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9716         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9717         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9718         if (logging != MagickFalse)
9719           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9720 #if MAGICKCORE_QUANTUM_DEPTH == 8
9721             "    %3ld (%3d,%3d,%3d)",
9722 #else
9723             "    %5ld (%5d,%5d,%5d)",
9724 #endif
9725             (long) i,palette[i].red,palette[i].green,palette[i].blue);
9726
9727       }
9728
9729       ping_have_PLTE=MagickTrue;
9730       image_depth=ping_bit_depth;
9731       ping_num_trans=0;
9732
9733       if (matte != MagickFalse)
9734       {
9735           /*
9736             Identify which colormap entry is transparent.
9737           */
9738           assert(number_colors <= 256);
9739           assert(image->colormap != NULL);
9740
9741           for (i=0; i < (ssize_t) number_transparent; i++)
9742              ping_trans_alpha[i]=0;
9743
9744
9745           ping_num_trans=(unsigned short) (number_transparent +
9746              number_semitransparent);
9747
9748           if (ping_num_trans == 0)
9749              ping_have_tRNS=MagickFalse;
9750
9751           else
9752              ping_have_tRNS=MagickTrue;
9753       }
9754
9755       if (ping_exclude_bKGD == MagickFalse)
9756       {
9757        /*
9758         * Identify which colormap entry is the background color.
9759         */
9760
9761         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9762           if (IsPNGColorEqual(ping_background,image->colormap[i]))
9763             break;
9764
9765         ping_background.index=(png_byte) i;
9766
9767         if (logging != MagickFalse)
9768           {
9769             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9770                  "      background_color index is %d",
9771                  (int) ping_background.index);
9772           }
9773       }
9774     } /* end of write_png8 */
9775
9776   else if (mng_info->write_png_colortype == 1)
9777     {
9778       image_matte=MagickFalse;
9779       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9780     }
9781
9782   else if (mng_info->write_png24 || mng_info->write_png48 ||
9783       mng_info->write_png_colortype == 3)
9784     {
9785       image_matte=MagickFalse;
9786       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9787     }
9788
9789   else if (mng_info->write_png32 || mng_info->write_png64 ||
9790       mng_info->write_png_colortype == 7)
9791     {
9792       image_matte=MagickTrue;
9793       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9794     }
9795
9796   else /* mng_info->write_pngNN not specified */
9797     {
9798       image_depth=ping_bit_depth;
9799
9800       if (mng_info->write_png_colortype != 0)
9801         {
9802           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9803
9804           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9805               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9806             image_matte=MagickTrue;
9807
9808           else
9809             image_matte=MagickFalse;
9810
9811           if (logging != MagickFalse)
9812              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9813              "   PNG colortype %d was specified:",(int) ping_color_type);
9814         }
9815
9816       else /* write_png_colortype not specified */
9817         {
9818           if (logging != MagickFalse)
9819              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9820              "  Selecting PNG colortype:");
9821
9822           ping_color_type=(png_byte) ((matte != MagickFalse)?
9823             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9824
9825           if (image_info->type == TrueColorType)
9826             {
9827               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9828               image_matte=MagickFalse;
9829             }
9830
9831           if (image_info->type == TrueColorMatteType)
9832             {
9833               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9834               image_matte=MagickTrue;
9835             }
9836
9837           if (image_info->type == PaletteType ||
9838               image_info->type == PaletteMatteType)
9839             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9840
9841           if (mng_info->write_png_colortype == 0 &&
9842              (image_info->type == UndefinedType ||
9843              image_info->type == OptimizeType))
9844             {
9845               if (ping_have_color == MagickFalse)
9846                 {
9847                   if (image_matte == MagickFalse)
9848                     {
9849                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9850                       image_matte=MagickFalse;
9851                     }
9852
9853                   else
9854                     {
9855                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9856                       image_matte=MagickTrue;
9857                     }
9858                 }
9859               else
9860                 {
9861                   if (image_matte == MagickFalse)
9862                     {
9863                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9864                       image_matte=MagickFalse;
9865                     }
9866
9867                   else
9868                     {
9869                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9870                       image_matte=MagickTrue;
9871                     }
9872                  }
9873             }
9874
9875         }
9876
9877       if (logging != MagickFalse)
9878          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9879          "    Selected PNG colortype=%d",ping_color_type);
9880
9881       if (ping_bit_depth < 8)
9882         {
9883           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9884               ping_color_type == PNG_COLOR_TYPE_RGB ||
9885               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9886             ping_bit_depth=8;
9887         }
9888
9889       old_bit_depth=ping_bit_depth;
9890
9891       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9892         {
9893           if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
9894              ping_bit_depth=1;
9895         }
9896
9897       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9898         {
9899            size_t one = 1;
9900            ping_bit_depth=1;
9901
9902            if (image->colors == 0)
9903            {
9904               /* DO SOMETHING */
9905                 png_error(ping,"image has 0 colors");
9906            }
9907
9908            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9909              ping_bit_depth <<= 1;
9910         }
9911
9912       if (logging != MagickFalse)
9913          {
9914            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9915             "    Number of colors: %.20g",(double) image_colors);
9916
9917            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9918             "    Tentative PNG bit depth: %d",ping_bit_depth);
9919          }
9920
9921       if (ping_bit_depth < (int) mng_info->write_png_depth)
9922          ping_bit_depth = mng_info->write_png_depth;
9923     }
9924
9925   image_depth=ping_bit_depth;
9926
9927   if (logging != MagickFalse)
9928     {
9929       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9930         "    Tentative PNG color type: %s (%.20g)",
9931         PngColorTypeToString(ping_color_type),
9932         (double) ping_color_type);
9933
9934       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9935         "    image_info->type: %.20g",(double) image_info->type);
9936
9937       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9938         "    image_depth: %.20g",(double) image_depth);
9939
9940       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9941
9942         "    image->depth: %.20g",(double) image->depth);
9943
9944       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9945         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
9946     }
9947
9948   if (matte != MagickFalse)
9949     {
9950       if (mng_info->IsPalette)
9951         {
9952           if (mng_info->write_png_colortype == 0)
9953             {
9954               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9955
9956               if (ping_have_color != MagickFalse)
9957                  ping_color_type=PNG_COLOR_TYPE_RGBA;
9958             }
9959
9960           /*
9961            * Determine if there is any transparent color.
9962           */
9963           if (number_transparent + number_semitransparent == 0)
9964             {
9965               /*
9966                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
9967               */
9968
9969               image_matte=MagickFalse;
9970
9971               if (mng_info->write_png_colortype == 0)
9972                 ping_color_type&=0x03;
9973             }
9974
9975           else
9976             {
9977               unsigned int
9978                 mask;
9979
9980               mask=0xffff;
9981
9982               if (ping_bit_depth == 8)
9983                  mask=0x00ff;
9984
9985               if (ping_bit_depth == 4)
9986                  mask=0x000f;
9987
9988               if (ping_bit_depth == 2)
9989                  mask=0x0003;
9990
9991               if (ping_bit_depth == 1)
9992                  mask=0x0001;
9993
9994               ping_trans_color.red=(png_uint_16)
9995                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9996
9997               ping_trans_color.green=(png_uint_16)
9998                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9999
10000               ping_trans_color.blue=(png_uint_16)
10001                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10002
10003               ping_trans_color.gray=(png_uint_16)
10004                 (ScaleQuantumToShort(GetPixelInfoIntensity(
10005                    image->colormap)) & mask);
10006
10007               ping_trans_color.index=(png_byte) 0;
10008
10009               ping_have_tRNS=MagickTrue;
10010             }
10011
10012           if (ping_have_tRNS != MagickFalse)
10013             {
10014               /*
10015                * Determine if there is one and only one transparent color
10016                * and if so if it is fully transparent.
10017                */
10018               if (ping_have_cheap_transparency == MagickFalse)
10019                 ping_have_tRNS=MagickFalse;
10020             }
10021
10022           if (ping_have_tRNS != MagickFalse)
10023             {
10024               if (mng_info->write_png_colortype == 0)
10025                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
10026
10027               if (image_depth == 8)
10028                 {
10029                   ping_trans_color.red&=0xff;
10030                   ping_trans_color.green&=0xff;
10031                   ping_trans_color.blue&=0xff;
10032                   ping_trans_color.gray&=0xff;
10033                 }
10034             }
10035         }
10036       else
10037         {
10038           if (image_depth == 8)
10039             {
10040               ping_trans_color.red&=0xff;
10041               ping_trans_color.green&=0xff;
10042               ping_trans_color.blue&=0xff;
10043               ping_trans_color.gray&=0xff;
10044             }
10045         }
10046     }
10047
10048     matte=image_matte;
10049
10050     if (ping_have_tRNS != MagickFalse)
10051       image_matte=MagickFalse;
10052
10053     if ((mng_info->IsPalette) &&
10054         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10055         ping_have_color == MagickFalse &&
10056         (image_matte == MagickFalse || image_depth >= 8))
10057       {
10058         size_t one=1;
10059
10060         if (image_matte != MagickFalse)
10061           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10062
10063         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10064           {
10065             ping_color_type=PNG_COLOR_TYPE_GRAY;
10066
10067             if (save_image_depth == 16 && image_depth == 8)
10068               {
10069                 if (logging != MagickFalse)
10070                   {
10071                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10072                         "  Scaling ping_trans_color (0)");
10073                   }
10074                     ping_trans_color.gray*=0x0101;
10075               }
10076           }
10077
10078         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10079           image_depth=MAGICKCORE_QUANTUM_DEPTH;
10080
10081         if ((image_colors == 0) ||
10082              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10083           image_colors=(int) (one << image_depth);
10084
10085         if (image_depth > 8)
10086           ping_bit_depth=16;
10087
10088         else
10089           {
10090             ping_bit_depth=8;
10091             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10092               {
10093                 if(!mng_info->write_png_depth)
10094                   {
10095                     ping_bit_depth=1;
10096
10097                     while ((int) (one << ping_bit_depth)
10098                         < (ssize_t) image_colors)
10099                       ping_bit_depth <<= 1;
10100                   }
10101               }
10102
10103             else if (ping_color_type ==
10104                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10105                 mng_info->IsPalette)
10106               {
10107               /* Check if grayscale is reducible */
10108
10109                 int
10110                   depth_4_ok=MagickTrue,
10111                   depth_2_ok=MagickTrue,
10112                   depth_1_ok=MagickTrue;
10113
10114                 for (i=0; i < (ssize_t) image_colors; i++)
10115                 {
10116                    unsigned char
10117                      intensity;
10118
10119                    intensity=ScaleQuantumToChar(image->colormap[i].red);
10120
10121                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10122                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10123                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10124                      depth_2_ok=depth_1_ok=MagickFalse;
10125                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10126                      depth_1_ok=MagickFalse;
10127                 }
10128
10129                 if (depth_1_ok && mng_info->write_png_depth <= 1)
10130                   ping_bit_depth=1;
10131
10132                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10133                   ping_bit_depth=2;
10134
10135                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10136                   ping_bit_depth=4;
10137               }
10138           }
10139
10140           image_depth=ping_bit_depth;
10141       }
10142
10143     else
10144
10145       if (mng_info->IsPalette)
10146       {
10147         number_colors=image_colors;
10148
10149         if (image_depth <= 8)
10150           {
10151             /*
10152               Set image palette.
10153             */
10154             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10155
10156             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10157               {
10158                 for (i=0; i < (ssize_t) number_colors; i++)
10159                 {
10160                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10161                   palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10162                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10163                 }
10164
10165                 if (logging != MagickFalse)
10166                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10167                     "  Setting up PLTE chunk with %d colors",
10168                     number_colors);
10169
10170                 ping_have_PLTE=MagickTrue;
10171               }
10172
10173             /* color_type is PNG_COLOR_TYPE_PALETTE */
10174             if (mng_info->write_png_depth == 0)
10175               {
10176                 size_t
10177                   one;
10178
10179                 ping_bit_depth=1;
10180                 one=1;
10181
10182                 while ((one << ping_bit_depth) < (size_t) number_colors)
10183                   ping_bit_depth <<= 1;
10184               }
10185
10186             ping_num_trans=0;
10187
10188             if (matte != MagickFalse)
10189               {
10190                 /*
10191                  * Set up trans_colors array.
10192                  */
10193                 assert(number_colors <= 256);
10194
10195                 ping_num_trans=(unsigned short) (number_transparent +
10196                   number_semitransparent);
10197
10198                 if (ping_num_trans == 0)
10199                   ping_have_tRNS=MagickFalse;
10200
10201                 else
10202                   {
10203                     if (logging != MagickFalse)
10204                       {
10205                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10206                           "  Scaling ping_trans_color (1)");
10207                       }
10208                     ping_have_tRNS=MagickTrue;
10209
10210                     for (i=0; i < ping_num_trans; i++)
10211                     {
10212                        ping_trans_alpha[i]= (png_byte)
10213                          ScaleQuantumToChar(image->colormap[i].alpha);
10214                     }
10215                   }
10216               }
10217           }
10218       }
10219
10220     else
10221       {
10222
10223         if (image_depth < 8)
10224           image_depth=8;
10225
10226         if ((save_image_depth == 16) && (image_depth == 8))
10227           {
10228             if (logging != MagickFalse)
10229               {
10230                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10231                   "    Scaling ping_trans_color from (%d,%d,%d)",
10232                   (int) ping_trans_color.red,
10233                   (int) ping_trans_color.green,
10234                   (int) ping_trans_color.blue);
10235               }
10236
10237             ping_trans_color.red*=0x0101;
10238             ping_trans_color.green*=0x0101;
10239             ping_trans_color.blue*=0x0101;
10240             ping_trans_color.gray*=0x0101;
10241
10242             if (logging != MagickFalse)
10243               {
10244                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10245                   "    to (%d,%d,%d)",
10246                   (int) ping_trans_color.red,
10247                   (int) ping_trans_color.green,
10248                   (int) ping_trans_color.blue);
10249               }
10250           }
10251       }
10252
10253     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
10254          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
10255
10256     /*
10257       Adjust background and transparency samples in sub-8-bit grayscale files.
10258     */
10259     if (ping_bit_depth < 8 && ping_color_type ==
10260         PNG_COLOR_TYPE_GRAY)
10261       {
10262          png_uint_16
10263            maxval;
10264
10265          size_t
10266            one=1;
10267
10268          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10269
10270          if (ping_exclude_bKGD == MagickFalse)
10271          {
10272
10273          ping_background.gray=(png_uint_16) ((maxval/65535.)*
10274            (ScaleQuantumToShort(((GetPixelInfoIntensity(
10275            &image->background_color))) +.5)));
10276
10277          if (logging != MagickFalse)
10278            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10279              "  Setting up bKGD chunk (2)");
10280          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10281              "      background_color index is %d",
10282              (int) ping_background.index);
10283
10284          ping_have_bKGD = MagickTrue;
10285          }
10286
10287          if (logging != MagickFalse)
10288            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10289              "  Scaling ping_trans_color.gray from %d",
10290              (int)ping_trans_color.gray);
10291
10292          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10293            ping_trans_color.gray)+.5);
10294
10295          if (logging != MagickFalse)
10296            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10297              "      to %d", (int)ping_trans_color.gray);
10298       }
10299
10300   if (ping_exclude_bKGD == MagickFalse)
10301   {
10302     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10303       {
10304         /*
10305            Identify which colormap entry is the background color.
10306         */
10307
10308         number_colors=image_colors;
10309
10310         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10311           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10312             break;
10313
10314         ping_background.index=(png_byte) i;
10315
10316         if (logging != MagickFalse)
10317           {
10318             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10319               "  Setting up bKGD chunk with index=%d",(int) i);
10320           }
10321
10322         if (i < (ssize_t) number_colors)
10323           {
10324             ping_have_bKGD = MagickTrue;
10325
10326             if (logging != MagickFalse)
10327               {
10328                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10329                   "     background   =(%d,%d,%d)",
10330                         (int) ping_background.red,
10331                         (int) ping_background.green,
10332                         (int) ping_background.blue);
10333               }
10334           }
10335
10336         else  /* Can't happen */
10337           {
10338             if (logging != MagickFalse)
10339               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10340                   "      No room in PLTE to add bKGD color");
10341             ping_have_bKGD = MagickFalse;
10342           }
10343       }
10344   }
10345
10346   if (logging != MagickFalse)
10347     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10348       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10349       ping_color_type);
10350   /*
10351     Initialize compression level and filtering.
10352   */
10353   if (logging != MagickFalse)
10354     {
10355       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10356         "  Setting up deflate compression");
10357
10358       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10359         "    Compression buffer size: 32768");
10360     }
10361
10362   png_set_compression_buffer_size(ping,32768L);
10363
10364   if (logging != MagickFalse)
10365     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10366       "    Compression mem level: 9");
10367
10368   png_set_compression_mem_level(ping, 9);
10369
10370   /* Untangle the "-quality" setting:
10371
10372      Undefined is 0; the default is used.
10373      Default is 75
10374
10375      10's digit:
10376
10377         0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10378            zlib default compression level
10379
10380         1-9: the zlib compression level
10381
10382      1's digit:
10383
10384         0-4: the PNG filter method
10385
10386         5:   libpng adaptive filtering if compression level > 5
10387              libpng filter type "none" if compression level <= 5
10388                 or if image is grayscale or palette
10389
10390         6:   libpng adaptive filtering
10391
10392         7:   "LOCO" filtering (intrapixel differing) if writing
10393              a MNG, otherwise "none".  Did not work in IM-6.7.0-9
10394              and earlier because of a missing "else".
10395
10396         8:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10397              filtering. Unused prior to IM-6.7.0-10, was same as 6
10398
10399         9:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10400              Unused prior to IM-6.7.0-10, was same as 6
10401
10402     Note that using the -quality option, not all combinations of
10403     PNG filter type, zlib compression level, and zlib compression
10404     strategy are possible.  This will be addressed soon in a
10405     release that accomodates "-define png:compression-strategy", etc.
10406
10407    */
10408
10409   quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10410      image_info->quality;
10411
10412   if (quality <= 9)
10413     {
10414       if (mng_info->write_png_compression_strategy == 0)
10415         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10416     }
10417
10418   else if (mng_info->write_png_compression_level == 0)
10419     {
10420       int
10421         level;
10422
10423       level=(int) MagickMin((ssize_t) quality/10,9);
10424
10425       mng_info->write_png_compression_level = level+1;
10426     }
10427
10428   if (mng_info->write_png_compression_strategy == 0)
10429     {
10430         if ((quality %10) == 8 || (quality %10) == 9)
10431 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10432           mng_info->write_png_compression_strategy=Z_RLE+1;
10433 #else
10434           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10435 #endif
10436     }
10437
10438   if (mng_info->write_png_compression_filter == 0)
10439         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10440
10441   if (logging != MagickFalse)
10442     {
10443      if (mng_info->write_png_compression_level)
10444         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10445           "    Compression level:    %d",
10446             (int) mng_info->write_png_compression_level-1);
10447
10448      if (mng_info->write_png_compression_strategy)
10449         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10450           "    Compression strategy: %d",
10451             (int) mng_info->write_png_compression_strategy-1);
10452
10453         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10454           "  Setting up filtering");
10455
10456         if (mng_info->write_png_compression_filter == 6)
10457           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10458             "    Base filter method: ADAPTIVE");
10459         else if (mng_info->write_png_compression_filter == 0 ||
10460                  mng_info->write_png_compression_filter == 1)
10461           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10462             "    Base filter method: NONE");
10463         else
10464           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10465             "    Base filter method: %d",
10466             (int) mng_info->write_png_compression_filter-1);
10467     }
10468
10469   if (mng_info->write_png_compression_level != 0)
10470     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10471
10472   if (mng_info->write_png_compression_filter == 6)
10473     {
10474       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10475          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10476          (quality < 50))
10477         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10478       else
10479         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10480      }
10481   else if (mng_info->write_png_compression_filter == 7 ||
10482       mng_info->write_png_compression_filter == 10)
10483     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10484
10485   else if (mng_info->write_png_compression_filter == 8)
10486     {
10487 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10488       if (mng_info->write_mng)
10489       {
10490          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10491              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10492         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10493       }
10494 #endif
10495       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10496     }
10497
10498   else if (mng_info->write_png_compression_filter == 9)
10499     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10500
10501   else if (mng_info->write_png_compression_filter != 0)
10502     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10503        mng_info->write_png_compression_filter-1);
10504
10505   if (mng_info->write_png_compression_strategy != 0)
10506     png_set_compression_strategy(ping,
10507        mng_info->write_png_compression_strategy-1);
10508
10509   ping_interlace_method=image_info->interlace != NoInterlace;
10510
10511   if (mng_info->write_mng)
10512     png_set_sig_bytes(ping,8);
10513
10514   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10515
10516   if (mng_info->write_png_colortype != 0)
10517     {
10518      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10519        if (ping_have_color != MagickFalse)
10520          {
10521            ping_color_type = PNG_COLOR_TYPE_RGB;
10522
10523            if (ping_bit_depth < 8)
10524              ping_bit_depth=8;
10525          }
10526
10527      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10528        if (ping_have_color != MagickFalse)
10529          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10530     }
10531
10532   if (ping_need_colortype_warning != MagickFalse ||
10533      ((mng_info->write_png_depth &&
10534      (int) mng_info->write_png_depth != ping_bit_depth) ||
10535      (mng_info->write_png_colortype &&
10536      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10537       mng_info->write_png_colortype != 7 &&
10538       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10539     {
10540       if (logging != MagickFalse)
10541         {
10542           if (ping_need_colortype_warning != MagickFalse)
10543             {
10544               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10545                  "  Image has transparency but tRNS chunk was excluded");
10546             }
10547
10548           if (mng_info->write_png_depth)
10549             {
10550               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10551                   "  Defined png:bit-depth=%u, Computed depth=%u",
10552                   mng_info->write_png_depth,
10553                   ping_bit_depth);
10554             }
10555
10556           if (mng_info->write_png_colortype)
10557             {
10558               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10559                   "  Defined png:color-type=%u, Computed color type=%u",
10560                   mng_info->write_png_colortype-1,
10561                   ping_color_type);
10562             }
10563         }
10564
10565       png_warning(ping,
10566         "Cannot write image with defined png:bit-depth or png:color-type.");
10567     }
10568
10569   if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10570     {
10571       /* Add an opaque matte channel */
10572       image->alpha_trait = BlendPixelTrait;
10573       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10574
10575       if (logging != MagickFalse)
10576         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10577           "  Added an opaque matte channel");
10578     }
10579
10580   if (number_transparent != 0 || number_semitransparent != 0)
10581     {
10582       if (ping_color_type < 4)
10583         {
10584            ping_have_tRNS=MagickTrue;
10585            if (logging != MagickFalse)
10586              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10587                "  Setting ping_have_tRNS=MagickTrue.");
10588         }
10589     }
10590
10591   if (logging != MagickFalse)
10592     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10593       "  Writing PNG header chunks");
10594
10595   png_set_IHDR(ping,ping_info,ping_width,ping_height,
10596                ping_bit_depth,ping_color_type,
10597                ping_interlace_method,ping_compression_method,
10598                ping_filter_method);
10599
10600   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10601     {
10602       png_set_PLTE(ping,ping_info,palette,number_colors);
10603
10604       if (logging != MagickFalse)
10605         {
10606           for (i=0; i< (ssize_t) number_colors; i++)
10607           {
10608             if (i < ping_num_trans)
10609               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10610                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10611                       (int) i,
10612                       (int) palette[i].red,
10613                       (int) palette[i].green,
10614                       (int) palette[i].blue,
10615                       (int) i,
10616                       (int) ping_trans_alpha[i]);
10617              else
10618               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10619                 "     PLTE[%d] = (%d,%d,%d)",
10620                       (int) i,
10621                       (int) palette[i].red,
10622                       (int) palette[i].green,
10623                       (int) palette[i].blue);
10624            }
10625          }
10626     }
10627
10628   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10629   if (ping_exclude_sRGB != MagickFalse ||
10630      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10631   {
10632     if ((ping_exclude_tEXt == MagickFalse ||
10633        ping_exclude_zTXt == MagickFalse) &&
10634        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10635     {
10636       ResetImageProfileIterator(image);
10637       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10638       {
10639         profile=GetImageProfile(image,name);
10640
10641         if (profile != (StringInfo *) NULL)
10642           {
10643 #ifdef PNG_WRITE_iCCP_SUPPORTED
10644             if ((LocaleCompare(name,"ICC") == 0) ||
10645                 (LocaleCompare(name,"ICM") == 0))
10646              {
10647
10648                if (ping_exclude_iCCP == MagickFalse)
10649                  {
10650                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10651                           "  Setting up iCCP chunk");
10652     
10653                        png_set_iCCP(ping,ping_info,(png_charp) name,0,
10654 #if (PNG_LIBPNG_VER < 10500)
10655                          (png_charp) GetStringInfoDatum(profile),
10656 #else
10657                          (png_const_bytep) GetStringInfoDatum(profile),
10658 #endif
10659                          (png_uint_32) GetStringInfoLength(profile));
10660                        ping_have_iCCP = MagickTrue;
10661                  }
10662              }
10663
10664             else
10665 #endif
10666               if (ping_exclude_zCCP == MagickFalse)
10667                 {
10668                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10669                       "  Setting up zTXT chunk with uuencoded ICC");
10670                   Magick_png_write_raw_profile(image_info,ping,ping_info,
10671                     (unsigned char *) name,(unsigned char *) name,
10672                     GetStringInfoDatum(profile),
10673                     (png_uint_32) GetStringInfoLength(profile));
10674                   ping_have_iCCP = MagickTrue;
10675                 }
10676           }
10677
10678           if (logging != MagickFalse)
10679             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10680               "  Setting up text chunk with %s profile",name);
10681
10682         name=GetNextImageProfile(image);
10683       }
10684     }
10685   }
10686
10687 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10688   if ((mng_info->have_write_global_srgb == 0) &&
10689       ping_have_iCCP != MagickTrue &&
10690       (ping_have_sRGB != MagickFalse ||
10691       png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10692     {
10693       if (ping_exclude_sRGB == MagickFalse)
10694         {
10695           /*
10696             Note image rendering intent.
10697           */
10698           if (logging != MagickFalse)
10699             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10700                 "  Setting up sRGB chunk");
10701
10702           (void) png_set_sRGB(ping,ping_info,(
10703             Magick_RenderingIntent_to_PNG_RenderingIntent(
10704               image->rendering_intent)));
10705
10706           ping_have_sRGB = MagickTrue;
10707         }
10708     }
10709
10710   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10711 #endif
10712     {
10713       if (ping_exclude_gAMA == MagickFalse &&
10714           ping_have_iCCP == MagickFalse &&
10715           ping_have_sRGB == MagickFalse &&
10716           (ping_exclude_sRGB == MagickFalse ||
10717           (image->gamma < .45 || image->gamma > .46)))
10718       {
10719       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10720         {
10721           /*
10722             Note image gamma.
10723             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10724           */
10725           if (logging != MagickFalse)
10726             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10727               "  Setting up gAMA chunk");
10728
10729           png_set_gAMA(ping,ping_info,image->gamma);
10730         }
10731       }
10732
10733       if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10734         {
10735           if ((mng_info->have_write_global_chrm == 0) &&
10736               (image->chromaticity.red_primary.x != 0.0))
10737             {
10738               /*
10739                 Note image chromaticity.
10740                 Note: if cHRM+gAMA == sRGB write sRGB instead.
10741               */
10742                PrimaryInfo
10743                  bp,
10744                  gp,
10745                  rp,
10746                  wp;
10747
10748                wp=image->chromaticity.white_point;
10749                rp=image->chromaticity.red_primary;
10750                gp=image->chromaticity.green_primary;
10751                bp=image->chromaticity.blue_primary;
10752
10753                if (logging != MagickFalse)
10754                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10755                    "  Setting up cHRM chunk");
10756
10757                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10758                    bp.x,bp.y);
10759            }
10760         }
10761     }
10762
10763   if (ping_exclude_bKGD == MagickFalse)
10764     {
10765       if (ping_have_bKGD != MagickFalse)
10766         {
10767           png_set_bKGD(ping,ping_info,&ping_background);
10768           if (logging)
10769             {
10770               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10771                    "    Setting up bKGD chunk");
10772               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10773                    "      background color = (%d,%d,%d)",
10774                         (int) ping_background.red,
10775                         (int) ping_background.green,
10776                         (int) ping_background.blue);
10777               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10778                    "      index = %d, gray=%d",
10779                         (int) ping_background.index,
10780                         (int) ping_background.gray);
10781             }
10782          }
10783     }
10784
10785   if (ping_exclude_pHYs == MagickFalse)
10786     {
10787       if (ping_have_pHYs != MagickFalse)
10788         {
10789           png_set_pHYs(ping,ping_info,
10790              ping_pHYs_x_resolution,
10791              ping_pHYs_y_resolution,
10792              ping_pHYs_unit_type);
10793
10794           if (logging)
10795             {
10796               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10797                    "    Setting up pHYs chunk");
10798               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10799                    "      x_resolution=%lu",
10800                    (unsigned long) ping_pHYs_x_resolution);
10801               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10802                    "      y_resolution=%lu",
10803                    (unsigned long) ping_pHYs_y_resolution);
10804               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10805                    "      unit_type=%lu",
10806                    (unsigned long) ping_pHYs_unit_type);
10807             }
10808         }
10809     }
10810
10811 #if defined(PNG_oFFs_SUPPORTED)
10812   if (ping_exclude_oFFs == MagickFalse)
10813     {
10814       if (image->page.x || image->page.y)
10815         {
10816            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10817               (png_int_32) image->page.y, 0);
10818
10819            if (logging != MagickFalse)
10820              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10821                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
10822                  (int) image->page.x, (int) image->page.y);
10823         }
10824     }
10825 #endif
10826
10827   if (mng_info->need_blob != MagickFalse)
10828   {
10829     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10830        MagickFalse)
10831        png_error(ping,"WriteBlob Failed");
10832
10833      ping_have_blob=MagickTrue;
10834   }
10835
10836   png_write_info_before_PLTE(ping, ping_info);
10837
10838   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10839     {
10840       if (logging != MagickFalse)
10841         {
10842           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10843               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10844         }
10845
10846       if (ping_color_type == 3)
10847          (void) png_set_tRNS(ping, ping_info,
10848                 ping_trans_alpha,
10849                 ping_num_trans,
10850                 NULL);
10851
10852       else
10853         {
10854            (void) png_set_tRNS(ping, ping_info,
10855                   NULL,
10856                   0,
10857                   &ping_trans_color);
10858
10859            if (logging != MagickFalse)
10860              {
10861                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10862                  "     tRNS color   =(%d,%d,%d)",
10863                        (int) ping_trans_color.red,
10864                        (int) ping_trans_color.green,
10865                        (int) ping_trans_color.blue);
10866              }
10867          }
10868     }
10869
10870   /* write any png-chunk-b profiles */
10871   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10872
10873   png_write_info(ping,ping_info);
10874
10875   /* write any PNG-chunk-m profiles */
10876   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10877
10878   if (ping_exclude_vpAg == MagickFalse)
10879     {
10880       if ((image->page.width != 0 && image->page.width != image->columns) ||
10881           (image->page.height != 0 && image->page.height != image->rows))
10882         {
10883           unsigned char
10884             chunk[14];
10885
10886           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
10887           PNGType(chunk,mng_vpAg);
10888           LogPNGChunk(logging,mng_vpAg,9L);
10889           PNGLong(chunk+4,(png_uint_32) image->page.width);
10890           PNGLong(chunk+8,(png_uint_32) image->page.height);
10891           chunk[12]=0;   /* unit = pixels */
10892           (void) WriteBlob(image,13,chunk);
10893           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10894         }
10895     }
10896
10897 #if (PNG_LIBPNG_VER == 10206)
10898     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10899 #define PNG_HAVE_IDAT               0x04
10900     ping->mode |= PNG_HAVE_IDAT;
10901 #undef PNG_HAVE_IDAT
10902 #endif
10903
10904   png_set_packing(ping);
10905   /*
10906     Allocate memory.
10907   */
10908   rowbytes=image->columns;
10909   if (image_depth > 8)
10910     rowbytes*=2;
10911   switch (ping_color_type)
10912     {
10913       case PNG_COLOR_TYPE_RGB:
10914         rowbytes*=3;
10915         break;
10916
10917       case PNG_COLOR_TYPE_GRAY_ALPHA:
10918         rowbytes*=2;
10919         break;
10920
10921       case PNG_COLOR_TYPE_RGBA:
10922         rowbytes*=4;
10923         break;
10924
10925       default:
10926         break;
10927     }
10928
10929   if (logging != MagickFalse)
10930     {
10931       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10932         "  Writing PNG image data");
10933
10934       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10935         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10936     }
10937   pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10938   if (pixel_info == (MemoryInfo *) NULL)
10939     png_error(ping,"Allocation of memory for pixels failed");
10940   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
10941
10942   /*
10943     Initialize image scanlines.
10944   */
10945   quantum_info=AcquireQuantumInfo(image_info,image);
10946   if (quantum_info == (QuantumInfo *) NULL)
10947     png_error(ping,"Memory allocation for quantum_info failed");
10948   quantum_info->format=UndefinedQuantumFormat;
10949   quantum_info->depth=image_depth;
10950   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10951   num_passes=png_set_interlace_handling(ping);
10952
10953   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10954        !mng_info->write_png48 && !mng_info->write_png64 &&
10955        !mng_info->write_png32) &&
10956        (mng_info->IsPalette ||
10957        (image_info->type == BilevelType)) &&
10958        image_matte == MagickFalse &&
10959        ping_have_non_bw == MagickFalse)
10960     {
10961       /* Palette, Bilevel, or Opaque Monochrome */
10962       register const Quantum
10963         *p;
10964
10965       quantum_info->depth=8;
10966       for (pass=0; pass < num_passes; pass++)
10967       {
10968         /*
10969           Convert PseudoClass image to a PNG monochrome image.
10970         */
10971         for (y=0; y < (ssize_t) image->rows; y++)
10972         {
10973           if (logging != MagickFalse && y == 0)
10974              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10975                  "    Writing row of pixels (0)");
10976
10977           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10978
10979           if (p == (const Quantum *) NULL)
10980             break;
10981
10982           if (mng_info->IsPalette)
10983             {
10984               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10985                 quantum_info,GrayQuantum,ping_pixels,exception);
10986               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10987                   mng_info->write_png_depth &&
10988                   mng_info->write_png_depth != old_bit_depth)
10989                 {
10990                   /* Undo pixel scaling */
10991                   for (i=0; i < (ssize_t) image->columns; i++)
10992                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10993                      >> (8-old_bit_depth));
10994                 }
10995             }
10996
10997           else
10998             {
10999               (void) ExportQuantumPixels(image,(CacheView *) NULL,
11000                 quantum_info,RedQuantum,ping_pixels,exception);
11001             }
11002
11003           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11004             for (i=0; i < (ssize_t) image->columns; i++)
11005                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11006                       255 : 0);
11007
11008           if (logging != MagickFalse && y == 0)
11009             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11010                 "    Writing row of pixels (1)");
11011
11012           png_write_row(ping,ping_pixels);
11013         }
11014         if (image->previous == (Image *) NULL)
11015           {
11016             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11017             if (status == MagickFalse)
11018               break;
11019           }
11020       }
11021     }
11022
11023   else   /* Not Palette, Bilevel, or Opaque Monochrome */
11024     {
11025       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11026           !mng_info->write_png48 && !mng_info->write_png64 &&
11027           !mng_info->write_png32) && (image_matte != MagickFalse ||
11028           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11029           (mng_info->IsPalette) && ping_have_color == MagickFalse)
11030         {
11031           register const Quantum
11032             *p;
11033
11034           for (pass=0; pass < num_passes; pass++)
11035           {
11036
11037           for (y=0; y < (ssize_t) image->rows; y++)
11038           {
11039             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11040
11041             if (p == (const Quantum *) NULL)
11042               break;
11043
11044             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11045               {
11046                 if (mng_info->IsPalette)
11047                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11048                     quantum_info,GrayQuantum,ping_pixels,exception);
11049
11050                 else
11051                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11052                     quantum_info,RedQuantum,ping_pixels,exception);
11053
11054                 if (logging != MagickFalse && y == 0)
11055                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11056                        "    Writing GRAY PNG pixels (2)");
11057               }
11058
11059             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11060               {
11061                 if (logging != MagickFalse && y == 0)
11062                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11063                          "    Writing GRAY_ALPHA PNG pixels (2)");
11064
11065                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11066                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11067               }
11068
11069             if (logging != MagickFalse && y == 0)
11070               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11071                   "    Writing row of pixels (2)");
11072
11073             png_write_row(ping,ping_pixels);
11074           }
11075
11076           if (image->previous == (Image *) NULL)
11077             {
11078               status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11079               if (status == MagickFalse)
11080                 break;
11081             }
11082           }
11083         }
11084
11085       else
11086         {
11087           register const Quantum
11088             *p;
11089
11090           for (pass=0; pass < num_passes; pass++)
11091           {
11092             if ((image_depth > 8) ||
11093                 mng_info->write_png24 ||
11094                 mng_info->write_png32 ||
11095                 mng_info->write_png48 ||
11096                 mng_info->write_png64 ||
11097                 (!mng_info->write_png8 && !mng_info->IsPalette))
11098             {
11099               for (y=0; y < (ssize_t) image->rows; y++)
11100               {
11101                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11102
11103                 if (p == (const Quantum *) NULL)
11104                   break;
11105
11106                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11107                   {
11108                     if (image->storage_class == DirectClass)
11109                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11110                         quantum_info,RedQuantum,ping_pixels,exception);
11111
11112                     else
11113                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11114                         quantum_info,GrayQuantum,ping_pixels,exception);
11115                   }
11116
11117                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11118                   {
11119                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11120                       quantum_info,GrayAlphaQuantum,ping_pixels,
11121                       exception);
11122
11123                     if (logging != MagickFalse && y == 0)
11124                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11125                            "    Writing GRAY_ALPHA PNG pixels (3)");
11126                   }
11127
11128                 else if (image_matte != MagickFalse)
11129                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11130                     quantum_info,RGBAQuantum,ping_pixels,exception);
11131
11132                 else
11133                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11134                     quantum_info,RGBQuantum,ping_pixels,exception);
11135
11136                 if (logging != MagickFalse && y == 0)
11137                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11138                       "    Writing row of pixels (3)");
11139
11140                 png_write_row(ping,ping_pixels);
11141               }
11142             }
11143
11144           else
11145             /* not ((image_depth > 8) ||
11146                 mng_info->write_png24 || mng_info->write_png32 ||
11147                 mng_info->write_png48 || mng_info->write_png64 ||
11148                 (!mng_info->write_png8 && !mng_info->IsPalette))
11149              */
11150             {
11151               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11152                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11153                 {
11154                   if (logging != MagickFalse)
11155                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11156                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11157
11158                   quantum_info->depth=8;
11159                   image_depth=8;
11160                 }
11161
11162               for (y=0; y < (ssize_t) image->rows; y++)
11163               {
11164                 if (logging != MagickFalse && y == 0)
11165                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11166                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
11167
11168                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11169
11170                 if (p == (const Quantum *) NULL)
11171                   break;
11172
11173                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11174                   {
11175                     quantum_info->depth=image->depth;
11176
11177                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11178                        quantum_info,GrayQuantum,ping_pixels,exception);
11179                   }
11180
11181                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11182                   {
11183                     if (logging != MagickFalse && y == 0)
11184                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11185                            "  Writing GRAY_ALPHA PNG pixels (4)");
11186
11187                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11188                          quantum_info,GrayAlphaQuantum,ping_pixels,
11189                          exception);
11190                   }
11191
11192                 else
11193                   {
11194                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11195                       quantum_info,IndexQuantum,ping_pixels,exception);
11196
11197                     if (logging != MagickFalse && y <= 2)
11198                     {
11199                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11200                           "  Writing row of non-gray pixels (4)");
11201
11202                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11203                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
11204                           (int)ping_pixels[0],(int)ping_pixels[1]);
11205                     }
11206                   }
11207                 png_write_row(ping,ping_pixels);
11208               }
11209             }
11210
11211             if (image->previous == (Image *) NULL)
11212               {
11213                 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11214                 if (status == MagickFalse)
11215                   break;
11216               }
11217           }
11218         }
11219     }
11220
11221   if (quantum_info != (QuantumInfo *) NULL)
11222     quantum_info=DestroyQuantumInfo(quantum_info);
11223
11224   if (logging != MagickFalse)
11225     {
11226       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11227         "  Wrote PNG image data");
11228
11229       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11230         "    Width: %.20g",(double) ping_width);
11231
11232       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11233         "    Height: %.20g",(double) ping_height);
11234
11235       if (mng_info->write_png_depth)
11236         {
11237           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11238             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
11239         }
11240
11241       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11242         "    PNG bit-depth written: %d",ping_bit_depth);
11243
11244       if (mng_info->write_png_colortype)
11245         {
11246           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11247             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
11248         }
11249
11250       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11251         "    PNG color-type written: %d",ping_color_type);
11252
11253       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11254         "    PNG Interlace method: %d",ping_interlace_method);
11255     }
11256   /*
11257     Generate text chunks after IDAT.
11258   */
11259   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11260   {
11261     ResetImagePropertyIterator(image);
11262     property=GetNextImageProperty(image);
11263     while (property != (const char *) NULL)
11264     {
11265       png_textp
11266         text;
11267
11268       value=GetImageProperty(image,property,exception);
11269
11270       /* Don't write any "png:" or "jpeg:" properties; those are just for
11271        * "identify" or for passing through to another JPEG
11272        */
11273       if ((LocaleNCompare(property,"png:",4) != 0 &&
11274            LocaleNCompare(property,"jpeg:",5) != 0) &&
11275
11276
11277           /* Suppress density and units if we wrote a pHYs chunk */
11278           (ping_exclude_pHYs != MagickFalse      ||
11279           LocaleCompare(property,"density") != 0 ||
11280           LocaleCompare(property,"units") != 0) &&
11281
11282           /* Suppress the IM-generated Date:create and Date:modify */
11283           (ping_exclude_date == MagickFalse      ||
11284           LocaleNCompare(property, "Date:",5) != 0))
11285         {
11286         if (value != (const char *) NULL)
11287           {
11288
11289 #if PNG_LIBPNG_VER >= 10400
11290             text=(png_textp) png_malloc(ping,
11291                  (png_alloc_size_t) sizeof(png_text));
11292 #else
11293             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11294 #endif
11295             text[0].key=(char *) property;
11296             text[0].text=(char *) value;
11297             text[0].text_length=strlen(value);
11298
11299             if (ping_exclude_tEXt != MagickFalse)
11300                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11301
11302             else if (ping_exclude_zTXt != MagickFalse)
11303                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11304
11305             else
11306             {
11307                text[0].compression=image_info->compression == NoCompression ||
11308                  (image_info->compression == UndefinedCompression &&
11309                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11310                  PNG_TEXT_COMPRESSION_zTXt ;
11311             }
11312
11313             if (logging != MagickFalse)
11314               {
11315                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11316                   "  Setting up text chunk");
11317
11318                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11319                   "    keyword: '%s'",text[0].key);
11320               }
11321
11322             png_set_text(ping,ping_info,text,1);
11323             png_free(ping,text);
11324           }
11325         }
11326       property=GetNextImageProperty(image);
11327     }
11328   }
11329
11330   /* write any PNG-chunk-e profiles */
11331   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11332
11333   if (logging != MagickFalse)
11334     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11335       "  Writing PNG end info");
11336
11337   png_write_end(ping,ping_info);
11338
11339   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11340     {
11341       if (mng_info->page.x || mng_info->page.y ||
11342           (ping_width != mng_info->page.width) ||
11343           (ping_height != mng_info->page.height))
11344         {
11345           unsigned char
11346             chunk[32];
11347
11348           /*
11349             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11350           */
11351           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11352           PNGType(chunk,mng_FRAM);
11353           LogPNGChunk(logging,mng_FRAM,27L);
11354           chunk[4]=4;
11355           chunk[5]=0;  /* frame name separator (no name) */
11356           chunk[6]=1;  /* flag for changing delay, for next frame only */
11357           chunk[7]=0;  /* flag for changing frame timeout */
11358           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11359           chunk[9]=0;  /* flag for changing frame sync_id */
11360           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11361           chunk[14]=0; /* clipping boundaries delta type */
11362           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11363           PNGLong(chunk+19,
11364              (png_uint_32) (mng_info->page.x + ping_width));
11365           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11366           PNGLong(chunk+27,
11367              (png_uint_32) (mng_info->page.y + ping_height));
11368           (void) WriteBlob(image,31,chunk);
11369           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11370           mng_info->old_framing_mode=4;
11371           mng_info->framing_mode=1;
11372         }
11373
11374       else
11375         mng_info->framing_mode=3;
11376     }
11377   if (mng_info->write_mng && !mng_info->need_fram &&
11378       ((int) image->dispose == 3))
11379      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11380
11381   /*
11382     Free PNG resources.
11383   */
11384
11385   png_destroy_write_struct(&ping,&ping_info);
11386
11387   pixel_info=RelinquishVirtualMemory(pixel_info);
11388
11389   if (ping_have_blob != MagickFalse)
11390      (void) CloseBlob(image);
11391
11392   image_info=DestroyImageInfo(image_info);
11393   image=DestroyImage(image);
11394
11395   /* Store bit depth actually written */
11396   s[0]=(char) ping_bit_depth;
11397   s[1]='\0';
11398
11399   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11400
11401   if (logging != MagickFalse)
11402     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11403       "  exit WriteOnePNGImage()");
11404
11405 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
11406   UnlockSemaphoreInfo(ping_semaphore);
11407 #endif
11408
11409    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11410     *    Throwing an Exception when an error occurs.
11411     */
11412
11413   return(MagickTrue);
11414 /*  End write one PNG image */
11415
11416 }
11417
11418 /*
11419 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11420 %                                                                             %
11421 %                                                                             %
11422 %                                                                             %
11423 %   W r i t e P N G I m a g e                                                 %
11424 %                                                                             %
11425 %                                                                             %
11426 %                                                                             %
11427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11428 %
11429 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11430 %  Multiple-image Network Graphics (MNG) image file.
11431 %
11432 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11433 %
11434 %  The format of the WritePNGImage method is:
11435 %
11436 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11437 %        Image *image,ExceptionInfo *exception)
11438 %
11439 %  A description of each parameter follows:
11440 %
11441 %    o image_info: the image info.
11442 %
11443 %    o image:  The image.
11444 %
11445 %    o exception: return any errors or warnings in this structure.
11446 %
11447 %  Returns MagickTrue on success, MagickFalse on failure.
11448 %
11449 %  Communicating with the PNG encoder:
11450 %
11451 %  While the datastream written is always in PNG format and normally would
11452 %  be given the "png" file extension, this method also writes the following
11453 %  pseudo-formats which are subsets of png:
11454 %
11455 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11456 %               a depth greater than 8, the depth is reduced. If transparency
11457 %               is present, the tRNS chunk must only have values 0 and 255
11458 %               (i.e., transparency is binary: fully opaque or fully
11459 %               transparent).  If other values are present they will be
11460 %               50%-thresholded to binary transparency.  If more than 256
11461 %               colors are present, they will be quantized to the 4-4-4-1,
11462 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11463 %               of any resulting fully-transparent pixels is changed to
11464 %               the image's background color.
11465 %
11466 %               If you want better quantization or dithering of the colors
11467 %               or alpha than that, you need to do it before calling the
11468 %               PNG encoder. The pixels contain 8-bit indices even if
11469 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11470 %               images will be written as indexed PNG files even though the
11471 %               PNG grayscale type might be slightly more efficient.  Please
11472 %               note that writing to the PNG8 format may result in loss
11473 %               of color and alpha data.
11474 %
11475 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11476 %               chunk can be present to convey binary transparency by naming
11477 %               one of the colors as transparent.  The only loss incurred
11478 %               is reduction of sample depth to 8.  If the image has more
11479 %               than one transparent color, has semitransparent pixels, or
11480 %               has an opaque pixel with the same RGB components as the
11481 %               transparent color, an image is not written.
11482 %
11483 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
11484 %               transparency is permitted, i.e., the alpha sample for
11485 %               each pixel can have any value from 0 to 255. The alpha
11486 %               channel is present even if the image is fully opaque.
11487 %               The only loss in data is the reduction of the sample depth
11488 %               to 8.
11489 %
11490 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
11491 %               chunk can be present to convey binary transparency by naming
11492 %               one of the colors as transparent.  If the image has more
11493 %               than one transparent color, has semitransparent pixels, or
11494 %               has an opaque pixel with the same RGB components as the
11495 %               transparent color, an image is not written.
11496 %
11497 %    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
11498 %               transparency is permitted, i.e., the alpha sample for
11499 %               each pixel can have any value from 0 to 65535. The alpha
11500 %               channel is present even if the image is fully opaque.
11501 %
11502 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
11503 %               image, if the input was a PNG, is written.  If these values
11504 %               cannot be found, then "PNG00" falls back to the regular "PNG"
11505 %               format.
11506 %
11507 %    o -define: For more precise control of the PNG output, you can use the
11508 %               Image options "png:bit-depth" and "png:color-type".  These
11509 %               can be set from the commandline with "-define" and also
11510 %               from the application programming interfaces.  The options
11511 %               are case-independent and are converted to lowercase before
11512 %               being passed to this encoder.
11513 %
11514 %               png:color-type can be 0, 2, 3, 4, or 6.
11515 %
11516 %               When png:color-type is 0 (Grayscale), png:bit-depth can
11517 %               be 1, 2, 4, 8, or 16.
11518 %
11519 %               When png:color-type is 2 (RGB), png:bit-depth can
11520 %               be 8 or 16.
11521 %
11522 %               When png:color-type is 3 (Indexed), png:bit-depth can
11523 %               be 1, 2, 4, or 8.  This refers to the number of bits
11524 %               used to store the index.  The color samples always have
11525 %               bit-depth 8 in indexed PNG files.
11526 %
11527 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11528 %               png:bit-depth can be 8 or 16.
11529 %
11530 %               If the image cannot be written without loss with the
11531 %               requested bit-depth and color-type, a PNG file will not
11532 %               be written, a warning will be issued, and the encoder will
11533 %               return MagickFalse.
11534 %
11535 %  Since image encoders should not be responsible for the "heavy lifting",
11536 %  the user should make sure that ImageMagick has already reduced the
11537 %  image depth and number of colors and limit transparency to binary
11538 %  transparency prior to attempting to write the image with depth, color,
11539 %  or transparency limitations.
11540 %
11541 %  Note that another definition, "png:bit-depth-written" exists, but it
11542 %  is not intended for external use.  It is only used internally by the
11543 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11544 %
11545 %  It is possible to request that the PNG encoder write previously-formatted
11546 %  ancillary chunks in the output PNG file, using the "-profile" commandline
11547 %  option as shown below or by setting the profile via a programming
11548 %  interface:
11549 %
11550 %     -profile PNG-chunk-x:<file>
11551 %
11552 %  where x is a location flag and <file> is a file containing the chunk
11553 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11554 %  This encoder will compute the chunk length and CRC, so those must not
11555 %  be included in the file.
11556 %
11557 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11558 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
11559 %  of the same type, then add a short unique string after the "x" to prevent
11560 %  subsequent profiles from overwriting the preceding ones, e.g.,
11561 %
11562 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11563 %
11564 %  As of version 6.6.6 the following optimizations are always done:
11565 %
11566 %   o  32-bit depth is reduced to 16.
11567 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
11568 %      high byte and low byte are identical.
11569 %   o  Palette is sorted to remove unused entries and to put a
11570 %      transparent color first, if BUILD_PNG_PALETTE is defined.
11571 %   o  Opaque matte channel is removed when writing an indexed PNG.
11572 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
11573 %      this can be done without loss and a larger bit depth N was not
11574 %      requested via the "-define png:bit-depth=N" option.
11575 %   o  If matte channel is present but only one transparent color is
11576 %      present, RGB+tRNS is written instead of RGBA
11577 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
11578 %      was requested when converting an opaque image).
11579 %
11580 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11581 */
11582 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11583   Image *image,ExceptionInfo *exception)
11584 {
11585   MagickBooleanType
11586     excluding,
11587     logging,
11588     have_mng_structure,
11589     status;
11590
11591   MngInfo
11592     *mng_info;
11593
11594   const char
11595     *value;
11596
11597   int
11598     i,
11599     source;
11600
11601   /*
11602     Open image file.
11603   */
11604   assert(image_info != (const ImageInfo *) NULL);
11605   assert(image_info->signature == MagickSignature);
11606   assert(image != (Image *) NULL);
11607   assert(image->signature == MagickSignature);
11608   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11609   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11610   /*
11611     Allocate a MngInfo structure.
11612   */
11613   have_mng_structure=MagickFalse;
11614   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11615
11616   if (mng_info == (MngInfo *) NULL)
11617     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11618
11619   /*
11620     Initialize members of the MngInfo structure.
11621   */
11622   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11623   mng_info->image=image;
11624   mng_info->equal_backgrounds=MagickTrue;
11625   have_mng_structure=MagickTrue;
11626
11627   /* See if user has requested a specific PNG subformat */
11628
11629   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11630   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11631   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11632   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11633   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11634
11635   value=GetImageOption(image_info,"png:format");
11636
11637   if (value != (char *) NULL)
11638     {
11639       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11640          "  Format=%s",value);
11641
11642       mng_info->write_png8 = MagickFalse;
11643       mng_info->write_png24 = MagickFalse;
11644       mng_info->write_png32 = MagickFalse;
11645       mng_info->write_png48 = MagickFalse;
11646       mng_info->write_png64 = MagickFalse;
11647
11648       if (LocaleCompare(value,"png8") == 0)
11649         mng_info->write_png8 = MagickTrue;
11650
11651       else if (LocaleCompare(value,"png24") == 0)
11652         mng_info->write_png24 = MagickTrue;
11653
11654       else if (LocaleCompare(value,"png32") == 0)
11655         mng_info->write_png32 = MagickTrue;
11656
11657       else if (LocaleCompare(value,"png48") == 0)
11658         mng_info->write_png48 = MagickTrue;
11659
11660       else if (LocaleCompare(value,"png64") == 0)
11661         mng_info->write_png64 = MagickTrue;
11662
11663       else if (LocaleCompare(value,"png00") == 0)
11664         {
11665           /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11666           value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11667     
11668           if (value != (char *) NULL)
11669             {
11670               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11671                  "  png00 inherited bit depth=%s",value);
11672     
11673               if (LocaleCompare(value,"1") == 0)
11674                 mng_info->write_png_depth = 1;
11675     
11676               else if (LocaleCompare(value,"1") == 0)
11677                 mng_info->write_png_depth = 2;
11678     
11679               else if (LocaleCompare(value,"2") == 0)
11680                 mng_info->write_png_depth = 4;
11681     
11682               else if (LocaleCompare(value,"8") == 0)
11683                 mng_info->write_png_depth = 8;
11684     
11685               else if (LocaleCompare(value,"16") == 0)
11686                 mng_info->write_png_depth = 16;
11687             }
11688     
11689           value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11690     
11691           if (value != (char *) NULL)
11692             {
11693               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11694                  "  png00 inherited color type=%s",value);
11695     
11696               if (LocaleCompare(value,"0") == 0)
11697                 mng_info->write_png_colortype = 1;
11698     
11699               else if (LocaleCompare(value,"2") == 0)
11700                 mng_info->write_png_colortype = 3;
11701     
11702               else if (LocaleCompare(value,"3") == 0)
11703                 mng_info->write_png_colortype = 4;
11704     
11705               else if (LocaleCompare(value,"4") == 0)
11706                 mng_info->write_png_colortype = 5;
11707     
11708               else if (LocaleCompare(value,"6") == 0)
11709                 mng_info->write_png_colortype = 7;
11710             }
11711         }
11712     }
11713
11714   if (mng_info->write_png8)
11715     {
11716       mng_info->write_png_colortype = /* 3 */ 4;
11717       mng_info->write_png_depth = 8;
11718       image->depth = 8;
11719     }
11720
11721   if (mng_info->write_png24)
11722     {
11723       mng_info->write_png_colortype = /* 2 */ 3;
11724       mng_info->write_png_depth = 8;
11725       image->depth = 8;
11726
11727       if (image->alpha_trait == BlendPixelTrait)
11728         (void) SetImageType(image,TrueColorMatteType,exception);
11729
11730       else
11731         (void) SetImageType(image,TrueColorType,exception);
11732
11733       (void) SyncImage(image,exception);
11734     }
11735
11736   if (mng_info->write_png32)
11737     {
11738       mng_info->write_png_colortype = /* 6 */  7;
11739       mng_info->write_png_depth = 8;
11740       image->depth = 8;
11741
11742       if (image->alpha_trait == BlendPixelTrait)
11743         (void) SetImageType(image,TrueColorMatteType,exception);
11744
11745       else
11746         (void) SetImageType(image,TrueColorType,exception);
11747
11748       (void) SyncImage(image,exception);
11749     }
11750
11751   if (mng_info->write_png48)
11752     {
11753       mng_info->write_png_colortype = /* 2 */ 3;
11754       mng_info->write_png_depth = 16;
11755       image->depth = 16;
11756
11757       if (image->alpha_trait == BlendPixelTrait)
11758         (void) SetImageType(image,TrueColorMatteType,exception);
11759
11760       else
11761         (void) SetImageType(image,TrueColorType,exception);
11762
11763       (void) SyncImage(image,exception);
11764     }
11765
11766   if (mng_info->write_png64)
11767     {
11768       mng_info->write_png_colortype = /* 6 */  7;
11769       mng_info->write_png_depth = 16;
11770       image->depth = 16;
11771
11772       if (image->alpha_trait == BlendPixelTrait)
11773         (void) SetImageType(image,TrueColorMatteType,exception);
11774
11775       else
11776         (void) SetImageType(image,TrueColorType,exception);
11777
11778       (void) SyncImage(image,exception);
11779     }
11780
11781   value=GetImageOption(image_info,"png:bit-depth");
11782
11783   if (value != (char *) NULL)
11784     {
11785       if (LocaleCompare(value,"1") == 0)
11786         mng_info->write_png_depth = 1;
11787
11788       else if (LocaleCompare(value,"2") == 0)
11789         mng_info->write_png_depth = 2;
11790
11791       else if (LocaleCompare(value,"4") == 0)
11792         mng_info->write_png_depth = 4;
11793
11794       else if (LocaleCompare(value,"8") == 0)
11795         mng_info->write_png_depth = 8;
11796
11797       else if (LocaleCompare(value,"16") == 0)
11798         mng_info->write_png_depth = 16;
11799
11800       else
11801         (void) ThrowMagickException(exception,
11802              GetMagickModule(),CoderWarning,
11803              "ignoring invalid defined png:bit-depth",
11804              "=%s",value);
11805
11806       if (logging != MagickFalse)
11807         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11808           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11809     }
11810
11811   value=GetImageOption(image_info,"png:color-type");
11812
11813   if (value != (char *) NULL)
11814     {
11815       /* We must store colortype+1 because 0 is a valid colortype */
11816       if (LocaleCompare(value,"0") == 0)
11817         mng_info->write_png_colortype = 1;
11818
11819       else if (LocaleCompare(value,"1") == 0)
11820         mng_info->write_png_colortype = 2;
11821
11822       else if (LocaleCompare(value,"2") == 0)
11823         mng_info->write_png_colortype = 3;
11824
11825       else if (LocaleCompare(value,"3") == 0)
11826         mng_info->write_png_colortype = 4;
11827
11828       else if (LocaleCompare(value,"4") == 0)
11829         mng_info->write_png_colortype = 5;
11830
11831       else if (LocaleCompare(value,"6") == 0)
11832         mng_info->write_png_colortype = 7;
11833
11834       else
11835         (void) ThrowMagickException(exception,
11836              GetMagickModule(),CoderWarning,
11837              "ignoring invalid defined png:color-type",
11838              "=%s",value);
11839
11840       if (logging != MagickFalse)
11841         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11842           "  png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11843     }
11844
11845   /* Check for chunks to be excluded:
11846    *
11847    * The default is to not exclude any known chunks except for any
11848    * listed in the "unused_chunks" array, above.
11849    *
11850    * Chunks can be listed for exclusion via a "png:exclude-chunk"
11851    * define (in the image properties or in the image artifacts)
11852    * or via a mng_info member.  For convenience, in addition
11853    * to or instead of a comma-separated list of chunks, the
11854    * "exclude-chunk" string can be simply "all" or "none".
11855    *
11856    * The exclude-chunk define takes priority over the mng_info.
11857    *
11858    * A "png:include-chunk" define takes  priority over both the
11859    * mng_info and the "png:exclude-chunk" define.  Like the
11860    * "exclude-chunk" string, it can define "all" or "none" as
11861    * well as a comma-separated list.  Chunks that are unknown to
11862    * ImageMagick are always excluded, regardless of their "copy-safe"
11863    * status according to the PNG specification, and even if they
11864    * appear in the "include-chunk" list. Such defines appearing among
11865    * the image options take priority over those found among the image
11866    * artifacts.
11867    *
11868    * Finally, all chunks listed in the "unused_chunks" array are
11869    * automatically excluded, regardless of the other instructions
11870    * or lack thereof.
11871    *
11872    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11873    * will not be written and the gAMA chunk will only be written if it
11874    * is not between .45 and .46, or approximately (1.0/2.2).
11875    *
11876    * If you exclude tRNS and the image has transparency, the colortype
11877    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11878    *
11879    * The -strip option causes StripImage() to set the png:include-chunk
11880    * artifact to "none,trns,gama".
11881    */
11882
11883   mng_info->ping_exclude_bKGD=MagickFalse;
11884   mng_info->ping_exclude_cHRM=MagickFalse;
11885   mng_info->ping_exclude_date=MagickFalse;
11886   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11887   mng_info->ping_exclude_gAMA=MagickFalse;
11888   mng_info->ping_exclude_iCCP=MagickFalse;
11889   /* mng_info->ping_exclude_iTXt=MagickFalse; */
11890   mng_info->ping_exclude_oFFs=MagickFalse;
11891   mng_info->ping_exclude_pHYs=MagickFalse;
11892   mng_info->ping_exclude_sRGB=MagickFalse;
11893   mng_info->ping_exclude_tEXt=MagickFalse;
11894   mng_info->ping_exclude_tRNS=MagickFalse;
11895   mng_info->ping_exclude_vpAg=MagickFalse;
11896   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11897   mng_info->ping_exclude_zTXt=MagickFalse;
11898
11899   mng_info->ping_preserve_colormap=MagickFalse;
11900
11901   value=GetImageOption(image_info,"png:preserve-colormap");
11902   if (value == NULL)
11903      value=GetImageArtifact(image,"png:preserve-colormap");
11904   if (value != NULL)
11905      mng_info->ping_preserve_colormap=MagickTrue;
11906
11907   mng_info->ping_preserve_iCCP=MagickFalse;
11908
11909   value=GetImageOption(image_info,"png:preserve-iCCP");
11910   if (value == NULL)
11911      value=GetImageArtifact(image,"png:preserve-iCCP");
11912   if (value != NULL)
11913      mng_info->ping_preserve_iCCP=MagickTrue;
11914
11915   /* These compression-level, compression-strategy, and compression-filter
11916    * defines take precedence over values from the -quality option.
11917    */
11918   value=GetImageOption(image_info,"png:compression-level");
11919   if (value == NULL)
11920      value=GetImageArtifact(image,"png:compression-level");
11921   if (value != NULL)
11922   {
11923       /* We have to add 1 to everything because 0 is a valid input,
11924        * and we want to use 0 (the default) to mean undefined.
11925        */
11926       if (LocaleCompare(value,"0") == 0)
11927         mng_info->write_png_compression_level = 1;
11928
11929       else if (LocaleCompare(value,"1") == 0)
11930         mng_info->write_png_compression_level = 2;
11931
11932       else if (LocaleCompare(value,"2") == 0)
11933         mng_info->write_png_compression_level = 3;
11934
11935       else if (LocaleCompare(value,"3") == 0)
11936         mng_info->write_png_compression_level = 4;
11937
11938       else if (LocaleCompare(value,"4") == 0)
11939         mng_info->write_png_compression_level = 5;
11940
11941       else if (LocaleCompare(value,"5") == 0)
11942         mng_info->write_png_compression_level = 6;
11943
11944       else if (LocaleCompare(value,"6") == 0)
11945         mng_info->write_png_compression_level = 7;
11946
11947       else if (LocaleCompare(value,"7") == 0)
11948         mng_info->write_png_compression_level = 8;
11949
11950       else if (LocaleCompare(value,"8") == 0)
11951         mng_info->write_png_compression_level = 9;
11952
11953       else if (LocaleCompare(value,"9") == 0)
11954         mng_info->write_png_compression_level = 10;
11955
11956       else
11957         (void) ThrowMagickException(exception,
11958              GetMagickModule(),CoderWarning,
11959              "ignoring invalid defined png:compression-level",
11960              "=%s",value);
11961     }
11962
11963   value=GetImageOption(image_info,"png:compression-strategy");
11964   if (value == NULL)
11965      value=GetImageArtifact(image,"png:compression-strategy");
11966   if (value != NULL)
11967   {
11968
11969       if (LocaleCompare(value,"0") == 0)
11970         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11971
11972       else if (LocaleCompare(value,"1") == 0)
11973         mng_info->write_png_compression_strategy = Z_FILTERED+1;
11974
11975       else if (LocaleCompare(value,"2") == 0)
11976         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11977
11978       else if (LocaleCompare(value,"3") == 0)
11979 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
11980         mng_info->write_png_compression_strategy = Z_RLE+1;
11981 #else
11982         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11983 #endif
11984
11985       else if (LocaleCompare(value,"4") == 0)
11986 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
11987         mng_info->write_png_compression_strategy = Z_FIXED+1;
11988 #else
11989         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11990 #endif
11991
11992       else
11993         (void) ThrowMagickException(exception,
11994              GetMagickModule(),CoderWarning,
11995              "ignoring invalid defined png:compression-strategy",
11996              "=%s",value);
11997     }
11998
11999   value=GetImageOption(image_info,"png:compression-filter");
12000   if (value == NULL)
12001      value=GetImageArtifact(image,"png:compression-filter");
12002   if (value != NULL)
12003   {
12004
12005       /* To do: combinations of filters allowed by libpng
12006        * masks 0x08 through 0xf8
12007        *
12008        * Implement this as a comma-separated list of 0,1,2,3,4,5
12009        * where 5 is a special case meaning PNG_ALL_FILTERS.
12010        */
12011
12012       if (LocaleCompare(value,"0") == 0)
12013         mng_info->write_png_compression_filter = 1;
12014
12015       else if (LocaleCompare(value,"1") == 0)
12016         mng_info->write_png_compression_filter = 2;
12017
12018       else if (LocaleCompare(value,"2") == 0)
12019         mng_info->write_png_compression_filter = 3;
12020
12021       else if (LocaleCompare(value,"3") == 0)
12022         mng_info->write_png_compression_filter = 4;
12023
12024       else if (LocaleCompare(value,"4") == 0)
12025         mng_info->write_png_compression_filter = 5;
12026
12027       else if (LocaleCompare(value,"5") == 0)
12028         mng_info->write_png_compression_filter = 6;
12029
12030       else
12031         (void) ThrowMagickException(exception,
12032              GetMagickModule(),CoderWarning,
12033              "ignoring invalid defined png:compression-filter",
12034              "=%s",value);
12035     }
12036
12037   excluding=MagickFalse;
12038
12039   for (source=0; source<1; source++)
12040   {
12041     if (source==0)
12042       {
12043        value=GetImageOption(image_info,"png:exclude-chunk");
12044
12045        if (value == NULL)
12046          value=GetImageArtifact(image,"png:exclude-chunks");
12047       }
12048     else
12049       {
12050        value=GetImageOption(image_info,"png:exclude-chunk");
12051
12052        if (value == NULL)
12053          value=GetImageArtifact(image,"png:exclude-chunks");
12054       }
12055
12056     if (value != NULL)
12057     {
12058
12059     size_t
12060       last;
12061
12062     excluding=MagickTrue;
12063
12064     if (logging != MagickFalse)
12065       {
12066         if (source == 0)
12067            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12068               "  png:exclude-chunk=%s found in image artifacts.\n", value);
12069         else
12070            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12071               "  png:exclude-chunk=%s found in image properties.\n", value);
12072       }
12073
12074     last=strlen(value);
12075
12076     for (i=0; i<(int) last; i+=5)
12077     {
12078
12079       if (LocaleNCompare(value+i,"none",4) == 0)
12080       {
12081         mng_info->ping_exclude_bKGD=MagickFalse;
12082         mng_info->ping_exclude_cHRM=MagickFalse;
12083         mng_info->ping_exclude_date=MagickFalse;
12084         mng_info->ping_exclude_EXIF=MagickFalse;
12085         mng_info->ping_exclude_gAMA=MagickFalse;
12086         mng_info->ping_exclude_iCCP=MagickFalse;
12087         /* mng_info->ping_exclude_iTXt=MagickFalse; */
12088         mng_info->ping_exclude_oFFs=MagickFalse;
12089         mng_info->ping_exclude_pHYs=MagickFalse;
12090         mng_info->ping_exclude_sRGB=MagickFalse;
12091         mng_info->ping_exclude_tEXt=MagickFalse;
12092         mng_info->ping_exclude_tRNS=MagickFalse;
12093         mng_info->ping_exclude_vpAg=MagickFalse;
12094         mng_info->ping_exclude_zCCP=MagickFalse;
12095         mng_info->ping_exclude_zTXt=MagickFalse;
12096       }
12097
12098       if (LocaleNCompare(value+i,"bkgd",4) == 0)
12099         mng_info->ping_exclude_bKGD=MagickTrue;
12100
12101       if (LocaleNCompare(value+i,"chrm",4) == 0)
12102         mng_info->ping_exclude_cHRM=MagickTrue;
12103
12104       if (LocaleNCompare(value+i,"date",4) == 0)
12105         mng_info->ping_exclude_date=MagickTrue;
12106
12107       if (LocaleNCompare(value+i,"exif",4) == 0)
12108         mng_info->ping_exclude_EXIF=MagickTrue;
12109
12110       if (LocaleNCompare(value+i,"gama",4) == 0)
12111         mng_info->ping_exclude_gAMA=MagickTrue;
12112
12113       if (LocaleNCompare(value+i,"iccp",4) == 0)
12114         mng_info->ping_exclude_iCCP=MagickTrue;
12115
12116     /*
12117       if (LocaleNCompare(value+i,"itxt",4) == 0)
12118         mng_info->ping_exclude_iTXt=MagickTrue;
12119      */
12120
12121       if (LocaleNCompare(value+i,"gama",4) == 0)
12122         mng_info->ping_exclude_gAMA=MagickTrue;
12123
12124       if (LocaleNCompare(value+i,"offs",4) == 0)
12125         mng_info->ping_exclude_oFFs=MagickTrue;
12126
12127       if (LocaleNCompare(value+i,"phys",4) == 0)
12128         mng_info->ping_exclude_pHYs=MagickTrue;
12129
12130       if (LocaleNCompare(value+i,"srgb",4) == 0)
12131         mng_info->ping_exclude_sRGB=MagickTrue;
12132
12133       if (LocaleNCompare(value+i,"text",4) == 0)
12134         mng_info->ping_exclude_tEXt=MagickTrue;
12135
12136       if (LocaleNCompare(value+i,"trns",4) == 0)
12137         mng_info->ping_exclude_tRNS=MagickTrue;
12138
12139       if (LocaleNCompare(value+i,"vpag",4) == 0)
12140         mng_info->ping_exclude_vpAg=MagickTrue;
12141
12142       if (LocaleNCompare(value+i,"zccp",4) == 0)
12143         mng_info->ping_exclude_zCCP=MagickTrue;
12144
12145       if (LocaleNCompare(value+i,"ztxt",4) == 0)
12146         mng_info->ping_exclude_zTXt=MagickTrue;
12147
12148       if (LocaleNCompare(value+i,"all",3) == 0)
12149       {
12150         mng_info->ping_exclude_bKGD=MagickTrue;
12151         mng_info->ping_exclude_cHRM=MagickTrue;
12152         mng_info->ping_exclude_date=MagickTrue;
12153         mng_info->ping_exclude_EXIF=MagickTrue;
12154         mng_info->ping_exclude_gAMA=MagickTrue;
12155         mng_info->ping_exclude_iCCP=MagickTrue;
12156         /* mng_info->ping_exclude_iTXt=MagickTrue; */
12157         mng_info->ping_exclude_oFFs=MagickTrue;
12158         mng_info->ping_exclude_pHYs=MagickTrue;
12159         mng_info->ping_exclude_sRGB=MagickTrue;
12160         mng_info->ping_exclude_tEXt=MagickTrue;
12161         mng_info->ping_exclude_tRNS=MagickTrue;
12162         mng_info->ping_exclude_vpAg=MagickTrue;
12163         mng_info->ping_exclude_zCCP=MagickTrue;
12164         mng_info->ping_exclude_zTXt=MagickTrue;
12165         i--;
12166       }
12167       }
12168     }
12169   }
12170
12171   for (source=0; source<1; source++)
12172   {
12173     if (source==0)
12174       {
12175        value=GetImageOption(image_info,"png:include-chunk");
12176
12177        if (value == NULL)
12178          value=GetImageArtifact(image,"png:include-chunks");
12179       }
12180     else
12181       {
12182        value=GetImageOption(image_info,"png:include-chunk");
12183
12184        if (value == NULL)
12185          value=GetImageArtifact(image,"png:include-chunks");
12186       }
12187
12188     if (value != NULL)
12189     {
12190     size_t
12191       last;
12192
12193     excluding=MagickTrue;
12194
12195     if (logging != MagickFalse)
12196       {
12197         if (source == 0)
12198            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12199               "  png:include-chunk=%s found in image artifacts.\n", value);
12200         else
12201            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12202               "  png:include-chunk=%s found in image properties.\n", value);
12203       }
12204
12205     last=strlen(value);
12206
12207     for (i=0; i<(int) last; i+=5)
12208       {
12209       if (LocaleNCompare(value+i,"none",4) == 0)
12210         {
12211           mng_info->ping_exclude_bKGD=MagickTrue;
12212           mng_info->ping_exclude_cHRM=MagickTrue;
12213           mng_info->ping_exclude_date=MagickTrue;
12214           mng_info->ping_exclude_EXIF=MagickTrue;
12215           mng_info->ping_exclude_gAMA=MagickTrue;
12216           mng_info->ping_exclude_iCCP=MagickTrue;
12217           /* mng_info->ping_exclude_iTXt=MagickTrue; */
12218           mng_info->ping_exclude_oFFs=MagickTrue;
12219           mng_info->ping_exclude_pHYs=MagickTrue;
12220           mng_info->ping_exclude_sRGB=MagickTrue;
12221           mng_info->ping_exclude_tEXt=MagickTrue;
12222           mng_info->ping_exclude_tRNS=MagickTrue;
12223           mng_info->ping_exclude_vpAg=MagickTrue;
12224           mng_info->ping_exclude_zCCP=MagickTrue;
12225           mng_info->ping_exclude_zTXt=MagickTrue;
12226         }
12227
12228       if (LocaleNCompare(value+i,"bkgd",4) == 0)
12229         mng_info->ping_exclude_bKGD=MagickFalse;
12230
12231       if (LocaleNCompare(value+i,"chrm",4) == 0)
12232         mng_info->ping_exclude_cHRM=MagickFalse;
12233
12234       if (LocaleNCompare(value+i,"date",4) == 0)
12235         mng_info->ping_exclude_date=MagickFalse;
12236
12237       if (LocaleNCompare(value+i,"exif",4) == 0)
12238         mng_info->ping_exclude_EXIF=MagickFalse;
12239
12240       if (LocaleNCompare(value+i,"gama",4) == 0)
12241         mng_info->ping_exclude_gAMA=MagickFalse;
12242
12243       if (LocaleNCompare(value+i,"iccp",4) == 0)
12244         mng_info->ping_exclude_iCCP=MagickFalse;
12245
12246     /*
12247       if (LocaleNCompare(value+i,"itxt",4) == 0)
12248         mng_info->ping_exclude_iTXt=MagickFalse;
12249      */
12250
12251       if (LocaleNCompare(value+i,"gama",4) == 0)
12252         mng_info->ping_exclude_gAMA=MagickFalse;
12253
12254       if (LocaleNCompare(value+i,"offs",4) == 0)
12255         mng_info->ping_exclude_oFFs=MagickFalse;
12256
12257       if (LocaleNCompare(value+i,"phys",4) == 0)
12258         mng_info->ping_exclude_pHYs=MagickFalse;
12259
12260       if (LocaleNCompare(value+i,"srgb",4) == 0)
12261         mng_info->ping_exclude_sRGB=MagickFalse;
12262
12263       if (LocaleNCompare(value+i,"text",4) == 0)
12264         mng_info->ping_exclude_tEXt=MagickFalse;
12265
12266       if (LocaleNCompare(value+i,"trns",4) == 0)
12267         mng_info->ping_exclude_tRNS=MagickFalse;
12268
12269       if (LocaleNCompare(value+i,"vpag",4) == 0)
12270         mng_info->ping_exclude_vpAg=MagickFalse;
12271
12272       if (LocaleNCompare(value+i,"zccp",4) == 0)
12273         mng_info->ping_exclude_zCCP=MagickFalse;
12274
12275       if (LocaleNCompare(value+i,"ztxt",4) == 0)
12276         mng_info->ping_exclude_zTXt=MagickFalse;
12277
12278       if (LocaleNCompare(value+i,"all",3) == 0)
12279         {
12280           mng_info->ping_exclude_bKGD=MagickFalse;
12281           mng_info->ping_exclude_cHRM=MagickFalse;
12282           mng_info->ping_exclude_date=MagickFalse;
12283           mng_info->ping_exclude_EXIF=MagickFalse;
12284           mng_info->ping_exclude_gAMA=MagickFalse;
12285           mng_info->ping_exclude_iCCP=MagickFalse;
12286           /* mng_info->ping_exclude_iTXt=MagickFalse; */
12287           mng_info->ping_exclude_oFFs=MagickFalse;
12288           mng_info->ping_exclude_pHYs=MagickFalse;
12289           mng_info->ping_exclude_sRGB=MagickFalse;
12290           mng_info->ping_exclude_tEXt=MagickFalse;
12291           mng_info->ping_exclude_tRNS=MagickFalse;
12292           mng_info->ping_exclude_vpAg=MagickFalse;
12293           mng_info->ping_exclude_zCCP=MagickFalse;
12294           mng_info->ping_exclude_zTXt=MagickFalse;
12295           i--;
12296         }
12297       }
12298     }
12299   }
12300
12301   if (excluding != MagickFalse && logging != MagickFalse)
12302   {
12303     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12304       "  Chunks to be excluded from the output png:");
12305     if (mng_info->ping_exclude_bKGD != MagickFalse)
12306       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12307           "    bKGD");
12308     if (mng_info->ping_exclude_cHRM != MagickFalse)
12309       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12310           "    cHRM");
12311     if (mng_info->ping_exclude_date != MagickFalse)
12312       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12313           "    date");
12314     if (mng_info->ping_exclude_EXIF != MagickFalse)
12315       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12316           "    EXIF");
12317     if (mng_info->ping_exclude_gAMA != MagickFalse)
12318       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12319           "    gAMA");
12320     if (mng_info->ping_exclude_iCCP != MagickFalse)
12321       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12322           "    iCCP");
12323 /*
12324     if (mng_info->ping_exclude_iTXt != MagickFalse)
12325       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12326           "    iTXt");
12327 */
12328     if (mng_info->ping_exclude_oFFs != MagickFalse)
12329       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12330           "    oFFs");
12331     if (mng_info->ping_exclude_pHYs != MagickFalse)
12332       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12333           "    pHYs");
12334     if (mng_info->ping_exclude_sRGB != MagickFalse)
12335       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12336           "    sRGB");
12337     if (mng_info->ping_exclude_tEXt != MagickFalse)
12338       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12339           "    tEXt");
12340     if (mng_info->ping_exclude_tRNS != MagickFalse)
12341       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12342           "    tRNS");
12343     if (mng_info->ping_exclude_vpAg != MagickFalse)
12344       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12345           "    vpAg");
12346     if (mng_info->ping_exclude_zCCP != MagickFalse)
12347       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12348           "    zCCP");
12349     if (mng_info->ping_exclude_zTXt != MagickFalse)
12350       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12351           "    zTXt");
12352   }
12353
12354   mng_info->need_blob = MagickTrue;
12355
12356   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12357
12358   MngInfoFreeStruct(mng_info,&have_mng_structure);
12359
12360   if (logging != MagickFalse)
12361     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12362
12363   return(status);
12364 }
12365
12366 #if defined(JNG_SUPPORTED)
12367
12368 /* Write one JNG image */
12369 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12370    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12371 {
12372   Image
12373     *jpeg_image;
12374
12375   ImageInfo
12376     *jpeg_image_info;
12377
12378   MagickBooleanType
12379     logging,
12380     status;
12381
12382   size_t
12383     length;
12384
12385   unsigned char
12386     *blob,
12387     chunk[80],
12388     *p;
12389
12390   unsigned int
12391     jng_alpha_compression_method,
12392     jng_alpha_sample_depth,
12393     jng_color_type,
12394     transparent;
12395
12396   size_t
12397     jng_alpha_quality,
12398     jng_quality;
12399
12400   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12401     "  Enter WriteOneJNGImage()");
12402
12403   blob=(unsigned char *) NULL;
12404   jpeg_image=(Image *) NULL;
12405   jpeg_image_info=(ImageInfo *) NULL;
12406
12407   status=MagickTrue;
12408   transparent=image_info->type==GrayscaleMatteType ||
12409      image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
12410
12411   jng_alpha_sample_depth = 0;
12412
12413   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12414
12415   jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12416
12417   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12418       image_info->quality;
12419
12420   if (jng_alpha_quality >= 1000)
12421     jng_alpha_quality /= 1000;
12422
12423   if (transparent)
12424     {
12425       jng_color_type=14;
12426
12427       /* Create JPEG blob, image, and image_info */
12428       if (logging != MagickFalse)
12429         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12430           "  Creating jpeg_image_info for alpha.");
12431
12432       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12433
12434       if (jpeg_image_info == (ImageInfo *) NULL)
12435         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12436
12437       if (logging != MagickFalse)
12438         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12439           "  Creating jpeg_image.");
12440
12441       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12442       if (jpeg_image == (Image *) NULL)
12443         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12444       (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12445       jpeg_image->alpha_trait=UndefinedPixelTrait;
12446       jpeg_image->quality=jng_alpha_quality;
12447       jpeg_image_info->type=GrayscaleType;
12448       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12449       (void) AcquireUniqueFilename(jpeg_image->filename);
12450       (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
12451         "%s",jpeg_image->filename);
12452     }
12453   else
12454     {
12455       jng_alpha_compression_method=0;
12456       jng_color_type=10;
12457       jng_alpha_sample_depth=0;
12458     }
12459
12460   /* To do: check bit depth of PNG alpha channel */
12461
12462   /* Check if image is grayscale. */
12463   if (image_info->type != TrueColorMatteType && image_info->type !=
12464     TrueColorType && IsImageGray(image,exception))
12465     jng_color_type-=2;
12466
12467   if (logging != MagickFalse)
12468     {
12469         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12470           "    JNG Quality           = %d",(int) jng_quality);
12471         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12472           "    JNG Color Type        = %d",jng_color_type);
12473         if (transparent)
12474           {
12475             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12476               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12477             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12478               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12479             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12480               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12481           }
12482     }
12483
12484   if (transparent)
12485     {
12486       if (jng_alpha_compression_method==0)
12487         {
12488           const char
12489             *value;
12490
12491           /* Encode alpha as a grayscale PNG blob */
12492           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12493             exception);
12494           if (logging != MagickFalse)
12495             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12496               "  Creating PNG blob.");
12497           length=0;
12498
12499           (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12500           (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12501           jpeg_image_info->interlace=NoInterlace;
12502
12503           /* Exclude all ancillary chunks */
12504           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12505
12506           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12507             exception);
12508
12509           /* Retrieve sample depth used */
12510           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12511           if (value != (char *) NULL)
12512             jng_alpha_sample_depth= (unsigned int) value[0];
12513         }
12514       else
12515         {
12516           /* Encode alpha as a grayscale JPEG blob */
12517
12518           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12519             exception);
12520
12521           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12522           (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12523           jpeg_image_info->interlace=NoInterlace;
12524           if (logging != MagickFalse)
12525             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12526               "  Creating blob.");
12527           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12528            exception);
12529           jng_alpha_sample_depth=8;
12530
12531           if (logging != MagickFalse)
12532             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12533               "  Successfully read jpeg_image into a blob, length=%.20g.",
12534               (double) length);
12535
12536         }
12537       /* Destroy JPEG image and image_info */
12538       jpeg_image=DestroyImage(jpeg_image);
12539       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12540       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12541     }
12542
12543   /* Write JHDR chunk */
12544   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12545   PNGType(chunk,mng_JHDR);
12546   LogPNGChunk(logging,mng_JHDR,16L);
12547   PNGLong(chunk+4,(png_uint_32) image->columns);
12548   PNGLong(chunk+8,(png_uint_32) image->rows);
12549   chunk[12]=jng_color_type;
12550   chunk[13]=8;  /* sample depth */
12551   chunk[14]=8; /*jng_image_compression_method */
12552   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12553   chunk[16]=jng_alpha_sample_depth;
12554   chunk[17]=jng_alpha_compression_method;
12555   chunk[18]=0; /*jng_alpha_filter_method */
12556   chunk[19]=0; /*jng_alpha_interlace_method */
12557   (void) WriteBlob(image,20,chunk);
12558   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12559   if (logging != MagickFalse)
12560     {
12561       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12562         "    JNG width:%15lu",(unsigned long) image->columns);
12563
12564       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12565         "    JNG height:%14lu",(unsigned long) image->rows);
12566
12567       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12568         "    JNG color type:%10d",jng_color_type);
12569
12570       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12571         "    JNG sample depth:%8d",8);
12572
12573       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12574         "    JNG compression:%9d",8);
12575
12576       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12577         "    JNG interlace:%11d",0);
12578
12579       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12580         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
12581
12582       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12583         "    JNG alpha compression:%3d",jng_alpha_compression_method);
12584
12585       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12586         "    JNG alpha filter:%8d",0);
12587
12588       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12589         "    JNG alpha interlace:%5d",0);
12590     }
12591
12592   /* Write any JNG-chunk-b profiles */
12593   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12594
12595   /*
12596      Write leading ancillary chunks
12597   */
12598
12599   if (transparent)
12600   {
12601     /*
12602       Write JNG bKGD chunk
12603     */
12604
12605     unsigned char
12606       blue,
12607       green,
12608       red;
12609
12610     ssize_t
12611       num_bytes;
12612
12613     if (jng_color_type == 8 || jng_color_type == 12)
12614       num_bytes=6L;
12615     else
12616       num_bytes=10L;
12617     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12618     PNGType(chunk,mng_bKGD);
12619     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12620     red=ScaleQuantumToChar(image->background_color.red);
12621     green=ScaleQuantumToChar(image->background_color.green);
12622     blue=ScaleQuantumToChar(image->background_color.blue);
12623     *(chunk+4)=0;
12624     *(chunk+5)=red;
12625     *(chunk+6)=0;
12626     *(chunk+7)=green;
12627     *(chunk+8)=0;
12628     *(chunk+9)=blue;
12629     (void) WriteBlob(image,(size_t) num_bytes,chunk);
12630     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12631   }
12632
12633   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12634     {
12635       /*
12636         Write JNG sRGB chunk
12637       */
12638       (void) WriteBlobMSBULong(image,1L);
12639       PNGType(chunk,mng_sRGB);
12640       LogPNGChunk(logging,mng_sRGB,1L);
12641
12642       if (image->rendering_intent != UndefinedIntent)
12643         chunk[4]=(unsigned char)
12644           Magick_RenderingIntent_to_PNG_RenderingIntent(
12645           (image->rendering_intent));
12646
12647       else
12648         chunk[4]=(unsigned char)
12649           Magick_RenderingIntent_to_PNG_RenderingIntent(
12650           (PerceptualIntent));
12651
12652       (void) WriteBlob(image,5,chunk);
12653       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12654     }
12655   else
12656     {
12657       if (image->gamma != 0.0)
12658         {
12659           /*
12660              Write JNG gAMA chunk
12661           */
12662           (void) WriteBlobMSBULong(image,4L);
12663           PNGType(chunk,mng_gAMA);
12664           LogPNGChunk(logging,mng_gAMA,4L);
12665           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12666           (void) WriteBlob(image,8,chunk);
12667           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12668         }
12669
12670       if ((mng_info->equal_chrms == MagickFalse) &&
12671           (image->chromaticity.red_primary.x != 0.0))
12672         {
12673           PrimaryInfo
12674             primary;
12675
12676           /*
12677              Write JNG cHRM chunk
12678           */
12679           (void) WriteBlobMSBULong(image,32L);
12680           PNGType(chunk,mng_cHRM);
12681           LogPNGChunk(logging,mng_cHRM,32L);
12682           primary=image->chromaticity.white_point;
12683           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12684           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12685           primary=image->chromaticity.red_primary;
12686           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12687           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12688           primary=image->chromaticity.green_primary;
12689           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12690           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12691           primary=image->chromaticity.blue_primary;
12692           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12693           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12694           (void) WriteBlob(image,36,chunk);
12695           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12696         }
12697     }
12698
12699   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12700     {
12701       /*
12702          Write JNG pHYs chunk
12703       */
12704       (void) WriteBlobMSBULong(image,9L);
12705       PNGType(chunk,mng_pHYs);
12706       LogPNGChunk(logging,mng_pHYs,9L);
12707       if (image->units == PixelsPerInchResolution)
12708         {
12709           PNGLong(chunk+4,(png_uint_32)
12710             (image->resolution.x*100.0/2.54+0.5));
12711
12712           PNGLong(chunk+8,(png_uint_32)
12713             (image->resolution.y*100.0/2.54+0.5));
12714
12715           chunk[12]=1;
12716         }
12717
12718       else
12719         {
12720           if (image->units == PixelsPerCentimeterResolution)
12721             {
12722               PNGLong(chunk+4,(png_uint_32)
12723                 (image->resolution.x*100.0+0.5));
12724
12725               PNGLong(chunk+8,(png_uint_32)
12726                 (image->resolution.y*100.0+0.5));
12727
12728               chunk[12]=1;
12729             }
12730
12731           else
12732             {
12733               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12734               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12735               chunk[12]=0;
12736             }
12737         }
12738       (void) WriteBlob(image,13,chunk);
12739       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12740     }
12741
12742   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12743     {
12744       /*
12745          Write JNG oFFs chunk
12746       */
12747       (void) WriteBlobMSBULong(image,9L);
12748       PNGType(chunk,mng_oFFs);
12749       LogPNGChunk(logging,mng_oFFs,9L);
12750       PNGsLong(chunk+4,(ssize_t) (image->page.x));
12751       PNGsLong(chunk+8,(ssize_t) (image->page.y));
12752       chunk[12]=0;
12753       (void) WriteBlob(image,13,chunk);
12754       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12755     }
12756   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12757     {
12758        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
12759        PNGType(chunk,mng_vpAg);
12760        LogPNGChunk(logging,mng_vpAg,9L);
12761        PNGLong(chunk+4,(png_uint_32) image->page.width);
12762        PNGLong(chunk+8,(png_uint_32) image->page.height);
12763        chunk[12]=0;   /* unit = pixels */
12764        (void) WriteBlob(image,13,chunk);
12765        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12766     }
12767
12768
12769   if (transparent)
12770     {
12771       if (jng_alpha_compression_method==0)
12772         {
12773           register ssize_t
12774             i;
12775
12776           ssize_t
12777             len;
12778
12779           /* Write IDAT chunk header */
12780           if (logging != MagickFalse)
12781             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12782               "  Write IDAT chunks from blob, length=%.20g.",(double)
12783               length);
12784
12785           /* Copy IDAT chunks */
12786           len=0;
12787           p=blob+8;
12788           for (i=8; i<(ssize_t) length; i+=len+12)
12789           {
12790             len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12791             p+=4;
12792
12793             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12794               {
12795                 /* Found an IDAT chunk. */
12796                 (void) WriteBlobMSBULong(image,(size_t) len);
12797                 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12798                 (void) WriteBlob(image,(size_t) len+4,p);
12799                 (void) WriteBlobMSBULong(image,
12800                     crc32(0,p,(uInt) len+4));
12801               }
12802
12803             else
12804               {
12805                 if (logging != MagickFalse)
12806                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12807                     "    Skipping %c%c%c%c chunk, length=%.20g.",
12808                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
12809               }
12810             p+=(8+len);
12811           }
12812         }
12813       else
12814         {
12815           /* Write JDAA chunk header */
12816           if (logging != MagickFalse)
12817             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12818               "  Write JDAA chunk, length=%.20g.",(double) length);
12819           (void) WriteBlobMSBULong(image,(size_t) length);
12820           PNGType(chunk,mng_JDAA);
12821           LogPNGChunk(logging,mng_JDAA,length);
12822           /* Write JDAT chunk(s) data */
12823           (void) WriteBlob(image,4,chunk);
12824           (void) WriteBlob(image,length,blob);
12825           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12826              (uInt) length));
12827         }
12828       blob=(unsigned char *) RelinquishMagickMemory(blob);
12829     }
12830
12831   /* Encode image as a JPEG blob */
12832   if (logging != MagickFalse)
12833     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12834       "  Creating jpeg_image_info.");
12835   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12836   if (jpeg_image_info == (ImageInfo *) NULL)
12837     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12838
12839   if (logging != MagickFalse)
12840     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12841       "  Creating jpeg_image.");
12842
12843   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12844   if (jpeg_image == (Image *) NULL)
12845     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12846   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12847
12848   (void) AcquireUniqueFilename(jpeg_image->filename);
12849   (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12850     jpeg_image->filename);
12851
12852   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12853     exception);
12854
12855   if (logging != MagickFalse)
12856     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12857       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12858       (double) jpeg_image->rows);
12859
12860   if (jng_color_type == 8 || jng_color_type == 12)
12861     jpeg_image_info->type=GrayscaleType;
12862
12863   jpeg_image_info->quality=jng_quality;
12864   jpeg_image->quality=jng_quality;
12865   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12866   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12867
12868   if (logging != MagickFalse)
12869     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12870       "  Creating blob.");
12871
12872   blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12873
12874   if (logging != MagickFalse)
12875     {
12876       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12877         "  Successfully read jpeg_image into a blob, length=%.20g.",
12878         (double) length);
12879
12880       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12881         "  Write JDAT chunk, length=%.20g.",(double) length);
12882     }
12883
12884   /* Write JDAT chunk(s) */
12885   (void) WriteBlobMSBULong(image,(size_t) length);
12886   PNGType(chunk,mng_JDAT);
12887   LogPNGChunk(logging,mng_JDAT,length);
12888   (void) WriteBlob(image,4,chunk);
12889   (void) WriteBlob(image,length,blob);
12890   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12891
12892   jpeg_image=DestroyImage(jpeg_image);
12893   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12894   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12895   blob=(unsigned char *) RelinquishMagickMemory(blob);
12896
12897   /* Write any JNG-chunk-e profiles */
12898   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12899
12900   /* Write IEND chunk */
12901   (void) WriteBlobMSBULong(image,0L);
12902   PNGType(chunk,mng_IEND);
12903   LogPNGChunk(logging,mng_IEND,0);
12904   (void) WriteBlob(image,4,chunk);
12905   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12906
12907   if (logging != MagickFalse)
12908     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12909       "  exit WriteOneJNGImage()");
12910
12911   return(status);
12912 }
12913
12914
12915 /*
12916 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12917 %                                                                             %
12918 %                                                                             %
12919 %                                                                             %
12920 %   W r i t e J N G I m a g e                                                 %
12921 %                                                                             %
12922 %                                                                             %
12923 %                                                                             %
12924 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12925 %
12926 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12927 %
12928 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
12929 %
12930 %  The format of the WriteJNGImage method is:
12931 %
12932 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12933 %        Image *image,ExceptionInfo *exception)
12934 %
12935 %  A description of each parameter follows:
12936 %
12937 %    o image_info: the image info.
12938 %
12939 %    o image:  The image.
12940 %
12941 %    o exception: return any errors or warnings in this structure.
12942 %
12943 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12944 */
12945 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12946   ExceptionInfo *exception)
12947 {
12948   MagickBooleanType
12949     have_mng_structure,
12950     logging,
12951     status;
12952
12953   MngInfo
12954     *mng_info;
12955
12956   /*
12957     Open image file.
12958   */
12959   assert(image_info != (const ImageInfo *) NULL);
12960   assert(image_info->signature == MagickSignature);
12961   assert(image != (Image *) NULL);
12962   assert(image->signature == MagickSignature);
12963   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12964   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12965   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12966   if (status == MagickFalse)
12967     return(status);
12968
12969   /*
12970     Allocate a MngInfo structure.
12971   */
12972   have_mng_structure=MagickFalse;
12973   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12974   if (mng_info == (MngInfo *) NULL)
12975     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12976   /*
12977     Initialize members of the MngInfo structure.
12978   */
12979   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12980   mng_info->image=image;
12981   have_mng_structure=MagickTrue;
12982
12983   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12984
12985   status=WriteOneJNGImage(mng_info,image_info,image,exception);
12986   (void) CloseBlob(image);
12987
12988   (void) CatchImageException(image);
12989   MngInfoFreeStruct(mng_info,&have_mng_structure);
12990   if (logging != MagickFalse)
12991     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12992   return(status);
12993 }
12994 #endif
12995
12996 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12997   ExceptionInfo *exception)
12998 {
12999   const char
13000     *option;
13001
13002   Image
13003     *next_image;
13004
13005   MagickBooleanType
13006     have_mng_structure,
13007     status;
13008
13009   volatile MagickBooleanType
13010     logging;
13011
13012   MngInfo
13013     *mng_info;
13014
13015   int
13016     image_count,
13017     need_iterations,
13018     need_matte;
13019
13020   volatile int
13021 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13022     defined(PNG_MNG_FEATURES_SUPPORTED)
13023     need_local_plte,
13024 #endif
13025     all_images_are_gray,
13026     need_defi,
13027     use_global_plte;
13028
13029   register ssize_t
13030     i;
13031
13032   unsigned char
13033     chunk[800];
13034
13035   volatile unsigned int
13036     write_jng,
13037     write_mng;
13038
13039   volatile size_t
13040     scene;
13041
13042   size_t
13043     final_delay=0,
13044     initial_delay;
13045
13046 #if (PNG_LIBPNG_VER < 10200)
13047     if (image_info->verbose)
13048       printf("Your PNG library (libpng-%s) is rather old.\n",
13049          PNG_LIBPNG_VER_STRING);
13050 #endif
13051
13052   /*
13053     Open image file.
13054   */
13055   assert(image_info != (const ImageInfo *) NULL);
13056   assert(image_info->signature == MagickSignature);
13057   assert(image != (Image *) NULL);
13058   assert(image->signature == MagickSignature);
13059   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13060   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13061   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13062   if (status == MagickFalse)
13063     return(status);
13064
13065   /*
13066     Allocate a MngInfo structure.
13067   */
13068   have_mng_structure=MagickFalse;
13069   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13070   if (mng_info == (MngInfo *) NULL)
13071     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13072   /*
13073     Initialize members of the MngInfo structure.
13074   */
13075   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13076   mng_info->image=image;
13077   have_mng_structure=MagickTrue;
13078   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13079
13080   /*
13081    * See if user has requested a specific PNG subformat to be used
13082    * for all of the PNGs in the MNG being written, e.g.,
13083    *
13084    *    convert *.png png8:animation.mng
13085    *
13086    * To do: check -define png:bit_depth and png:color_type as well,
13087    * or perhaps use mng:bit_depth and mng:color_type instead for
13088    * global settings.
13089    */
13090
13091   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13092   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13093   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13094
13095   write_jng=MagickFalse;
13096   if (image_info->compression == JPEGCompression)
13097     write_jng=MagickTrue;
13098
13099   mng_info->adjoin=image_info->adjoin &&
13100     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13101
13102   if (logging != MagickFalse)
13103     {
13104       /* Log some info about the input */
13105       Image
13106         *p;
13107
13108       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13109         "  Checking input image(s)");
13110
13111       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13112         "    Image_info depth: %.20g",(double) image_info->depth);
13113
13114       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13115         "    Type: %d",image_info->type);
13116
13117       scene=0;
13118       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13119       {
13120         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13121           "    Scene: %.20g",(double) scene++);
13122
13123         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13124           "      Image depth: %.20g",(double) p->depth);
13125
13126         if (p->alpha_trait == BlendPixelTrait)
13127           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13128             "      Matte: True");
13129
13130         else
13131           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13132             "      Matte: False");
13133
13134         if (p->storage_class == PseudoClass)
13135           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13136             "      Storage class: PseudoClass");
13137
13138         else
13139           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13140             "      Storage class: DirectClass");
13141
13142         if (p->colors)
13143           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13144             "      Number of colors: %.20g",(double) p->colors);
13145
13146         else
13147           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13148             "      Number of colors: unspecified");
13149
13150         if (mng_info->adjoin == MagickFalse)
13151           break;
13152       }
13153     }
13154
13155   use_global_plte=MagickFalse;
13156   all_images_are_gray=MagickFalse;
13157 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13158   need_local_plte=MagickTrue;
13159 #endif
13160   need_defi=MagickFalse;
13161   need_matte=MagickFalse;
13162   mng_info->framing_mode=1;
13163   mng_info->old_framing_mode=1;
13164
13165   if (write_mng)
13166       if (image_info->page != (char *) NULL)
13167         {
13168           /*
13169             Determine image bounding box.
13170           */
13171           SetGeometry(image,&mng_info->page);
13172           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13173             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13174         }
13175   if (write_mng)
13176     {
13177       unsigned int
13178         need_geom;
13179
13180       unsigned short
13181         red,
13182         green,
13183         blue;
13184
13185       mng_info->page=image->page;
13186       need_geom=MagickTrue;
13187       if (mng_info->page.width || mng_info->page.height)
13188          need_geom=MagickFalse;
13189       /*
13190         Check all the scenes.
13191       */
13192       initial_delay=image->delay;
13193       need_iterations=MagickFalse;
13194       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13195       mng_info->equal_physs=MagickTrue,
13196       mng_info->equal_gammas=MagickTrue;
13197       mng_info->equal_srgbs=MagickTrue;
13198       mng_info->equal_backgrounds=MagickTrue;
13199       image_count=0;
13200 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13201     defined(PNG_MNG_FEATURES_SUPPORTED)
13202       all_images_are_gray=MagickTrue;
13203       mng_info->equal_palettes=MagickFalse;
13204       need_local_plte=MagickFalse;
13205 #endif
13206       for (next_image=image; next_image != (Image *) NULL; )
13207       {
13208         if (need_geom)
13209           {
13210             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13211               mng_info->page.width=next_image->columns+next_image->page.x;
13212
13213             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13214               mng_info->page.height=next_image->rows+next_image->page.y;
13215           }
13216
13217         if (next_image->page.x || next_image->page.y)
13218           need_defi=MagickTrue;
13219
13220         if (next_image->alpha_trait == BlendPixelTrait)
13221           need_matte=MagickTrue;
13222
13223         if ((int) next_image->dispose >= BackgroundDispose)
13224           if ((next_image->alpha_trait == BlendPixelTrait) ||
13225                next_image->page.x || next_image->page.y ||
13226               ((next_image->columns < mng_info->page.width) &&
13227                (next_image->rows < mng_info->page.height)))
13228             mng_info->need_fram=MagickTrue;
13229
13230         if (next_image->iterations)
13231           need_iterations=MagickTrue;
13232
13233         final_delay=next_image->delay;
13234
13235         if (final_delay != initial_delay || final_delay > 1UL*
13236            next_image->ticks_per_second)
13237           mng_info->need_fram=1;
13238
13239 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13240     defined(PNG_MNG_FEATURES_SUPPORTED)
13241         /*
13242           check for global palette possibility.
13243         */
13244         if (image->alpha_trait == BlendPixelTrait)
13245            need_local_plte=MagickTrue;
13246
13247         if (need_local_plte == 0)
13248           {
13249             if (IsImageGray(image,exception) == MagickFalse)
13250               all_images_are_gray=MagickFalse;
13251             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13252             if (use_global_plte == 0)
13253               use_global_plte=mng_info->equal_palettes;
13254             need_local_plte=!mng_info->equal_palettes;
13255           }
13256 #endif
13257         if (GetNextImageInList(next_image) != (Image *) NULL)
13258           {
13259             if (next_image->background_color.red !=
13260                 next_image->next->background_color.red ||
13261                 next_image->background_color.green !=
13262                 next_image->next->background_color.green ||
13263                 next_image->background_color.blue !=
13264                 next_image->next->background_color.blue)
13265               mng_info->equal_backgrounds=MagickFalse;
13266
13267             if (next_image->gamma != next_image->next->gamma)
13268               mng_info->equal_gammas=MagickFalse;
13269
13270             if (next_image->rendering_intent !=
13271                 next_image->next->rendering_intent)
13272               mng_info->equal_srgbs=MagickFalse;
13273
13274             if ((next_image->units != next_image->next->units) ||
13275                 (next_image->resolution.x != next_image->next->resolution.x) ||
13276                 (next_image->resolution.y != next_image->next->resolution.y))
13277               mng_info->equal_physs=MagickFalse;
13278
13279             if (mng_info->equal_chrms)
13280               {
13281                 if (next_image->chromaticity.red_primary.x !=
13282                     next_image->next->chromaticity.red_primary.x ||
13283                     next_image->chromaticity.red_primary.y !=
13284                     next_image->next->chromaticity.red_primary.y ||
13285                     next_image->chromaticity.green_primary.x !=
13286                     next_image->next->chromaticity.green_primary.x ||
13287                     next_image->chromaticity.green_primary.y !=
13288                     next_image->next->chromaticity.green_primary.y ||
13289                     next_image->chromaticity.blue_primary.x !=
13290                     next_image->next->chromaticity.blue_primary.x ||
13291                     next_image->chromaticity.blue_primary.y !=
13292                     next_image->next->chromaticity.blue_primary.y ||
13293                     next_image->chromaticity.white_point.x !=
13294                     next_image->next->chromaticity.white_point.x ||
13295                     next_image->chromaticity.white_point.y !=
13296                     next_image->next->chromaticity.white_point.y)
13297                   mng_info->equal_chrms=MagickFalse;
13298               }
13299           }
13300         image_count++;
13301         next_image=GetNextImageInList(next_image);
13302       }
13303       if (image_count < 2)
13304         {
13305           mng_info->equal_backgrounds=MagickFalse;
13306           mng_info->equal_chrms=MagickFalse;
13307           mng_info->equal_gammas=MagickFalse;
13308           mng_info->equal_srgbs=MagickFalse;
13309           mng_info->equal_physs=MagickFalse;
13310           use_global_plte=MagickFalse;
13311 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13312           need_local_plte=MagickTrue;
13313 #endif
13314           need_iterations=MagickFalse;
13315         }
13316
13317      if (mng_info->need_fram == MagickFalse)
13318        {
13319          /*
13320            Only certain framing rates 100/n are exactly representable without
13321            the FRAM chunk but we'll allow some slop in VLC files
13322          */
13323          if (final_delay == 0)
13324            {
13325              if (need_iterations != MagickFalse)
13326                {
13327                  /*
13328                    It's probably a GIF with loop; don't run it *too* fast.
13329                  */
13330                  if (mng_info->adjoin)
13331                    {
13332                      final_delay=10;
13333                      (void) ThrowMagickException(exception,GetMagickModule(),
13334                        CoderWarning,
13335                        "input has zero delay between all frames; assuming",
13336                        " 10 cs `%s'","");
13337                    }
13338                }
13339              else
13340                mng_info->ticks_per_second=0;
13341            }
13342          if (final_delay != 0)
13343            mng_info->ticks_per_second=(png_uint_32)
13344               (image->ticks_per_second/final_delay);
13345          if (final_delay > 50)
13346            mng_info->ticks_per_second=2;
13347
13348          if (final_delay > 75)
13349            mng_info->ticks_per_second=1;
13350
13351          if (final_delay > 125)
13352            mng_info->need_fram=MagickTrue;
13353
13354          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13355             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13356             (final_delay != 25) && (final_delay != 50) && (final_delay !=
13357                1UL*image->ticks_per_second))
13358            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13359        }
13360
13361      if (mng_info->need_fram != MagickFalse)
13362         mng_info->ticks_per_second=1UL*image->ticks_per_second;
13363      /*
13364         If pseudocolor, we should also check to see if all the
13365         palettes are identical and write a global PLTE if they are.
13366         ../glennrp Feb 99.
13367      */
13368      /*
13369         Write the MNG version 1.0 signature and MHDR chunk.
13370      */
13371      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13372      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13373      PNGType(chunk,mng_MHDR);
13374      LogPNGChunk(logging,mng_MHDR,28L);
13375      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13376      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13377      PNGLong(chunk+12,mng_info->ticks_per_second);
13378      PNGLong(chunk+16,0L);  /* layer count=unknown */
13379      PNGLong(chunk+20,0L);  /* frame count=unknown */
13380      PNGLong(chunk+24,0L);  /* play time=unknown   */
13381      if (write_jng)
13382        {
13383          if (need_matte)
13384            {
13385              if (need_defi || mng_info->need_fram || use_global_plte)
13386                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13387
13388              else
13389                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13390            }
13391
13392          else
13393            {
13394              if (need_defi || mng_info->need_fram || use_global_plte)
13395                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13396
13397              else
13398                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13399            }
13400        }
13401
13402      else
13403        {
13404          if (need_matte)
13405            {
13406              if (need_defi || mng_info->need_fram || use_global_plte)
13407                PNGLong(chunk+28,11L);    /* simplicity=LC */
13408
13409              else
13410                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13411            }
13412
13413          else
13414            {
13415              if (need_defi || mng_info->need_fram || use_global_plte)
13416                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13417
13418              else
13419                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13420            }
13421        }
13422      (void) WriteBlob(image,32,chunk);
13423      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13424      option=GetImageOption(image_info,"mng:need-cacheoff");
13425      if (option != (const char *) NULL)
13426        {
13427          size_t
13428            length;
13429
13430          /*
13431            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13432          */
13433          PNGType(chunk,mng_nEED);
13434          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13435          (void) WriteBlobMSBULong(image,(size_t) length);
13436          LogPNGChunk(logging,mng_nEED,(size_t) length);
13437          length+=4;
13438          (void) WriteBlob(image,length,chunk);
13439          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13440        }
13441      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13442          (GetNextImageInList(image) != (Image *) NULL) &&
13443          (image->iterations != 1))
13444        {
13445          /*
13446            Write MNG TERM chunk
13447          */
13448          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13449          PNGType(chunk,mng_TERM);
13450          LogPNGChunk(logging,mng_TERM,10L);
13451          chunk[4]=3;  /* repeat animation */
13452          chunk[5]=0;  /* show last frame when done */
13453          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13454             final_delay/MagickMax(image->ticks_per_second,1)));
13455
13456          if (image->iterations == 0)
13457            PNGLong(chunk+10,PNG_UINT_31_MAX);
13458
13459          else
13460            PNGLong(chunk+10,(png_uint_32) image->iterations);
13461
13462          if (logging != MagickFalse)
13463            {
13464              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13465                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13466               final_delay/MagickMax(image->ticks_per_second,1)));
13467
13468              if (image->iterations == 0)
13469                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13470                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13471
13472              else
13473                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13474                  "     Image iterations: %.20g",(double) image->iterations);
13475            }
13476          (void) WriteBlob(image,14,chunk);
13477          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13478        }
13479      /*
13480        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13481      */
13482      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13483           mng_info->equal_srgbs)
13484        {
13485          /*
13486            Write MNG sRGB chunk
13487          */
13488          (void) WriteBlobMSBULong(image,1L);
13489          PNGType(chunk,mng_sRGB);
13490          LogPNGChunk(logging,mng_sRGB,1L);
13491
13492          if (image->rendering_intent != UndefinedIntent)
13493            chunk[4]=(unsigned char)
13494              Magick_RenderingIntent_to_PNG_RenderingIntent(
13495              (image->rendering_intent));
13496
13497          else
13498            chunk[4]=(unsigned char)
13499              Magick_RenderingIntent_to_PNG_RenderingIntent(
13500                (PerceptualIntent));
13501
13502          (void) WriteBlob(image,5,chunk);
13503          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13504          mng_info->have_write_global_srgb=MagickTrue;
13505        }
13506
13507      else
13508        {
13509          if (image->gamma && mng_info->equal_gammas)
13510            {
13511              /*
13512                 Write MNG gAMA chunk
13513              */
13514              (void) WriteBlobMSBULong(image,4L);
13515              PNGType(chunk,mng_gAMA);
13516              LogPNGChunk(logging,mng_gAMA,4L);
13517              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13518              (void) WriteBlob(image,8,chunk);
13519              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13520              mng_info->have_write_global_gama=MagickTrue;
13521            }
13522          if (mng_info->equal_chrms)
13523            {
13524              PrimaryInfo
13525                primary;
13526
13527              /*
13528                 Write MNG cHRM chunk
13529              */
13530              (void) WriteBlobMSBULong(image,32L);
13531              PNGType(chunk,mng_cHRM);
13532              LogPNGChunk(logging,mng_cHRM,32L);
13533              primary=image->chromaticity.white_point;
13534              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13535              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13536              primary=image->chromaticity.red_primary;
13537              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13538              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13539              primary=image->chromaticity.green_primary;
13540              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13541              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13542              primary=image->chromaticity.blue_primary;
13543              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13544              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13545              (void) WriteBlob(image,36,chunk);
13546              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13547              mng_info->have_write_global_chrm=MagickTrue;
13548            }
13549        }
13550      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13551        {
13552          /*
13553             Write MNG pHYs chunk
13554          */
13555          (void) WriteBlobMSBULong(image,9L);
13556          PNGType(chunk,mng_pHYs);
13557          LogPNGChunk(logging,mng_pHYs,9L);
13558
13559          if (image->units == PixelsPerInchResolution)
13560            {
13561              PNGLong(chunk+4,(png_uint_32)
13562                (image->resolution.x*100.0/2.54+0.5));
13563
13564              PNGLong(chunk+8,(png_uint_32)
13565                (image->resolution.y*100.0/2.54+0.5));
13566
13567              chunk[12]=1;
13568            }
13569
13570          else
13571            {
13572              if (image->units == PixelsPerCentimeterResolution)
13573                {
13574                  PNGLong(chunk+4,(png_uint_32)
13575                    (image->resolution.x*100.0+0.5));
13576
13577                  PNGLong(chunk+8,(png_uint_32)
13578                    (image->resolution.y*100.0+0.5));
13579
13580                  chunk[12]=1;
13581                }
13582
13583              else
13584                {
13585                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13586                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13587                  chunk[12]=0;
13588                }
13589            }
13590          (void) WriteBlob(image,13,chunk);
13591          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13592        }
13593      /*
13594        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13595        or does not cover the entire frame.
13596      */
13597      if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
13598          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13599          (image->page.width+image->page.x < mng_info->page.width))
13600          || (image->page.height && (image->page.height+image->page.y
13601          < mng_info->page.height))))
13602        {
13603          (void) WriteBlobMSBULong(image,6L);
13604          PNGType(chunk,mng_BACK);
13605          LogPNGChunk(logging,mng_BACK,6L);
13606          red=ScaleQuantumToShort(image->background_color.red);
13607          green=ScaleQuantumToShort(image->background_color.green);
13608          blue=ScaleQuantumToShort(image->background_color.blue);
13609          PNGShort(chunk+4,red);
13610          PNGShort(chunk+6,green);
13611          PNGShort(chunk+8,blue);
13612          (void) WriteBlob(image,10,chunk);
13613          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13614          if (mng_info->equal_backgrounds)
13615            {
13616              (void) WriteBlobMSBULong(image,6L);
13617              PNGType(chunk,mng_bKGD);
13618              LogPNGChunk(logging,mng_bKGD,6L);
13619              (void) WriteBlob(image,10,chunk);
13620              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13621            }
13622        }
13623
13624 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13625      if ((need_local_plte == MagickFalse) &&
13626          (image->storage_class == PseudoClass) &&
13627          (all_images_are_gray == MagickFalse))
13628        {
13629          size_t
13630            data_length;
13631
13632          /*
13633            Write MNG PLTE chunk
13634          */
13635          data_length=3*image->colors;
13636          (void) WriteBlobMSBULong(image,data_length);
13637          PNGType(chunk,mng_PLTE);
13638          LogPNGChunk(logging,mng_PLTE,data_length);
13639
13640          for (i=0; i < (ssize_t) image->colors; i++)
13641          {
13642            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13643              image->colormap[i].red) & 0xff);
13644            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13645              image->colormap[i].green) & 0xff);
13646            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13647              image->colormap[i].blue) & 0xff);
13648          }
13649
13650          (void) WriteBlob(image,data_length+4,chunk);
13651          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13652          mng_info->have_write_global_plte=MagickTrue;
13653        }
13654 #endif
13655     }
13656   scene=0;
13657   mng_info->delay=0;
13658 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13659     defined(PNG_MNG_FEATURES_SUPPORTED)
13660   mng_info->equal_palettes=MagickFalse;
13661 #endif
13662   do
13663   {
13664     if (mng_info->adjoin)
13665     {
13666 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13667     defined(PNG_MNG_FEATURES_SUPPORTED)
13668     /*
13669       If we aren't using a global palette for the entire MNG, check to
13670       see if we can use one for two or more consecutive images.
13671     */
13672     if (need_local_plte && use_global_plte && !all_images_are_gray)
13673       {
13674         if (mng_info->IsPalette)
13675           {
13676             /*
13677               When equal_palettes is true, this image has the same palette
13678               as the previous PseudoClass image
13679             */
13680             mng_info->have_write_global_plte=mng_info->equal_palettes;
13681             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13682             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13683               {
13684                 /*
13685                   Write MNG PLTE chunk
13686                 */
13687                 size_t
13688                   data_length;
13689
13690                 data_length=3*image->colors;
13691                 (void) WriteBlobMSBULong(image,data_length);
13692                 PNGType(chunk,mng_PLTE);
13693                 LogPNGChunk(logging,mng_PLTE,data_length);
13694
13695                 for (i=0; i < (ssize_t) image->colors; i++)
13696                 {
13697                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13698                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13699                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13700                 }
13701
13702                 (void) WriteBlob(image,data_length+4,chunk);
13703                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13704                    (uInt) (data_length+4)));
13705                 mng_info->have_write_global_plte=MagickTrue;
13706               }
13707           }
13708         else
13709           mng_info->have_write_global_plte=MagickFalse;
13710       }
13711 #endif
13712     if (need_defi)
13713       {
13714         ssize_t
13715           previous_x,
13716           previous_y;
13717
13718         if (scene)
13719           {
13720             previous_x=mng_info->page.x;
13721             previous_y=mng_info->page.y;
13722           }
13723         else
13724           {
13725             previous_x=0;
13726             previous_y=0;
13727           }
13728         mng_info->page=image->page;
13729         if ((mng_info->page.x !=  previous_x) ||
13730             (mng_info->page.y != previous_y))
13731           {
13732              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
13733              PNGType(chunk,mng_DEFI);
13734              LogPNGChunk(logging,mng_DEFI,12L);
13735              chunk[4]=0; /* object 0 MSB */
13736              chunk[5]=0; /* object 0 LSB */
13737              chunk[6]=0; /* visible  */
13738              chunk[7]=0; /* abstract */
13739              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13740              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13741              (void) WriteBlob(image,16,chunk);
13742              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13743           }
13744       }
13745     }
13746
13747    mng_info->write_mng=write_mng;
13748
13749    if ((int) image->dispose >= 3)
13750      mng_info->framing_mode=3;
13751
13752    if (mng_info->need_fram && mng_info->adjoin &&
13753        ((image->delay != mng_info->delay) ||
13754         (mng_info->framing_mode != mng_info->old_framing_mode)))
13755      {
13756        if (image->delay == mng_info->delay)
13757          {
13758            /*
13759              Write a MNG FRAM chunk with the new framing mode.
13760            */
13761            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
13762            PNGType(chunk,mng_FRAM);
13763            LogPNGChunk(logging,mng_FRAM,1L);
13764            chunk[4]=(unsigned char) mng_info->framing_mode;
13765            (void) WriteBlob(image,5,chunk);
13766            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13767          }
13768        else
13769          {
13770            /*
13771              Write a MNG FRAM chunk with the delay.
13772            */
13773            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13774            PNGType(chunk,mng_FRAM);
13775            LogPNGChunk(logging,mng_FRAM,10L);
13776            chunk[4]=(unsigned char) mng_info->framing_mode;
13777            chunk[5]=0;  /* frame name separator (no name) */
13778            chunk[6]=2;  /* flag for changing default delay */
13779            chunk[7]=0;  /* flag for changing frame timeout */
13780            chunk[8]=0;  /* flag for changing frame clipping */
13781            chunk[9]=0;  /* flag for changing frame sync_id */
13782            PNGLong(chunk+10,(png_uint_32)
13783              ((mng_info->ticks_per_second*
13784              image->delay)/MagickMax(image->ticks_per_second,1)));
13785            (void) WriteBlob(image,14,chunk);
13786            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13787            mng_info->delay=(png_uint_32) image->delay;
13788          }
13789        mng_info->old_framing_mode=mng_info->framing_mode;
13790      }
13791
13792 #if defined(JNG_SUPPORTED)
13793    if (image_info->compression == JPEGCompression)
13794      {
13795        ImageInfo
13796          *write_info;
13797
13798        if (logging != MagickFalse)
13799          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13800            "  Writing JNG object.");
13801        /* To do: specify the desired alpha compression method. */
13802        write_info=CloneImageInfo(image_info);
13803        write_info->compression=UndefinedCompression;
13804        status=WriteOneJNGImage(mng_info,write_info,image,exception);
13805        write_info=DestroyImageInfo(write_info);
13806      }
13807    else
13808 #endif
13809      {
13810        if (logging != MagickFalse)
13811          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13812            "  Writing PNG object.");
13813
13814        mng_info->need_blob = MagickFalse;
13815        mng_info->ping_preserve_colormap = MagickFalse;
13816
13817        /* We don't want any ancillary chunks written */
13818        mng_info->ping_exclude_bKGD=MagickTrue;
13819        mng_info->ping_exclude_cHRM=MagickTrue;
13820        mng_info->ping_exclude_date=MagickTrue;
13821        mng_info->ping_exclude_EXIF=MagickTrue;
13822        mng_info->ping_exclude_gAMA=MagickTrue;
13823        mng_info->ping_exclude_iCCP=MagickTrue;
13824        /* mng_info->ping_exclude_iTXt=MagickTrue; */
13825        mng_info->ping_exclude_oFFs=MagickTrue;
13826        mng_info->ping_exclude_pHYs=MagickTrue;
13827        mng_info->ping_exclude_sRGB=MagickTrue;
13828        mng_info->ping_exclude_tEXt=MagickTrue;
13829        mng_info->ping_exclude_tRNS=MagickTrue;
13830        mng_info->ping_exclude_vpAg=MagickTrue;
13831        mng_info->ping_exclude_zCCP=MagickTrue;
13832        mng_info->ping_exclude_zTXt=MagickTrue;
13833
13834        status=WriteOnePNGImage(mng_info,image_info,image,exception);
13835      }
13836
13837     if (status == MagickFalse)
13838       {
13839         MngInfoFreeStruct(mng_info,&have_mng_structure);
13840         (void) CloseBlob(image);
13841         return(MagickFalse);
13842       }
13843     (void) CatchImageException(image);
13844     if (GetNextImageInList(image) == (Image *) NULL)
13845       break;
13846     image=SyncNextImageInList(image);
13847     status=SetImageProgress(image,SaveImagesTag,scene++,
13848       GetImageListLength(image));
13849
13850     if (status == MagickFalse)
13851       break;
13852
13853   } while (mng_info->adjoin);
13854
13855   if (write_mng)
13856     {
13857       while (GetPreviousImageInList(image) != (Image *) NULL)
13858         image=GetPreviousImageInList(image);
13859       /*
13860         Write the MEND chunk.
13861       */
13862       (void) WriteBlobMSBULong(image,0x00000000L);
13863       PNGType(chunk,mng_MEND);
13864       LogPNGChunk(logging,mng_MEND,0L);
13865       (void) WriteBlob(image,4,chunk);
13866       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13867     }
13868   /*
13869     Relinquish resources.
13870   */
13871   (void) CloseBlob(image);
13872   MngInfoFreeStruct(mng_info,&have_mng_structure);
13873
13874   if (logging != MagickFalse)
13875     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13876
13877   return(MagickTrue);
13878 }
13879 #else /* PNG_LIBPNG_VER > 10011 */
13880
13881 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13882 {
13883   (void) image;
13884   printf("Your PNG library is too old: You have libpng-%s\n",
13885      PNG_LIBPNG_VER_STRING);
13886
13887   ThrowBinaryException(CoderError,"PNG library is too old",
13888      image_info->filename);
13889 }
13890
13891 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13892 {
13893   return(WritePNGImage(image_info,image));
13894 }
13895 #endif /* PNG_LIBPNG_VER > 10011 */
13896 #endif