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