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