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