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