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