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