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