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