]> granicus.if.org Git - imagemagick/blob - coders/png.c
Do not replace gAMA chunk found in input with 1/2.2
[imagemagick] / coders / png.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            PPPP   N   N   GGGG                              %
7 %                            P   P  NN  N  G                                  %
8 %                            PPPP   N N N  G  GG                              %
9 %                            P      N  NN  G   G                              %
10 %                            P      N   N   GGG                               %
11 %                                                                             %
12 %                                                                             %
13 %              Read/Write Portable Network Graphics Image Format              %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                           Glenn Randers-Pehrson                             %
18 %                               November 1997                                 %
19 %                                                                             %
20 %                                                                             %
21 %  Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization      %
22 %  dedicated to making software imaging solutions freely available.           %
23 %                                                                             %
24 %  You may not use this file except in compliance with the License.  You may  %
25 %  obtain a copy of the License at                                            %
26 %                                                                             %
27 %    http://www.imagemagick.org/script/license.php                            %
28 %                                                                             %
29 %  Unless required by applicable law or agreed to in writing, software        %
30 %  distributed under the License is distributed on an "AS IS" BASIS,          %
31 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32 %  See the License for the specific language governing permissions and        %
33 %  limitations under the License.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39
40 \f
41 /*
42   Include declarations.
43 */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
52 #include "MagickCore/color-private.h"
53 #include "MagickCore/colormap.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/histogram.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/layer.h"
65 #include "MagickCore/list.h"
66 #include "MagickCore/log.h"
67 #include "MagickCore/MagickCore.h"
68 #include "MagickCore/memory_.h"
69 #include "MagickCore/module.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/profile.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/static.h"
82 #include "MagickCore/statistic.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/utility.h"
87 #if defined(MAGICKCORE_PNG_DELEGATE)
88
89 /* Suppress libpng pedantic warnings that were added in
90  * libpng-1.2.41 and libpng-1.4.0.  If you are working on
91  * migration to libpng-1.5, remove these defines and then
92  * fix any code that generates warnings.
93  */
94 /* #define PNG_DEPRECATED   Use of this function is deprecated */
95 /* #define PNG_USE_RESULT   The result of this function must be checked */
96 /* #define PNG_NORETURN     This function does not return */
97 /* #define PNG_ALLOCATED    The result of the function is new memory */
98 /* #define PNG_DEPSTRUCT    Access to this struct member is deprecated */
99
100 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
101 #define PNG_PTR_NORETURN
102
103 #include "png.h"
104 #include "zlib.h"
105 \f
106 /* ImageMagick differences */
107 #define first_scene scene
108
109 #if PNG_LIBPNG_VER > 10011
110 /*
111   Optional declarations. Define or undefine them as you like.
112 */
113 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
114
115 /*
116   Features under construction.  Define these to work on them.
117 */
118 #undef MNG_OBJECT_BUFFERS
119 #undef MNG_BASI_SUPPORTED
120 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121 #define MNG_INSERT_LAYERS   /* Troublesome, but seem to work as of 5.4.4 */
122 #if defined(MAGICKCORE_JPEG_DELEGATE)
123 #  define JNG_SUPPORTED /* Not finished as of 5.5.2.  See "To do" comments. */
124 #endif
125 #if !defined(RGBColorMatchExact)
126 #define IsPNGColorEqual(color,target) \
127        (((color).red == (target).red) && \
128         ((color).green == (target).green) && \
129         ((color).blue == (target).blue))
130 #endif
131
132 /* Macros for left-bit-replication to ensure that pixels
133  * and PixelInfos all have the same image->depth, and for use
134  * in PNG8 quantization.
135  */
136
137
138 /* LBR01: Replicate top bit */
139
140 #define LBR01PacketRed(pixelpacket) \
141      (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
142         0 : QuantumRange);
143
144 #define LBR01PacketGreen(pixelpacket) \
145      (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
146         0 : QuantumRange);
147
148 #define LBR01PacketBlue(pixelpacket) \
149      (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
150         0 : QuantumRange);
151
152 #define LBR01PacketAlpha(pixelpacket) \
153      (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
154         0 : QuantumRange);
155
156 #define LBR01PacketRGB(pixelpacket) \
157         { \
158         LBR01PacketRed((pixelpacket)); \
159         LBR01PacketGreen((pixelpacket)); \
160         LBR01PacketBlue((pixelpacket)); \
161         }
162
163 #define LBR01PacketRGBO(pixelpacket) \
164         { \
165         LBR01PacketRGB((pixelpacket)); \
166         LBR01PacketAlpha((pixelpacket)); \
167         }
168
169 #define LBR01PixelRed(pixel) \
170         (SetPixelRed(image, \
171         ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
172         0 : QuantumRange,(pixel)));
173
174 #define LBR01PixelGreen(pixel) \
175         (SetPixelGreen(image, \
176         ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
177         0 : QuantumRange,(pixel)));
178
179 #define LBR01PixelBlue(pixel) \
180         (SetPixelBlue(image, \
181         ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
182         0 : QuantumRange,(pixel)));
183
184 #define LBR01PixelAlpha(pixel) \
185         (SetPixelAlpha(image, \
186         ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
187         0 : QuantumRange,(pixel)));
188
189 #define LBR01PixelRGB(pixel) \
190         { \
191         LBR01PixelRed((pixel)); \
192         LBR01PixelGreen((pixel)); \
193         LBR01PixelBlue((pixel)); \
194         }
195
196 #define LBR01PixelRGBA(pixel) \
197         { \
198         LBR01PixelRGB((pixel)); \
199         LBR01PixelAlpha((pixel)); \
200         }
201
202 /* LBR02: Replicate top 2 bits */
203
204 #define LBR02PacketRed(pixelpacket) \
205    { \
206      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
207      (pixelpacket).red=ScaleCharToQuantum( \
208        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
209    }
210 #define LBR02PacketGreen(pixelpacket) \
211    { \
212      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
213      (pixelpacket).green=ScaleCharToQuantum( \
214        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
215    }
216 #define LBR02PacketBlue(pixelpacket) \
217    { \
218      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
219      (pixelpacket).blue=ScaleCharToQuantum( \
220        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
221    }
222 #define LBR02PacketAlpha(pixelpacket) \
223    { \
224      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
225      (pixelpacket).alpha=ScaleCharToQuantum( \
226        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
227    }
228
229 #define LBR02PacketRGB(pixelpacket) \
230         { \
231         LBR02PacketRed((pixelpacket)); \
232         LBR02PacketGreen((pixelpacket)); \
233         LBR02PacketBlue((pixelpacket)); \
234         }
235
236 #define LBR02PacketRGBO(pixelpacket) \
237         { \
238         LBR02PacketRGB((pixelpacket)); \
239         LBR02PacketAlpha((pixelpacket)); \
240         }
241
242 #define LBR02PixelRed(pixel) \
243    { \
244      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
245        & 0xc0; \
246      SetPixelRed(image, ScaleCharToQuantum( \
247        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
248        (pixel)); \
249    }
250 #define LBR02PixelGreen(pixel) \
251    { \
252      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
253        & 0xc0; \
254      SetPixelGreen(image, ScaleCharToQuantum( \
255        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
256        (pixel)); \
257    }
258 #define LBR02PixelBlue(pixel) \
259    { \
260      unsigned char lbr_bits= \
261        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
262      SetPixelBlue(image, ScaleCharToQuantum( \
263        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
264        (pixel)); \
265    }
266 #define LBR02PixelAlpha(pixel) \
267    { \
268      unsigned char lbr_bits= \
269        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
270      SetPixelAlpha(image, ScaleCharToQuantum( \
271        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
272        (pixel) ); \
273    }
274
275 #define LBR02PixelRGB(pixel) \
276         { \
277         LBR02PixelRed((pixel)); \
278         LBR02PixelGreen((pixel)); \
279         LBR02PixelBlue((pixel)); \
280         }
281
282 #define LBR02PixelRGBA(pixel) \
283         { \
284         LBR02PixelRGB((pixel)); \
285         LBR02PixelAlpha((pixel)); \
286         }
287
288 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
289    PNG8 quantization) */
290
291 #define LBR03PacketRed(pixelpacket) \
292    { \
293      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
294      (pixelpacket).red=ScaleCharToQuantum( \
295        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
296    }
297 #define LBR03PacketGreen(pixelpacket) \
298    { \
299      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
300      (pixelpacket).green=ScaleCharToQuantum( \
301        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
302    }
303 #define LBR03PacketBlue(pixelpacket) \
304    { \
305      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
306      (pixelpacket).blue=ScaleCharToQuantum( \
307        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
308    }
309
310 #define LBR03PacketRGB(pixelpacket) \
311         { \
312         LBR03PacketRed((pixelpacket)); \
313         LBR03PacketGreen((pixelpacket)); \
314         LBR03PacketBlue((pixelpacket)); \
315         }
316
317 #define LBR03PixelRed(pixel) \
318    { \
319      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
320        & 0xe0; \
321      SetPixelRed(image, ScaleCharToQuantum( \
322        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
323    }
324 #define LBR03Green(pixel) \
325    { \
326      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
327        & 0xe0; \
328      SetPixelGreen(image, ScaleCharToQuantum( \
329        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
330    }
331 #define LBR03Blue(pixel) \
332    { \
333      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
334        & 0xe0; \
335      SetPixelBlue(image, ScaleCharToQuantum( \
336        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
337    }
338
339 #define LBR03RGB(pixel) \
340         { \
341         LBR03PixelRed((pixel)); \
342         LBR03Green((pixel)); \
343         LBR03Blue((pixel)); \
344         }
345
346 /* LBR04: Replicate top 4 bits */
347
348 #define LBR04PacketRed(pixelpacket) \
349    { \
350      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
351      (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
352    }
353 #define LBR04PacketGreen(pixelpacket) \
354    { \
355      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
356      (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
357    }
358 #define LBR04PacketBlue(pixelpacket) \
359    { \
360      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
361      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
362    }
363 #define LBR04PacketAlpha(pixelpacket) \
364    { \
365      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
366      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
367    }
368
369 #define LBR04PacketRGB(pixelpacket) \
370         { \
371         LBR04PacketRed((pixelpacket)); \
372         LBR04PacketGreen((pixelpacket)); \
373         LBR04PacketBlue((pixelpacket)); \
374         }
375
376 #define LBR04PacketRGBO(pixelpacket) \
377         { \
378         LBR04PacketRGB((pixelpacket)); \
379         LBR04PacketAlpha((pixelpacket)); \
380         }
381
382 #define LBR04PixelRed(pixel) \
383    { \
384      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
385        & 0xf0; \
386      SetPixelRed(image,\
387        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
388    }
389 #define LBR04PixelGreen(pixel) \
390    { \
391      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
392        & 0xf0; \
393      SetPixelGreen(image,\
394        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
395    }
396 #define LBR04PixelBlue(pixel) \
397    { \
398      unsigned char lbr_bits= \
399        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
400      SetPixelBlue(image,\
401        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
402    }
403 #define LBR04PixelAlpha(pixel) \
404    { \
405      unsigned char lbr_bits= \
406        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
407      SetPixelAlpha(image,\
408        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
409    }
410
411 #define LBR04PixelRGB(pixel) \
412         { \
413         LBR04PixelRed((pixel)); \
414         LBR04PixelGreen((pixel)); \
415         LBR04PixelBlue((pixel)); \
416         }
417
418 #define LBR04PixelRGBA(pixel) \
419         { \
420         LBR04PixelRGB((pixel)); \
421         LBR04PixelAlpha((pixel)); \
422         }
423
424
425 #if MAGICKCORE_QUANTUM_DEPTH > 8
426 /* LBR08: Replicate top 8 bits */
427
428 #define LBR08PacketRed(pixelpacket) \
429    { \
430      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
431      (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
432    }
433 #define LBR08PacketGreen(pixelpacket) \
434    { \
435      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
436      (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
437    }
438 #define LBR08PacketBlue(pixelpacket) \
439    { \
440      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
441      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
442    }
443 #define LBR08PacketAlpha(pixelpacket) \
444    { \
445      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
446      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
447    }
448
449 #define LBR08PacketRGB(pixelpacket) \
450         { \
451         LBR08PacketRed((pixelpacket)); \
452         LBR08PacketGreen((pixelpacket)); \
453         LBR08PacketBlue((pixelpacket)); \
454         }
455
456 #define LBR08PacketRGBO(pixelpacket) \
457         { \
458         LBR08PacketRGB((pixelpacket)); \
459         LBR08PacketAlpha((pixelpacket)); \
460         }
461
462 #define LBR08PixelRed(pixel) \
463    { \
464      unsigned char lbr_bits= \
465        ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
466      SetPixelRed(image,\
467        ScaleCharToQuantum((lbr_bits)), (pixel)); \
468    }
469 #define LBR08PixelGreen(pixel) \
470    { \
471      unsigned char lbr_bits= \
472        ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
473      SetPixelGreen(image,\
474        ScaleCharToQuantum((lbr_bits)), (pixel)); \
475    }
476 #define LBR08PixelBlue(pixel) \
477    { \
478      unsigned char lbr_bits= \
479        ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
480      SetPixelBlue(image,\
481        ScaleCharToQuantum((lbr_bits)), (pixel)); \
482    }
483 #define LBR08PixelAlpha(pixel) \
484    { \
485      unsigned char lbr_bits= \
486        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
487      SetPixelAlpha(image,\
488        ScaleCharToQuantum((lbr_bits)), (pixel)); \
489    }
490
491 #define LBR08PixelRGB(pixel) \
492         { \
493         LBR08PixelRed((pixel)); \
494         LBR08PixelGreen((pixel)); \
495         LBR08PixelBlue((pixel)); \
496         }
497
498 #define LBR08PixelRGBA(pixel) \
499         { \
500         LBR08PixelRGB((pixel)); \
501         LBR08PixelAlpha((pixel)); \
502         }
503 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
504
505
506 #if MAGICKCORE_QUANTUM_DEPTH > 16
507 /* LBR16: Replicate top 16 bits */
508
509 #define LBR16PacketRed(pixelpacket) \
510    { \
511      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
512      (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
513    }
514 #define LBR16PacketGreen(pixelpacket) \
515    { \
516      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
517      (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
518    }
519 #define LBR16PacketBlue(pixelpacket) \
520    { \
521      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
522      (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
523    }
524 #define LBR16PacketAlpha(pixelpacket) \
525    { \
526      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
527      (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
528    }
529
530 #define LBR16PacketRGB(pixelpacket) \
531         { \
532         LBR16PacketRed((pixelpacket)); \
533         LBR16PacketGreen((pixelpacket)); \
534         LBR16PacketBlue((pixelpacket)); \
535         }
536
537 #define LBR16PacketRGBO(pixelpacket) \
538         { \
539         LBR16PacketRGB((pixelpacket)); \
540         LBR16PacketAlpha((pixelpacket)); \
541         }
542
543 #define LBR16PixelRed(pixel) \
544    { \
545      unsigned short lbr_bits= \
546        ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
547      SetPixelRed(image,\
548        ScaleShortToQuantum((lbr_bits)),(pixel)); \
549    }
550 #define LBR16PixelGreen(pixel) \
551    { \
552      unsigned short lbr_bits= \
553        ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
554      SetPixelGreen(image,\
555        ScaleShortToQuantum((lbr_bits)),(pixel)); \
556    }
557 #define LBR16PixelBlue(pixel) \
558    { \
559      unsigned short lbr_bits= \
560        ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
561      SetPixelBlue(image,\
562        ScaleShortToQuantum((lbr_bits)),(pixel)); \
563    }
564 #define LBR16PixelAlpha(pixel) \
565    { \
566      unsigned short lbr_bits= \
567        ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
568      SetPixelAlpha(image,\
569        ScaleShortToQuantum((lbr_bits)),(pixel)); \
570    }
571
572 #define LBR16PixelRGB(pixel) \
573         { \
574         LBR16PixelRed((pixel)); \
575         LBR16PixelGreen((pixel)); \
576         LBR16PixelBlue((pixel)); \
577         }
578
579 #define LBR16PixelRGBA(pixel) \
580         { \
581         LBR16PixelRGB((pixel)); \
582         LBR16PixelAlpha((pixel)); \
583         }
584 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
585
586 /*
587   Establish thread safety.
588   setjmp/longjmp is claimed to be safe on these platforms:
589   setjmp/longjmp is alleged to be unsafe on these platforms:
590 */
591 #ifndef SETJMP_IS_THREAD_SAFE
592 #define PNG_SETJMP_NOT_THREAD_SAFE
593 #endif
594
595 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
596 static SemaphoreInfo
597   *ping_semaphore = (SemaphoreInfo *) NULL;
598 #endif
599
600 /*
601   This temporary until I set up malloc'ed object attributes array.
602   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
603   waste more memory.
604 */
605 #define MNG_MAX_OBJECTS 256
606
607 /*
608   If this not defined, spec is interpreted strictly.  If it is
609   defined, an attempt will be made to recover from some errors,
610   including
611       o global PLTE too short
612 */
613 #undef MNG_LOOSE
614
615 /*
616   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
617   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
618   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
619   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
620   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
621   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
622   will be enabled by default in libpng-1.2.0.
623 */
624 #ifdef PNG_MNG_FEATURES_SUPPORTED
625 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
626 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
627 #  endif
628 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
629 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
630 #  endif
631 #endif
632
633 /*
634   Maximum valid size_t in PNG/MNG chunks is (2^31)-1
635   This macro is only defined in libpng-1.0.3 and later.
636   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
637 */
638 #ifndef PNG_UINT_31_MAX
639 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
640 #endif
641
642 /*
643   Constant strings for known chunk types.  If you need to add a chunk,
644   add a string holding the name here.   To make the code more
645   portable, we use ASCII numbers like this, not characters.
646 */
647
648 static png_byte mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
649 static png_byte mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
650 static png_byte mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
651 static png_byte mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
652 static png_byte mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
653 static png_byte mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
654 static png_byte mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
655 static png_byte mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
656 static png_byte mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
657 static png_byte mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
658 static png_byte mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
659 static png_byte mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
660 static png_byte mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
661 static png_byte mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
662 static png_byte mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
663 static png_byte mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
664 static png_byte mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
665 static png_byte mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
666 static png_byte mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
667 static png_byte mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
668 static png_byte mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
669 static png_byte mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
670 static png_byte mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
671 static png_byte mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
672 static png_byte mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
673 static png_byte mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
674 static png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
675 static png_byte mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
676 static png_byte mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
677 static png_byte mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
678 static png_byte mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
679 static png_byte mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
680 static png_byte mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
681 static png_byte mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
682
683 #if defined(JNG_SUPPORTED)
684 static png_byte mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
685 static png_byte mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
686 static png_byte mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
687 static png_byte mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
688 static png_byte mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
689 static png_byte mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
690 #endif
691
692 /*
693 Other known chunks that are not yet supported by ImageMagick:
694 static png_byte mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
695 static png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
696 static png_byte mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
697 static png_byte mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
698 static png_byte mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
699 static png_byte mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
700 static png_byte mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
701 static png_byte mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
702 */
703
704 typedef struct _MngBox
705 {
706   long
707     left,
708     right,
709     top,
710     bottom;
711 } MngBox;
712
713 typedef struct _MngPair
714 {
715   volatile long
716     a,
717     b;
718 } MngPair;
719
720 #ifdef MNG_OBJECT_BUFFERS
721 typedef struct _MngBuffer
722 {
723
724   size_t
725     height,
726     width;
727
728   Image
729     *image;
730
731   png_color
732     plte[256];
733
734   int
735     reference_count;
736
737   unsigned char
738     alpha_sample_depth,
739     compression_method,
740     color_type,
741     concrete,
742     filter_method,
743     frozen,
744     image_type,
745     interlace_method,
746     pixel_sample_depth,
747     plte_length,
748     sample_depth,
749     viewable;
750 } MngBuffer;
751 #endif
752
753 typedef struct _MngInfo
754 {
755
756 #ifdef MNG_OBJECT_BUFFERS
757   MngBuffer
758     *ob[MNG_MAX_OBJECTS];
759 #endif
760
761   Image *
762     image;
763
764   RectangleInfo
765     page;
766
767   int
768     adjoin,
769 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
770     bytes_in_read_buffer,
771     found_empty_plte,
772 #endif
773     equal_backgrounds,
774     equal_chrms,
775     equal_gammas,
776 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
777     defined(PNG_MNG_FEATURES_SUPPORTED)
778     equal_palettes,
779 #endif
780     equal_physs,
781     equal_srgbs,
782     framing_mode,
783     have_global_bkgd,
784     have_global_chrm,
785     have_global_gama,
786     have_global_phys,
787     have_global_sbit,
788     have_global_srgb,
789     have_saved_bkgd_index,
790     have_write_global_chrm,
791     have_write_global_gama,
792     have_write_global_plte,
793     have_write_global_srgb,
794     need_fram,
795     object_id,
796     old_framing_mode,
797     saved_bkgd_index;
798
799   int
800     new_number_colors;
801
802   ssize_t
803     image_found,
804     loop_count[256],
805     loop_iteration[256],
806     scenes_found,
807     x_off[MNG_MAX_OBJECTS],
808     y_off[MNG_MAX_OBJECTS];
809
810   MngBox
811     clip,
812     frame,
813     image_box,
814     object_clip[MNG_MAX_OBJECTS];
815
816   unsigned char
817     /* These flags could be combined into one byte */
818     exists[MNG_MAX_OBJECTS],
819     frozen[MNG_MAX_OBJECTS],
820     loop_active[256],
821     invisible[MNG_MAX_OBJECTS],
822     viewable[MNG_MAX_OBJECTS];
823
824   MagickOffsetType
825     loop_jump[256];
826
827   png_colorp
828     global_plte;
829
830   png_color_8
831     global_sbit;
832
833   png_byte
834 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
835     read_buffer[8],
836 #endif
837     global_trns[256];
838
839   float
840     global_gamma;
841
842   ChromaticityInfo
843     global_chrm;
844
845   RenderingIntent
846     global_srgb_intent;
847
848   unsigned int
849     delay,
850     global_plte_length,
851     global_trns_length,
852     global_x_pixels_per_unit,
853     global_y_pixels_per_unit,
854     mng_width,
855     mng_height,
856     ticks_per_second;
857
858   MagickBooleanType
859     need_blob;
860
861   unsigned int
862     IsPalette,
863     global_phys_unit_type,
864     basi_warning,
865     clon_warning,
866     dhdr_warning,
867     jhdr_warning,
868     magn_warning,
869     past_warning,
870     phyg_warning,
871     phys_warning,
872     sbit_warning,
873     show_warning,
874     mng_type,
875     write_mng,
876     write_png_colortype,
877     write_png_depth,
878     write_png_compression_level,
879     write_png_compression_strategy,
880     write_png_compression_filter,
881     write_png8,
882     write_png24,
883     write_png32,
884     write_png48,
885     write_png64;
886
887 #ifdef MNG_BASI_SUPPORTED
888   size_t
889     basi_width,
890     basi_height;
891
892   unsigned int
893     basi_depth,
894     basi_color_type,
895     basi_compression_method,
896     basi_filter_type,
897     basi_interlace_method,
898     basi_red,
899     basi_green,
900     basi_blue,
901     basi_alpha,
902     basi_viewable;
903 #endif
904
905   png_uint_16
906     magn_first,
907     magn_last,
908     magn_mb,
909     magn_ml,
910     magn_mr,
911     magn_mt,
912     magn_mx,
913     magn_my,
914     magn_methx,
915     magn_methy;
916
917   PixelInfo
918     mng_global_bkgd;
919
920   /* Added at version 6.6.6-7 */
921   MagickBooleanType
922     ping_exclude_bKGD,
923     ping_exclude_cHRM,
924     ping_exclude_date,
925     ping_exclude_EXIF,
926     ping_exclude_gAMA,
927     ping_exclude_iCCP,
928     /* ping_exclude_iTXt, */
929     ping_exclude_oFFs,
930     ping_exclude_pHYs,
931     ping_exclude_sRGB,
932     ping_exclude_tEXt,
933     ping_exclude_tRNS,
934     ping_exclude_vpAg,
935     ping_exclude_zCCP, /* hex-encoded iCCP */
936     ping_exclude_zTXt,
937     ping_preserve_colormap;
938
939 } MngInfo;
940 #endif /* VER */
941 \f
942 /*
943   Forward declarations.
944 */
945 static MagickBooleanType
946   WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
947
948 static MagickBooleanType
949   WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
950
951 #if defined(JNG_SUPPORTED)
952 static MagickBooleanType
953   WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
954 #endif
955
956 #if PNG_LIBPNG_VER > 10011
957
958
959 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
960 static MagickBooleanType
961 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
962 {
963     /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
964      *
965      * This is true if the high byte and the next highest byte of
966      * each sample of the image, the colormap, and the background color
967      * are equal to each other.  We check this by seeing if the samples
968      * are unchanged when we scale them down to 8 and back up to Quantum.
969      *
970      * We don't use the method GetImageDepth() because it doesn't check
971      * background and doesn't handle PseudoClass specially.
972      */
973
974 #define QuantumToCharToQuantumEqQuantum(quantum) \
975   ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
976
977     MagickBooleanType
978       ok_to_reduce=MagickFalse;
979
980     if (image->depth >= 16)
981       {
982
983         const Quantum
984           *p;
985
986         ok_to_reduce=
987            QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
988            QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
989            QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
990            MagickTrue : MagickFalse;
991
992         if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
993           {
994             int indx;
995
996             for (indx=0; indx < (ssize_t) image->colors; indx++)
997               {
998                 ok_to_reduce=(
999                    QuantumToCharToQuantumEqQuantum(
1000                    image->colormap[indx].red) &&
1001                    QuantumToCharToQuantumEqQuantum(
1002                    image->colormap[indx].green) &&
1003                    QuantumToCharToQuantumEqQuantum(
1004                    image->colormap[indx].blue)) ?
1005                    MagickTrue : MagickFalse;
1006
1007                 if (ok_to_reduce == MagickFalse)
1008                    break;
1009               }
1010           }
1011
1012         if ((ok_to_reduce != MagickFalse) &&
1013             (image->storage_class != PseudoClass))
1014           {
1015             ssize_t
1016               y;
1017
1018             register ssize_t
1019               x;
1020
1021             for (y=0; y < (ssize_t) image->rows; y++)
1022             {
1023               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1024
1025               if (p == (const Quantum *) NULL)
1026                 {
1027                   ok_to_reduce = MagickFalse;
1028                   break;
1029                 }
1030
1031               for (x=(ssize_t) image->columns-1; x >= 0; x--)
1032               {
1033                 ok_to_reduce=
1034                    QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1035                    QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1036                    QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
1037                    MagickTrue : MagickFalse;
1038
1039                 if (ok_to_reduce == MagickFalse)
1040                   break;
1041
1042                 p+=GetPixelChannels(image);
1043               }
1044               if (x >= 0)
1045                 break;
1046             }
1047           }
1048
1049         if (ok_to_reduce != MagickFalse)
1050           {
1051             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1052                 "    OK to reduce PNG bit depth to 8 without loss of info");
1053           }
1054         else
1055           {
1056             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1057                 "    Not OK to reduce PNG bit depth to 8 without loss of info");
1058           }
1059       }
1060
1061     return ok_to_reduce;
1062 }
1063 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1064
1065 static const char* PngColorTypeToString(const unsigned int color_type)
1066 {
1067   const char
1068     *result = "Unknown";
1069
1070   switch (color_type)
1071     {
1072     case PNG_COLOR_TYPE_GRAY:
1073       result = "Gray";
1074       break;
1075     case PNG_COLOR_TYPE_GRAY_ALPHA:
1076       result = "Gray+Alpha";
1077       break;
1078     case PNG_COLOR_TYPE_PALETTE:
1079       result = "Palette";
1080       break;
1081     case PNG_COLOR_TYPE_RGB:
1082       result = "RGB";
1083       break;
1084     case PNG_COLOR_TYPE_RGB_ALPHA:
1085       result = "RGB+Alpha";
1086       break;
1087     }
1088
1089   return result;
1090 }
1091
1092 static int
1093 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1094 {
1095   switch (intent)
1096   {
1097     case PerceptualIntent:
1098        return 0;
1099
1100     case RelativeIntent:
1101        return 1;
1102
1103     case SaturationIntent:
1104        return 2;
1105
1106     case AbsoluteIntent:
1107        return 3;
1108
1109     default:
1110        return -1;
1111   }
1112 }
1113
1114 static RenderingIntent
1115 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1116 {
1117   switch (ping_intent)
1118   {
1119     case 0:
1120       return PerceptualIntent;
1121
1122     case 1:
1123       return RelativeIntent;
1124
1125     case 2:
1126       return SaturationIntent;
1127
1128     case 3:
1129       return AbsoluteIntent;
1130
1131     default:
1132       return UndefinedIntent;
1133     }
1134 }
1135
1136 static const char *
1137 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1138 {
1139   switch (ping_intent)
1140   {
1141     case 0:
1142       return "Perceptual Intent";
1143
1144     case 1:
1145       return "Relative Intent";
1146
1147     case 2:
1148       return "Saturation Intent";
1149
1150     case 3:
1151       return "Absolute Intent";
1152
1153     default:
1154       return "Undefined Intent";
1155     }
1156 }
1157
1158 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1159 {
1160   if (x > y)
1161     return(x);
1162
1163   return(y);
1164 }
1165
1166 static const char *
1167 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1168 {
1169   switch (ping_colortype)
1170   {
1171     case 0:
1172       return "Grayscale";
1173
1174     case 2:
1175       return "Truecolor";
1176
1177     case 3:
1178       return "Indexed";
1179
1180     case 4:
1181       return "GrayAlpha";
1182
1183     case 6:
1184       return "RGBA";
1185
1186     default:
1187       return "UndefinedColorType";
1188     }
1189 }
1190
1191
1192 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1193 {
1194   if (x < y)
1195     return(x);
1196
1197   return(y);
1198 }
1199 #endif /* PNG_LIBPNG_VER > 10011 */
1200 #endif /* MAGICKCORE_PNG_DELEGATE */
1201 \f
1202 /*
1203 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1204 %                                                                             %
1205 %                                                                             %
1206 %                                                                             %
1207 %   I s M N G                                                                 %
1208 %                                                                             %
1209 %                                                                             %
1210 %                                                                             %
1211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1212 %
1213 %  IsMNG() returns MagickTrue if the image format type, identified by the
1214 %  magick string, is MNG.
1215 %
1216 %  The format of the IsMNG method is:
1217 %
1218 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1219 %
1220 %  A description of each parameter follows:
1221 %
1222 %    o magick: compare image format pattern against these bytes.
1223 %
1224 %    o length: Specifies the length of the magick string.
1225 %
1226 %
1227 */
1228 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1229 {
1230   if (length < 8)
1231     return(MagickFalse);
1232
1233   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1234     return(MagickTrue);
1235
1236   return(MagickFalse);
1237 }
1238 \f
1239 /*
1240 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1241 %                                                                             %
1242 %                                                                             %
1243 %                                                                             %
1244 %   I s J N G                                                                 %
1245 %                                                                             %
1246 %                                                                             %
1247 %                                                                             %
1248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1249 %
1250 %  IsJNG() returns MagickTrue if the image format type, identified by the
1251 %  magick string, is JNG.
1252 %
1253 %  The format of the IsJNG method is:
1254 %
1255 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1256 %
1257 %  A description of each parameter follows:
1258 %
1259 %    o magick: compare image format pattern against these bytes.
1260 %
1261 %    o length: Specifies the length of the magick string.
1262 %
1263 %
1264 */
1265 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1266 {
1267   if (length < 8)
1268     return(MagickFalse);
1269
1270   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1271     return(MagickTrue);
1272
1273   return(MagickFalse);
1274 }
1275 \f
1276 /*
1277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1278 %                                                                             %
1279 %                                                                             %
1280 %                                                                             %
1281 %   I s P N G                                                                 %
1282 %                                                                             %
1283 %                                                                             %
1284 %                                                                             %
1285 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1286 %
1287 %  IsPNG() returns MagickTrue if the image format type, identified by the
1288 %  magick string, is PNG.
1289 %
1290 %  The format of the IsPNG method is:
1291 %
1292 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1293 %
1294 %  A description of each parameter follows:
1295 %
1296 %    o magick: compare image format pattern against these bytes.
1297 %
1298 %    o length: Specifies the length of the magick string.
1299 %
1300 */
1301 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1302 {
1303   if (length < 8)
1304     return(MagickFalse);
1305
1306   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1307     return(MagickTrue);
1308
1309   return(MagickFalse);
1310 }
1311 \f
1312 #if defined(MAGICKCORE_PNG_DELEGATE)
1313 #if defined(__cplusplus) || defined(c_plusplus)
1314 extern "C" {
1315 #endif
1316
1317 #if (PNG_LIBPNG_VER > 10011)
1318 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1319 {
1320   unsigned char
1321     buffer[4];
1322
1323   assert(image != (Image *) NULL);
1324   assert(image->signature == MagickSignature);
1325   buffer[0]=(unsigned char) (value >> 24);
1326   buffer[1]=(unsigned char) (value >> 16);
1327   buffer[2]=(unsigned char) (value >> 8);
1328   buffer[3]=(unsigned char) value;
1329   return((size_t) WriteBlob(image,4,buffer));
1330 }
1331
1332 static void PNGLong(png_bytep p,png_uint_32 value)
1333 {
1334   *p++=(png_byte) ((value >> 24) & 0xff);
1335   *p++=(png_byte) ((value >> 16) & 0xff);
1336   *p++=(png_byte) ((value >> 8) & 0xff);
1337   *p++=(png_byte) (value & 0xff);
1338 }
1339
1340 #if defined(JNG_SUPPORTED)
1341 static void PNGsLong(png_bytep p,png_int_32 value)
1342 {
1343   *p++=(png_byte) ((value >> 24) & 0xff);
1344   *p++=(png_byte) ((value >> 16) & 0xff);
1345   *p++=(png_byte) ((value >> 8) & 0xff);
1346   *p++=(png_byte) (value & 0xff);
1347 }
1348 #endif
1349
1350 static void PNGShort(png_bytep p,png_uint_16 value)
1351 {
1352   *p++=(png_byte) ((value >> 8) & 0xff);
1353   *p++=(png_byte) (value & 0xff);
1354 }
1355
1356 static void PNGType(png_bytep p,png_bytep type)
1357 {
1358   (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1359 }
1360
1361 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1362    size_t length)
1363 {
1364   if (logging != MagickFalse)
1365     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1366       "  Writing %c%c%c%c chunk, length: %.20g",
1367       type[0],type[1],type[2],type[3],(double) length);
1368 }
1369 #endif /* PNG_LIBPNG_VER > 10011 */
1370
1371 #if defined(__cplusplus) || defined(c_plusplus)
1372 }
1373 #endif
1374
1375 #if PNG_LIBPNG_VER > 10011
1376 /*
1377 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1378 %                                                                             %
1379 %                                                                             %
1380 %                                                                             %
1381 %   R e a d P N G I m a g e                                                   %
1382 %                                                                             %
1383 %                                                                             %
1384 %                                                                             %
1385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1386 %
1387 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1388 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1389 %  allocates the memory necessary for the new Image structure and returns a
1390 %  pointer to the new image or set of images.
1391 %
1392 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1393 %
1394 %  The format of the ReadPNGImage method is:
1395 %
1396 %      Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1397 %
1398 %  A description of each parameter follows:
1399 %
1400 %    o image_info: the image info.
1401 %
1402 %    o exception: return any errors or warnings in this structure.
1403 %
1404 %  To do, more or less in chronological order (as of version 5.5.2,
1405 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1406 %
1407 %    Get 16-bit cheap transparency working.
1408 %
1409 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1410 %
1411 %    Preserve all unknown and not-yet-handled known chunks found in input
1412 %    PNG file and copy them into output PNG files according to the PNG
1413 %    copying rules.
1414 %
1415 %    (At this point, PNG encoding should be in full MNG compliance)
1416 %
1417 %    Provide options for choice of background to use when the MNG BACK
1418 %    chunk is not present or is not mandatory (i.e., leave transparent,
1419 %    user specified, MNG BACK, PNG bKGD)
1420 %
1421 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1422 %    efficiently by linking in the duplicate frames.].
1423 %
1424 %    Decode and act on the MHDR simplicity profile (offer option to reject
1425 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1426 %
1427 %    Upgrade to full MNG without Delta-PNG.
1428 %
1429 %        o  BACK [done a while ago except for background image ID]
1430 %        o  MOVE [done 15 May 1999]
1431 %        o  CLIP [done 15 May 1999]
1432 %        o  DISC [done 19 May 1999]
1433 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1434 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1435 %        o  SHOW
1436 %        o  PAST
1437 %        o  BASI
1438 %        o  MNG-level tEXt/iTXt/zTXt
1439 %        o  pHYg
1440 %        o  pHYs
1441 %        o  sBIT
1442 %        o  bKGD
1443 %        o  iTXt (wait for libpng implementation).
1444 %
1445 %    Use the scene signature to discover when an identical scene is
1446 %    being reused, and just point to the original image->exception instead
1447 %    of storing another set of pixels.  This not specific to MNG
1448 %    but could be applied generally.
1449 %
1450 %    Upgrade to full MNG with Delta-PNG.
1451 %
1452 %    JNG tEXt/iTXt/zTXt
1453 %
1454 %    We will not attempt to read files containing the CgBI chunk.
1455 %    They are really Xcode files meant for display on the iPhone.
1456 %    These are not valid PNG files and it is impossible to recover
1457 %    the original PNG from files that have been converted to Xcode-PNG,
1458 %    since irretrievable loss of color data has occurred due to the
1459 %    use of premultiplied alpha.
1460 */
1461
1462 #if defined(__cplusplus) || defined(c_plusplus)
1463 extern "C" {
1464 #endif
1465
1466 /*
1467   This the function that does the actual reading of data.  It is
1468   the same as the one supplied in libpng, except that it receives the
1469   datastream from the ReadBlob() function instead of standard input.
1470 */
1471 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1472 {
1473   Image
1474     *image;
1475
1476   image=(Image *) png_get_io_ptr(png_ptr);
1477   if (length)
1478     {
1479       png_size_t
1480         check;
1481
1482       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1483       if (check != length)
1484         {
1485           char
1486             msg[MaxTextExtent];
1487
1488           (void) FormatLocaleString(msg,MaxTextExtent,
1489             "Expected %.20g bytes; found %.20g bytes",(double) length,
1490             (double) check);
1491           png_warning(png_ptr,msg);
1492           png_error(png_ptr,"Read Exception");
1493         }
1494     }
1495 }
1496
1497 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1498     !defined(PNG_MNG_FEATURES_SUPPORTED)
1499 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1500  * older than libpng-1.0.3a, which was the first to allow the empty
1501  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1502  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1503  * encountered after an empty PLTE, so we have to look ahead for bKGD
1504  * chunks and remove them from the datastream that is passed to libpng,
1505  * and store their contents for later use.
1506  */
1507 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1508 {
1509   MngInfo
1510     *mng_info;
1511
1512   Image
1513     *image;
1514
1515   png_size_t
1516     check;
1517
1518   register ssize_t
1519     i;
1520
1521   i=0;
1522   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1523   image=(Image *) mng_info->image;
1524   while (mng_info->bytes_in_read_buffer && length)
1525   {
1526     data[i]=mng_info->read_buffer[i];
1527     mng_info->bytes_in_read_buffer--;
1528     length--;
1529     i++;
1530   }
1531   if (length)
1532     {
1533       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1534
1535       if (check != length)
1536         png_error(png_ptr,"Read Exception");
1537
1538       if (length == 4)
1539         {
1540           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1541               (data[3] == 0))
1542             {
1543               check=(png_size_t) ReadBlob(image,(size_t) length,
1544                 (char *) mng_info->read_buffer);
1545               mng_info->read_buffer[4]=0;
1546               mng_info->bytes_in_read_buffer=4;
1547               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1548                 mng_info->found_empty_plte=MagickTrue;
1549               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1550                 {
1551                   mng_info->found_empty_plte=MagickFalse;
1552                   mng_info->have_saved_bkgd_index=MagickFalse;
1553                 }
1554             }
1555
1556           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1557               (data[3] == 1))
1558             {
1559               check=(png_size_t) ReadBlob(image,(size_t) length,
1560                 (char *) mng_info->read_buffer);
1561               mng_info->read_buffer[4]=0;
1562               mng_info->bytes_in_read_buffer=4;
1563               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1564                 if (mng_info->found_empty_plte)
1565                   {
1566                     /*
1567                       Skip the bKGD data byte and CRC.
1568                     */
1569                     check=(png_size_t)
1570                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1571                     check=(png_size_t) ReadBlob(image,(size_t) length,
1572                       (char *) mng_info->read_buffer);
1573                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1574                     mng_info->have_saved_bkgd_index=MagickTrue;
1575                     mng_info->bytes_in_read_buffer=0;
1576                   }
1577             }
1578         }
1579     }
1580 }
1581 #endif
1582
1583 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1584 {
1585   Image
1586     *image;
1587
1588   image=(Image *) png_get_io_ptr(png_ptr);
1589   if (length)
1590     {
1591       png_size_t
1592         check;
1593
1594       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1595
1596       if (check != length)
1597         png_error(png_ptr,"WriteBlob Failed");
1598     }
1599 }
1600
1601 static void png_flush_data(png_structp png_ptr)
1602 {
1603   (void) png_ptr;
1604 }
1605
1606 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1607 static int PalettesAreEqual(Image *a,Image *b)
1608 {
1609   ssize_t
1610     i;
1611
1612   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1613     return((int) MagickFalse);
1614
1615   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1616     return((int) MagickFalse);
1617
1618   if (a->colors != b->colors)
1619     return((int) MagickFalse);
1620
1621   for (i=0; i < (ssize_t) a->colors; i++)
1622   {
1623     if ((a->colormap[i].red != b->colormap[i].red) ||
1624         (a->colormap[i].green != b->colormap[i].green) ||
1625         (a->colormap[i].blue != b->colormap[i].blue))
1626       return((int) MagickFalse);
1627   }
1628
1629   return((int) MagickTrue);
1630 }
1631 #endif
1632
1633 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1634 {
1635   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1636       mng_info->exists[i] && !mng_info->frozen[i])
1637     {
1638 #ifdef MNG_OBJECT_BUFFERS
1639       if (mng_info->ob[i] != (MngBuffer *) NULL)
1640         {
1641           if (mng_info->ob[i]->reference_count > 0)
1642             mng_info->ob[i]->reference_count--;
1643
1644           if (mng_info->ob[i]->reference_count == 0)
1645             {
1646               if (mng_info->ob[i]->image != (Image *) NULL)
1647                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1648
1649               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1650             }
1651         }
1652       mng_info->ob[i]=(MngBuffer *) NULL;
1653 #endif
1654       mng_info->exists[i]=MagickFalse;
1655       mng_info->invisible[i]=MagickFalse;
1656       mng_info->viewable[i]=MagickFalse;
1657       mng_info->frozen[i]=MagickFalse;
1658       mng_info->x_off[i]=0;
1659       mng_info->y_off[i]=0;
1660       mng_info->object_clip[i].left=0;
1661       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1662       mng_info->object_clip[i].top=0;
1663       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1664     }
1665 }
1666
1667 static void MngInfoFreeStruct(MngInfo *mng_info,
1668     MagickBooleanType *have_mng_structure)
1669 {
1670   if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1671     {
1672       register ssize_t
1673         i;
1674
1675       for (i=1; i < MNG_MAX_OBJECTS; i++)
1676         MngInfoDiscardObject(mng_info,i);
1677
1678       if (mng_info->global_plte != (png_colorp) NULL)
1679         mng_info->global_plte=(png_colorp)
1680           RelinquishMagickMemory(mng_info->global_plte);
1681
1682       mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1683       *have_mng_structure=MagickFalse;
1684     }
1685 }
1686
1687 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1688 {
1689   MngBox
1690     box;
1691
1692   box=box1;
1693   if (box.left < box2.left)
1694     box.left=box2.left;
1695
1696   if (box.top < box2.top)
1697     box.top=box2.top;
1698
1699   if (box.right > box2.right)
1700     box.right=box2.right;
1701
1702   if (box.bottom > box2.bottom)
1703     box.bottom=box2.bottom;
1704
1705   return box;
1706 }
1707
1708 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1709 {
1710    MngBox
1711       box;
1712
1713   /*
1714     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1715   */
1716   box.left=(ssize_t) ((p[0]  << 24) | (p[1]  << 16) | (p[2]  << 8) | p[3]);
1717   box.right=(ssize_t) ((p[4]  << 24) | (p[5]  << 16) | (p[6]  << 8) | p[7]);
1718   box.top=(ssize_t) ((p[8]  << 24) | (p[9]  << 16) | (p[10] << 8) | p[11]);
1719   box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1720   if (delta_type != 0)
1721     {
1722       box.left+=previous_box.left;
1723       box.right+=previous_box.right;
1724       box.top+=previous_box.top;
1725       box.bottom+=previous_box.bottom;
1726     }
1727
1728   return(box);
1729 }
1730
1731 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1732   unsigned char *p)
1733 {
1734   MngPair
1735     pair;
1736   /*
1737     Read two ssize_ts from CLON, MOVE or PAST chunk
1738   */
1739   pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1740   pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1741
1742   if (delta_type != 0)
1743     {
1744       pair.a+=previous_pair.a;
1745       pair.b+=previous_pair.b;
1746     }
1747
1748   return(pair);
1749 }
1750
1751 static long mng_get_long(unsigned char *p)
1752 {
1753   return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1754 }
1755
1756 typedef struct _PNGErrorInfo
1757 {
1758   Image
1759     *image;
1760
1761   ExceptionInfo
1762     *exception;
1763 } PNGErrorInfo;
1764
1765 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1766 {
1767   ExceptionInfo
1768     *exception;
1769
1770   Image
1771     *image;
1772
1773   PNGErrorInfo
1774     *error_info;
1775
1776   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1777   image=error_info->image;
1778   exception=error_info->exception;
1779
1780   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781     "  libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1782
1783   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1784     "`%s'",image->filename);
1785
1786 #if (PNG_LIBPNG_VER < 10500)
1787   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1788    * are building with libpng-1.4.x and can be ignored.
1789    */
1790   longjmp(ping->jmpbuf,1);
1791 #else
1792   png_longjmp(ping,1);
1793 #endif
1794 }
1795
1796 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1797 {
1798   ExceptionInfo
1799     *exception;
1800
1801   Image
1802     *image;
1803
1804   PNGErrorInfo
1805     *error_info;
1806
1807   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1808     png_error(ping, message);
1809
1810   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1811   image=error_info->image;
1812   exception=error_info->exception;
1813   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1814     "  libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1815
1816   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1817     message,"`%s'",image->filename);
1818 }
1819
1820 #ifdef PNG_USER_MEM_SUPPORTED
1821 #if PNG_LIBPNG_VER >= 14000
1822 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1823 #else
1824 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1825 #endif
1826 {
1827   (void) png_ptr;
1828   return((png_voidp) AcquireMagickMemory((size_t) size));
1829 }
1830
1831 /*
1832   Free a pointer.  It is removed from the list at the same time.
1833 */
1834 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1835 {
1836   (void) png_ptr;
1837   ptr=RelinquishMagickMemory(ptr);
1838   return((png_free_ptr) NULL);
1839 }
1840 #endif
1841
1842 #if defined(__cplusplus) || defined(c_plusplus)
1843 }
1844 #endif
1845
1846 static int
1847 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1848    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1849 {
1850   register ssize_t
1851     i;
1852
1853   register unsigned char
1854     *dp;
1855
1856   register png_charp
1857     sp;
1858
1859   png_uint_32
1860     length,
1861     nibbles;
1862
1863   StringInfo
1864     *profile;
1865
1866   const unsigned char
1867     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1868                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1869                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1870                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1871                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1872                  13,14,15};
1873
1874   sp=text[ii].text+1;
1875   /* look for newline */
1876   while (*sp != '\n')
1877      sp++;
1878
1879   /* look for length */
1880   while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1881      sp++;
1882
1883   length=(png_uint_32) StringToLong(sp);
1884
1885   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1886        "      length: %lu",(unsigned long) length);
1887
1888   while (*sp != ' ' && *sp != '\n')
1889      sp++;
1890
1891   /* allocate space */
1892   if (length == 0)
1893   {
1894     png_warning(ping,"invalid profile length");
1895     return(MagickFalse);
1896   }
1897
1898   profile=BlobToStringInfo((const void *) NULL,length);
1899
1900   if (profile == (StringInfo *) NULL)
1901   {
1902     png_warning(ping, "unable to copy profile");
1903     return(MagickFalse);
1904   }
1905
1906   /* copy profile, skipping white space and column 1 "=" signs */
1907   dp=GetStringInfoDatum(profile);
1908   nibbles=length*2;
1909
1910   for (i=0; i < (ssize_t) nibbles; i++)
1911   {
1912     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1913     {
1914       if (*sp == '\0')
1915         {
1916           png_warning(ping, "ran out of profile data");
1917           profile=DestroyStringInfo(profile);
1918           return(MagickFalse);
1919         }
1920       sp++;
1921     }
1922
1923     if (i%2 == 0)
1924       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1925
1926     else
1927       (*dp++)+=unhex[(int) *sp++];
1928   }
1929   /*
1930     We have already read "Raw profile type.
1931   */
1932   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1933   profile=DestroyStringInfo(profile);
1934
1935   if (image_info->verbose)
1936     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1937
1938   return MagickTrue;
1939 }
1940
1941 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1942 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1943 {
1944   Image
1945     *image;
1946
1947
1948   /* The unknown chunk structure contains the chunk data:
1949      png_byte name[5];
1950      png_byte *data;
1951      png_size_t size;
1952
1953      Note that libpng has already taken care of the CRC handling.
1954   */
1955
1956
1957   if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1958       chunk->name[2] != 65 ||chunk-> name[3] != 103)
1959     return(0); /* Did not recognize */
1960
1961   /* recognized vpAg */
1962
1963   if (chunk->size != 9)
1964     return(-1); /* Error return */
1965
1966   if (chunk->data[8] != 0)
1967     return(0);  /* ImageMagick requires pixel units */
1968
1969   image=(Image *) png_get_user_chunk_ptr(ping);
1970
1971   image->page.width=(size_t) ((chunk->data[0] << 24) |
1972      (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1973
1974   image->page.height=(size_t) ((chunk->data[4] << 24) |
1975      (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1976
1977   /* Return one of the following: */
1978      /* return(-n);  chunk had an error */
1979      /* return(0);  did not recognize */
1980      /* return(n);  success */
1981
1982   return(1);
1983
1984 }
1985 #endif
1986
1987 /*
1988 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1989 %                                                                             %
1990 %                                                                             %
1991 %                                                                             %
1992 %   R e a d O n e P N G I m a g e                                             %
1993 %                                                                             %
1994 %                                                                             %
1995 %                                                                             %
1996 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1997 %
1998 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1999 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
2000 %  necessary for the new Image structure and returns a pointer to the new
2001 %  image.
2002 %
2003 %  The format of the ReadOnePNGImage method is:
2004 %
2005 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2006 %         ExceptionInfo *exception)
2007 %
2008 %  A description of each parameter follows:
2009 %
2010 %    o mng_info: Specifies a pointer to a MngInfo structure.
2011 %
2012 %    o image_info: the image info.
2013 %
2014 %    o exception: return any errors or warnings in this structure.
2015 %
2016 */
2017 static Image *ReadOnePNGImage(MngInfo *mng_info,
2018     const ImageInfo *image_info, ExceptionInfo *exception)
2019 {
2020   /* Read one PNG image */
2021
2022   /* To do: Read the tIME chunk into the date:modify property */
2023   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2024
2025   Image
2026     *image;
2027
2028   int
2029     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2030     num_raw_profiles,
2031     num_text,
2032     num_text_total,
2033     num_passes,
2034     number_colors,
2035     pass,
2036     ping_bit_depth,
2037     ping_color_type,
2038     ping_file_depth,
2039     ping_interlace_method,
2040     ping_compression_method,
2041     ping_filter_method,
2042     ping_num_trans,
2043     unit_type;
2044
2045   double
2046     file_gamma;
2047
2048   MagickBooleanType
2049     logging,
2050     ping_found_cHRM,
2051     ping_found_gAMA,
2052     ping_found_iCCP,
2053     ping_found_sRGB,
2054     ping_found_sRGB_cHRM,
2055     status;
2056
2057   PixelInfo
2058     transparent_color;
2059
2060   PNGErrorInfo
2061     error_info;
2062
2063   png_bytep
2064      ping_trans_alpha;
2065
2066   png_color_16p
2067      ping_background,
2068      ping_trans_color;
2069
2070   png_info
2071     *end_info,
2072     *ping_info;
2073
2074   png_struct
2075     *ping;
2076
2077   png_textp
2078     text;
2079
2080   png_uint_32
2081     ping_height,
2082     ping_width,
2083     x_resolution,
2084     y_resolution;
2085
2086   QuantumInfo
2087     *quantum_info;
2088
2089   ssize_t
2090     ping_rowbytes,
2091     y;
2092
2093   register unsigned char
2094     *p;
2095
2096   register ssize_t
2097     i,
2098     x;
2099
2100   register Quantum
2101     *q;
2102
2103   size_t
2104     length,
2105     row_offset;
2106
2107   ssize_t
2108     j;
2109
2110   unsigned char
2111     *volatile ping_pixels;
2112
2113 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2114   png_byte unused_chunks[]=
2115   {
2116     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2117     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2118     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2119     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2120     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2121     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2122 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2123                           /* ignore the APNG chunks */
2124      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2125     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2126     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2127 #endif
2128   };
2129 #endif
2130
2131   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2132     "  Enter ReadOnePNGImage()");
2133
2134 #if (PNG_LIBPNG_VER < 10200)
2135   if (image_info->verbose)
2136     printf("Your PNG library (libpng-%s) is rather old.\n",
2137        PNG_LIBPNG_VER_STRING);
2138 #endif
2139
2140 #if (PNG_LIBPNG_VER >= 10400)
2141 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
2142   if (image_info->verbose)
2143     {
2144       printf("Your PNG library (libpng-%s) is an old beta version.\n",
2145            PNG_LIBPNG_VER_STRING);
2146       printf("Please update it.\n");
2147     }
2148 #  endif
2149 #endif
2150
2151
2152   quantum_info = (QuantumInfo *) NULL;
2153   image=mng_info->image;
2154
2155   if (logging != MagickFalse)
2156   {
2157     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2158       " Before reading:");
2159
2160     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2161       "  image->alpha_trait=%d",(int) image->alpha_trait);
2162
2163     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2164       "  image->rendering_intent=%d",(int) image->rendering_intent);
2165
2166     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2167       "  image->colorspace=%d",(int) image->colorspace);
2168
2169     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2170       "  image->gamma=%f", image->gamma);
2171   }
2172   intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2173
2174   /* Set to an out-of-range color unless tRNS chunk is present */
2175   transparent_color.red=65537;
2176   transparent_color.green=65537;
2177   transparent_color.blue=65537;
2178   transparent_color.alpha=65537;
2179
2180   number_colors=0;
2181   num_text = 0;
2182   num_text_total = 0;
2183   num_raw_profiles = 0;
2184
2185   ping_found_cHRM = MagickFalse;
2186   ping_found_gAMA = MagickFalse;
2187   ping_found_iCCP = MagickFalse;
2188   ping_found_sRGB = MagickFalse;
2189
2190   /*
2191     Allocate the PNG structures
2192   */
2193 #ifdef PNG_USER_MEM_SUPPORTED
2194  error_info.image=image;
2195  error_info.exception=exception;
2196  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2197    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2198    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2199 #else
2200   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2201     MagickPNGErrorHandler,MagickPNGWarningHandler);
2202 #endif
2203   if (ping == (png_struct *) NULL)
2204     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2205
2206   ping_info=png_create_info_struct(ping);
2207
2208   if (ping_info == (png_info *) NULL)
2209     {
2210       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2211       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2212     }
2213
2214   end_info=png_create_info_struct(ping);
2215
2216   if (end_info == (png_info *) NULL)
2217     {
2218       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2219       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2220     }
2221
2222   ping_pixels=(unsigned char *) NULL;
2223
2224   if (setjmp(png_jmpbuf(ping)))
2225     {
2226       /*
2227         PNG image is corrupt.
2228       */
2229       png_destroy_read_struct(&ping,&ping_info,&end_info);
2230
2231 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2232       UnlockSemaphoreInfo(ping_semaphore);
2233 #endif
2234
2235       if (ping_pixels != (unsigned char *) NULL)
2236         ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2237
2238       if (logging != MagickFalse)
2239         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2240           "  exit ReadOnePNGImage() with error.");
2241
2242       if (image != (Image *) NULL)
2243         {
2244           InheritException(exception,exception);
2245           image->columns=0;
2246         }
2247
2248       return(GetFirstImageInList(image));
2249     }
2250
2251   /* {  For navigation to end of SETJMP-protected block.  Within this
2252    *    block, use png_error() instead of Throwing an Exception, to ensure
2253    *    that libpng is able to clean up, and that the semaphore is unlocked.
2254    */
2255
2256 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2257   LockSemaphoreInfo(ping_semaphore);
2258 #endif
2259
2260   /*
2261     Prepare PNG for reading.
2262   */
2263
2264   mng_info->image_found++;
2265   png_set_sig_bytes(ping,8);
2266
2267   if (LocaleCompare(image_info->magick,"MNG") == 0)
2268     {
2269 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2270       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2271       png_set_read_fn(ping,image,png_get_data);
2272 #else
2273 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2274       png_permit_empty_plte(ping,MagickTrue);
2275       png_set_read_fn(ping,image,png_get_data);
2276 #else
2277       mng_info->image=image;
2278       mng_info->bytes_in_read_buffer=0;
2279       mng_info->found_empty_plte=MagickFalse;
2280       mng_info->have_saved_bkgd_index=MagickFalse;
2281       png_set_read_fn(ping,mng_info,mng_get_data);
2282 #endif
2283 #endif
2284     }
2285
2286   else
2287     png_set_read_fn(ping,image,png_get_data);
2288
2289 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2290   /* Ignore unused chunks and all unknown chunks except for vpAg */
2291   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2292   png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2293   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2294      (int)sizeof(unused_chunks)/5);
2295   /* Callback for other unknown chunks */
2296   png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2297 #endif
2298
2299 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2300 #  if (PNG_LIBPNG_VER >= 10400)
2301     /* Limit the size of the chunk storage cache used for sPLT, text,
2302      * and unknown chunks.
2303      */
2304     png_set_chunk_cache_max(ping, 32767);
2305 #  endif
2306 #endif
2307
2308 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2309     /* Disable new libpng-1.5.10 feature */
2310     png_set_check_for_invalid_index (ping, 0);
2311 #endif
2312
2313 #if (PNG_LIBPNG_VER < 10400)
2314 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2315    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2316   /* Disable thread-unsafe features of pnggccrd */
2317   if (png_access_version_number() >= 10200)
2318   {
2319     png_uint_32 mmx_disable_mask=0;
2320     png_uint_32 asm_flags;
2321
2322     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2323                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2324                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2325                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2326     asm_flags=png_get_asm_flags(ping);
2327     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2328   }
2329 #  endif
2330 #endif
2331
2332   png_read_info(ping,ping_info);
2333
2334   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2335                &ping_bit_depth,&ping_color_type,
2336                &ping_interlace_method,&ping_compression_method,
2337                &ping_filter_method);
2338
2339   ping_file_depth = ping_bit_depth;
2340
2341   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2342   {
2343       char
2344         msg[MaxTextExtent];
2345
2346       (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2347       (void) SetImageProperty(image,"png:IHDR.color-type-orig ",msg,exception);
2348
2349       (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2350       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig  ",msg,exception);
2351   }
2352
2353   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2354                       &ping_trans_color);
2355
2356   (void) png_get_bKGD(ping, ping_info, &ping_background);
2357
2358   if (ping_bit_depth < 8)
2359     {
2360        png_set_packing(ping);
2361        ping_bit_depth = 8;
2362     }
2363
2364   image->depth=ping_bit_depth;
2365   image->depth=GetImageQuantumDepth(image,MagickFalse);
2366   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2367
2368   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2369       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2370     {
2371       image->rendering_intent=UndefinedIntent;
2372       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2373       image->gamma=1.000;
2374       (void) ResetMagickMemory(&image->chromaticity,0,
2375         sizeof(image->chromaticity));
2376     }
2377
2378   if (logging != MagickFalse)
2379     {
2380       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2381         "    PNG width: %.20g, height: %.20g",
2382         (double) ping_width, (double) ping_height);
2383
2384       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2385         "    PNG color_type: %d, bit_depth: %d",
2386         ping_color_type, ping_bit_depth);
2387
2388       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2389         "    PNG compression_method: %d",
2390         ping_compression_method);
2391
2392       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2393         "    PNG interlace_method: %d, filter_method: %d",
2394         ping_interlace_method,ping_filter_method);
2395     }
2396
2397   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2398     {
2399       ping_found_gAMA=MagickTrue;
2400       if (logging != MagickFalse)
2401         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2402           "    Found PNG gAMA chunk.");
2403     }
2404
2405   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2406     {
2407       ping_found_cHRM=MagickTrue;
2408       if (logging != MagickFalse)
2409         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2410           "    Found PNG cHRM chunk.");
2411     }
2412
2413   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2414     {
2415       ping_found_iCCP=MagickTrue;
2416       if (logging != MagickFalse)
2417         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2418           "    Found PNG iCCP chunk.");
2419     }
2420
2421   if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
2422     {
2423       ping_found_sRGB=MagickTrue;
2424       if (logging != MagickFalse)
2425         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2426           "    Found PNG sRGB chunk.");
2427     }
2428
2429 #ifdef PNG_READ_iCCP_SUPPORTED
2430   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2431     {
2432       int
2433         compression;
2434
2435 #if (PNG_LIBPNG_VER < 10500)
2436       png_charp
2437         info;
2438 #else
2439       png_bytep
2440         info;
2441 #endif
2442
2443       png_charp
2444         name;
2445
2446       png_uint_32
2447         profile_length;
2448
2449       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2450         &profile_length);
2451
2452       if (profile_length != 0)
2453         {
2454           StringInfo
2455             *profile;
2456
2457           if (logging != MagickFalse)
2458             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2459               "    Reading PNG iCCP chunk.");
2460           profile=BlobToStringInfo(info,profile_length);
2461           if (profile == (StringInfo *) NULL)
2462             {
2463               png_warning(ping, "ICC profile is NULL");
2464               profile=DestroyStringInfo(profile);
2465             }
2466           else
2467             {
2468               (void) SetImageProfile(image,"icc",profile,exception);
2469               profile=DestroyStringInfo(profile);
2470             }
2471       }
2472     }
2473 #endif
2474 #if defined(PNG_READ_sRGB_SUPPORTED)
2475   {
2476     if (mng_info->have_global_srgb)
2477       {
2478         image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2479           (mng_info->global_srgb_intent);
2480       }
2481
2482     if (png_get_sRGB(ping,ping_info,&intent))
2483       {
2484         image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2485           (intent);
2486
2487         if (logging != MagickFalse)
2488           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2489             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2490       }
2491   }
2492 #endif
2493   {
2494      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2495        if (mng_info->have_global_gama)
2496          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2497
2498      if (png_get_gAMA(ping,ping_info,&file_gamma))
2499        {
2500          image->gamma=(float) file_gamma;
2501          if (logging != MagickFalse)
2502            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2503              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2504        }
2505   }
2506
2507   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2508     {
2509       if (mng_info->have_global_chrm != MagickFalse)
2510         {
2511           (void) png_set_cHRM(ping,ping_info,
2512             mng_info->global_chrm.white_point.x,
2513             mng_info->global_chrm.white_point.y,
2514             mng_info->global_chrm.red_primary.x,
2515             mng_info->global_chrm.red_primary.y,
2516             mng_info->global_chrm.green_primary.x,
2517             mng_info->global_chrm.green_primary.y,
2518             mng_info->global_chrm.blue_primary.x,
2519             mng_info->global_chrm.blue_primary.y);
2520         }
2521     }
2522
2523   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2524     {
2525       (void) png_get_cHRM(ping,ping_info,
2526         &image->chromaticity.white_point.x,
2527         &image->chromaticity.white_point.y,
2528         &image->chromaticity.red_primary.x,
2529         &image->chromaticity.red_primary.y,
2530         &image->chromaticity.green_primary.x,
2531         &image->chromaticity.green_primary.y,
2532         &image->chromaticity.blue_primary.x,
2533         &image->chromaticity.blue_primary.y);
2534
2535        if (image->chromaticity.red_primary.x>0.6399f &&
2536            image->chromaticity.red_primary.x<0.6401f &&
2537            image->chromaticity.red_primary.y>0.3299f &&
2538            image->chromaticity.red_primary.y<0.3301f &&
2539            image->chromaticity.green_primary.x>0.2999f &&
2540            image->chromaticity.green_primary.x<0.3001f &&
2541            image->chromaticity.green_primary.y>0.5999f &&
2542            image->chromaticity.green_primary.y<0.6001f &&
2543            image->chromaticity.blue_primary.x>0.1499f &&
2544            image->chromaticity.blue_primary.x<0.1501f &&
2545            image->chromaticity.blue_primary.y>0.0599f &&
2546            image->chromaticity.blue_primary.y<0.0601f &&
2547            image->chromaticity.white_point.x>0.3126f &&
2548            image->chromaticity.white_point.x<0.3128f &&
2549            image->chromaticity.white_point.y>0.3289f &&
2550            image->chromaticity.white_point.y<0.3291f)
2551           ping_found_sRGB_cHRM=MagickTrue;
2552
2553       ping_found_cHRM=MagickTrue;
2554     }
2555
2556   if (image->rendering_intent != UndefinedIntent)
2557     {
2558       if (ping_found_sRGB != MagickTrue &&
2559           (ping_found_gAMA != MagickTrue ||
2560           (image->gamma > .45 && image->gamma < .46)) &&
2561           (ping_found_cHRM != MagickTrue ||
2562           ping_found_sRGB_cHRM == MagickTrue) &&
2563           ping_found_iCCP != MagickTrue)
2564       {
2565          png_set_sRGB(ping,ping_info,
2566             Magick_RenderingIntent_to_PNG_RenderingIntent
2567             (image->rendering_intent));
2568          if (ping_found_gAMA != MagickTrue)
2569             png_set_gAMA(ping,ping_info,1.000f/2.200f);
2570          file_gamma=1.000f/2.200f;
2571          ping_found_sRGB=MagickTrue;
2572          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2573            "    Setting sRGB and gAMA as if in input");
2574       }
2575     }
2576
2577 #if defined(PNG_oFFs_SUPPORTED)
2578   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2579     {
2580       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2581       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2582
2583       if (logging != MagickFalse)
2584         if (image->page.x || image->page.y)
2585           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2586             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2587             image->page.x,(double) image->page.y);
2588     }
2589 #endif
2590 #if defined(PNG_pHYs_SUPPORTED)
2591   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2592     {
2593       if (mng_info->have_global_phys)
2594         {
2595           png_set_pHYs(ping,ping_info,
2596                        mng_info->global_x_pixels_per_unit,
2597                        mng_info->global_y_pixels_per_unit,
2598                        mng_info->global_phys_unit_type);
2599         }
2600     }
2601
2602   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2603     {
2604       /*
2605         Set image resolution.
2606       */
2607       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2608         &unit_type);
2609       image->resolution.x=(double) x_resolution;
2610       image->resolution.y=(double) y_resolution;
2611
2612       if (unit_type == PNG_RESOLUTION_METER)
2613         {
2614           image->units=PixelsPerCentimeterResolution;
2615           image->resolution.x=(double) x_resolution/100.0;
2616           image->resolution.y=(double) y_resolution/100.0;
2617         }
2618
2619       if (logging != MagickFalse)
2620         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2621           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2622           (double) x_resolution,(double) y_resolution,unit_type);
2623     }
2624 #endif
2625
2626   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2627     {
2628       png_colorp
2629         palette;
2630
2631       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2632
2633       if ((number_colors == 0) &&
2634           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2635         {
2636           if (mng_info->global_plte_length)
2637             {
2638               png_set_PLTE(ping,ping_info,mng_info->global_plte,
2639                 (int) mng_info->global_plte_length);
2640
2641               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2642               {
2643                 if (mng_info->global_trns_length)
2644                   {
2645                     png_warning(ping,
2646                       "global tRNS has more entries than global PLTE");
2647                   }
2648                 else
2649                   {
2650                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
2651                        (int) mng_info->global_trns_length,NULL);
2652                   }
2653                }
2654 #ifdef PNG_READ_bKGD_SUPPORTED
2655               if (
2656 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2657                    mng_info->have_saved_bkgd_index ||
2658 #endif
2659                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2660                     {
2661                       png_color_16
2662                          background;
2663
2664 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2665                       if (mng_info->have_saved_bkgd_index)
2666                         background.index=mng_info->saved_bkgd_index;
2667 #endif
2668                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2669                         background.index=ping_background->index;
2670
2671                       background.red=(png_uint_16)
2672                         mng_info->global_plte[background.index].red;
2673
2674                       background.green=(png_uint_16)
2675                         mng_info->global_plte[background.index].green;
2676
2677                       background.blue=(png_uint_16)
2678                         mng_info->global_plte[background.index].blue;
2679
2680                       background.gray=(png_uint_16)
2681                         mng_info->global_plte[background.index].green;
2682
2683                       png_set_bKGD(ping,ping_info,&background);
2684                     }
2685 #endif
2686                 }
2687               else
2688                 png_error(ping,"No global PLTE in file");
2689             }
2690         }
2691
2692 #ifdef PNG_READ_bKGD_SUPPORTED
2693   if (mng_info->have_global_bkgd &&
2694           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2695       image->background_color=mng_info->mng_global_bkgd;
2696
2697   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2698     {
2699       unsigned int
2700         bkgd_scale;
2701
2702       /*
2703         Set image background color.
2704       */
2705       if (logging != MagickFalse)
2706         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2707           "    Reading PNG bKGD chunk.");
2708
2709       /* Scale background components to 16-bit, then scale
2710        * to quantum depth
2711        */
2712         if (logging != MagickFalse)
2713           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2714             "    raw ping_background=(%d,%d,%d).",ping_background->red,
2715             ping_background->green,ping_background->blue);
2716
2717         bkgd_scale = 1;
2718
2719         if (ping_file_depth == 1)
2720            bkgd_scale = 255;
2721
2722         else if (ping_file_depth == 2)
2723            bkgd_scale = 85;
2724
2725         else if (ping_file_depth == 4)
2726            bkgd_scale = 17;
2727
2728         if (ping_file_depth <= 8)
2729            bkgd_scale *= 257;
2730
2731         ping_background->red *= bkgd_scale;
2732         ping_background->green *= bkgd_scale;
2733         ping_background->blue *= bkgd_scale;
2734
2735         if (logging != MagickFalse)
2736           {
2737             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2738               "    bkgd_scale=%d.",bkgd_scale);
2739
2740             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2741               "    ping_background=(%d,%d,%d).",ping_background->red,
2742               ping_background->green,ping_background->blue);
2743           }
2744
2745         image->background_color.red=
2746             ScaleShortToQuantum(ping_background->red);
2747
2748         image->background_color.green=
2749             ScaleShortToQuantum(ping_background->green);
2750
2751         image->background_color.blue=
2752           ScaleShortToQuantum(ping_background->blue);
2753
2754         image->background_color.alpha=OpaqueAlpha;
2755
2756         if (logging != MagickFalse)
2757           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2758             "    image->background_color=(%.20g,%.20g,%.20g).",
2759             (double) image->background_color.red,
2760             (double) image->background_color.green,
2761             (double) image->background_color.blue);
2762     }
2763 #endif /* PNG_READ_bKGD_SUPPORTED */
2764
2765   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2766     {
2767       /*
2768         Image has a tRNS chunk.
2769       */
2770       int
2771         max_sample;
2772
2773       size_t
2774         one=1;
2775
2776       if (logging != MagickFalse)
2777         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2778           "    Reading PNG tRNS chunk.");
2779
2780       max_sample = (int) ((one << ping_file_depth) - 1);
2781
2782       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2783           (int)ping_trans_color->gray > max_sample) ||
2784           (ping_color_type == PNG_COLOR_TYPE_RGB &&
2785           ((int)ping_trans_color->red > max_sample ||
2786           (int)ping_trans_color->green > max_sample ||
2787           (int)ping_trans_color->blue > max_sample)))
2788         {
2789           if (logging != MagickFalse)
2790             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2791               "    Ignoring PNG tRNS chunk with out-of-range sample.");
2792           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2793           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2794           image->alpha_trait=UndefinedPixelTrait;
2795         }
2796       else
2797         {
2798           int
2799              scale_to_short;
2800
2801           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
2802
2803           /* Scale transparent_color to short */
2804           transparent_color.red= scale_to_short*ping_trans_color->red;
2805           transparent_color.green= scale_to_short*ping_trans_color->green;
2806           transparent_color.blue= scale_to_short*ping_trans_color->blue;
2807           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2808
2809           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2810             {
2811               if (logging != MagickFalse)
2812               {
2813                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2814                   "    Raw tRNS graylevel is %d.",ping_trans_color->gray);
2815
2816                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2817                   "    scaled graylevel is %.20g.",transparent_color.alpha);
2818               }
2819               transparent_color.red=transparent_color.alpha;
2820               transparent_color.green=transparent_color.alpha;
2821               transparent_color.blue=transparent_color.alpha;
2822             }
2823         }
2824     }
2825 #if defined(PNG_READ_sBIT_SUPPORTED)
2826   if (mng_info->have_global_sbit)
2827     {
2828       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2829         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2830     }
2831 #endif
2832   num_passes=png_set_interlace_handling(ping);
2833
2834   png_read_update_info(ping,ping_info);
2835
2836   ping_rowbytes=png_get_rowbytes(ping,ping_info);
2837
2838   /*
2839     Initialize image structure.
2840   */
2841   mng_info->image_box.left=0;
2842   mng_info->image_box.right=(ssize_t) ping_width;
2843   mng_info->image_box.top=0;
2844   mng_info->image_box.bottom=(ssize_t) ping_height;
2845   if (mng_info->mng_type == 0)
2846     {
2847       mng_info->mng_width=ping_width;
2848       mng_info->mng_height=ping_height;
2849       mng_info->frame=mng_info->image_box;
2850       mng_info->clip=mng_info->image_box;
2851     }
2852
2853   else
2854     {
2855       image->page.y=mng_info->y_off[mng_info->object_id];
2856     }
2857
2858   image->compression=ZipCompression;
2859   image->columns=ping_width;
2860   image->rows=ping_height;
2861
2862   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2863       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2864     {
2865       if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
2866           image->gamma == 1.0) && ping_found_sRGB != MagickTrue)
2867         {
2868           /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
2869            * image->colorspace to GRAY, and reset image->chromaticity.
2870            */
2871           SetImageColorspace(image,GRAYColorspace,exception);
2872         }
2873     }
2874   
2875   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2876       "  image->colorspace=%d",(int) image->colorspace);
2877
2878   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2879       ((int) ping_bit_depth < 16 &&
2880       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2881     {
2882       size_t
2883         one;
2884
2885       image->storage_class=PseudoClass;
2886       one=1;
2887       image->colors=one << ping_file_depth;
2888 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2889       if (image->colors > 256)
2890         image->colors=256;
2891 #else
2892       if (image->colors > 65536L)
2893         image->colors=65536L;
2894 #endif
2895       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2896         {
2897           png_colorp
2898             palette;
2899
2900           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2901           image->colors=(size_t) number_colors;
2902
2903           if (logging != MagickFalse)
2904             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2905               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2906         }
2907     }
2908
2909   if (image->storage_class == PseudoClass)
2910     {
2911       /*
2912         Initialize image colormap.
2913       */
2914       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
2915         png_error(ping,"Memory allocation failed");
2916
2917       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2918         {
2919           png_colorp
2920             palette;
2921
2922           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2923
2924           for (i=0; i < (ssize_t) number_colors; i++)
2925           {
2926             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2927             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2928             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2929           }
2930
2931           for ( ; i < (ssize_t) image->colors; i++)
2932           {
2933             image->colormap[i].red=0;
2934             image->colormap[i].green=0;
2935             image->colormap[i].blue=0;
2936           }
2937         }
2938
2939       else
2940         {
2941           size_t
2942             scale;
2943
2944           scale=(QuantumRange/((1UL << ping_file_depth)-1));
2945
2946           if (scale < 1)
2947              scale=1;
2948
2949           for (i=0; i < (ssize_t) image->colors; i++)
2950           {
2951             image->colormap[i].red=(Quantum) (i*scale);
2952             image->colormap[i].green=(Quantum) (i*scale);
2953             image->colormap[i].blue=(Quantum) (i*scale);
2954           }
2955        }
2956     }
2957
2958    /* Set some properties for reporting by "identify" */
2959     {
2960       char
2961         msg[MaxTextExtent];
2962
2963      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
2964         ping_interlace_method in value */
2965
2966      (void) FormatLocaleString(msg,MaxTextExtent,
2967          "%d, %d",(int) ping_width, (int) ping_height);
2968      (void) SetImageProperty(image,"png:IHDR.width,height    ",msg,exception);
2969
2970      (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
2971      (void) SetImageProperty(image,"png:IHDR.bit_depth       ",msg,exception);
2972
2973      (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
2974          (int) ping_color_type,
2975          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
2976      (void) SetImageProperty(image,"png:IHDR.color_type      ",msg,exception);
2977
2978      if (ping_interlace_method == 0)
2979        {
2980          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
2981             (int) ping_interlace_method);
2982        }
2983      else if (ping_interlace_method == 1)
2984        {
2985          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
2986             (int) ping_interlace_method);
2987        }
2988      else
2989        {
2990          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
2991             (int) ping_interlace_method);
2992        }
2993        (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
2994
2995      if (number_colors != 0)
2996        {
2997          (void) FormatLocaleString(msg,MaxTextExtent,"%d",
2998             (int) number_colors);
2999          (void) SetImageProperty(image,"png:PLTE.number_colors   ",msg,
3000             exception);
3001        }
3002    }
3003
3004   /*
3005     Read image scanlines.
3006   */
3007   if (image->delay != 0)
3008     mng_info->scenes_found++;
3009
3010   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3011       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3012       (image_info->first_scene+image_info->number_scenes))))
3013     {
3014       /* This happens later in non-ping decodes */
3015       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3016         image->storage_class=DirectClass;
3017
3018       if (logging != MagickFalse)
3019         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3020           "    Skipping PNG image data for scene %.20g",(double)
3021           mng_info->scenes_found-1);
3022       png_destroy_read_struct(&ping,&ping_info,&end_info);
3023
3024 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3025       UnlockSemaphoreInfo(ping_semaphore);
3026 #endif
3027
3028       if (logging != MagickFalse)
3029         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3030           "  exit ReadOnePNGImage().");
3031
3032       return(image);
3033     }
3034
3035   if (logging != MagickFalse)
3036     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3037       "    Reading PNG IDAT chunk(s)");
3038
3039   if (num_passes > 1)
3040     ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
3041       ping_rowbytes*sizeof(*ping_pixels));
3042
3043   else
3044     ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
3045       sizeof(*ping_pixels));
3046
3047   if (ping_pixels == (unsigned char *) NULL)
3048     png_error(ping,"Memory allocation failed");
3049
3050   if (logging != MagickFalse)
3051     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3052       "    Converting PNG pixels to pixel packets");
3053   /*
3054     Convert PNG pixels to pixel packets.
3055   */
3056   quantum_info=AcquireQuantumInfo(image_info,image);
3057
3058   if (quantum_info == (QuantumInfo *) NULL)
3059      png_error(ping,"Failed to allocate quantum_info");
3060
3061   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3062
3063   {
3064
3065    MagickBooleanType
3066      found_transparent_pixel;
3067
3068   found_transparent_pixel=MagickFalse;
3069
3070   if (image->storage_class == DirectClass)
3071     {
3072       for (pass=0; pass < num_passes; pass++)
3073       {
3074         /*
3075           Convert image to DirectClass pixel packets.
3076         */
3077         image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3078             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3079             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3080             BlendPixelTrait : UndefinedPixelTrait;
3081
3082         for (y=0; y < (ssize_t) image->rows; y++)
3083         {
3084           if (num_passes > 1)
3085             row_offset=ping_rowbytes*y;
3086
3087           else
3088             row_offset=0;
3089
3090           png_read_row(ping,ping_pixels+row_offset,NULL);
3091
3092           if (pass < num_passes-1)
3093             continue;
3094
3095           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3096
3097           if (q == (Quantum *) NULL)
3098             break;
3099
3100           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3101             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3102               GrayQuantum,ping_pixels+row_offset,exception);
3103
3104           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3105             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3106               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3107
3108           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3109             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3110               RGBAQuantum,ping_pixels+row_offset,exception);
3111
3112           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3113             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3114               IndexQuantum,ping_pixels+row_offset,exception);
3115
3116           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3117             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3118               RGBQuantum,ping_pixels+row_offset,exception);
3119
3120           if (found_transparent_pixel == MagickFalse)
3121             {
3122               /* Is there a transparent pixel in the row? */
3123               if (y== 0 && logging != MagickFalse)
3124                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3125                    "    Looking for cheap transparent pixel");
3126
3127               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3128               {
3129                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3130                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3131                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3132                   {
3133                     if (logging != MagickFalse)
3134                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3135                         "    ...got one.");
3136
3137                     found_transparent_pixel = MagickTrue;
3138                     break;
3139                   }
3140                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3141                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3142                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3143                     transparent_color.red &&
3144                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3145                     transparent_color.green &&
3146                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3147                     transparent_color.blue))
3148                   {
3149                     if (logging != MagickFalse)
3150                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3151                         "    ...got one.");
3152                     found_transparent_pixel = MagickTrue;
3153                     break;
3154                   }
3155                 q+=GetPixelChannels(image);
3156               }
3157             }
3158
3159           if ((image->previous == (Image *) NULL) && (num_passes == 1))
3160             {
3161               status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3162                   image->rows);
3163
3164               if (status == MagickFalse)
3165                 break;
3166             }
3167           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3168             break;
3169         }
3170
3171         if ((image->previous == (Image *) NULL) && (num_passes != 1))
3172           {
3173             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3174             if (status == MagickFalse)
3175               break;
3176           }
3177       }
3178     }
3179
3180   else /* image->storage_class != DirectClass */
3181
3182     for (pass=0; pass < num_passes; pass++)
3183     {
3184       Quantum
3185         *quantum_scanline;
3186
3187       register Quantum
3188         *r;
3189
3190       /*
3191         Convert grayscale image to PseudoClass pixel packets.
3192       */
3193       if (logging != MagickFalse)
3194         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3195           "    Converting grayscale pixels to pixel packets");
3196
3197       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3198         BlendPixelTrait : UndefinedPixelTrait;
3199
3200       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3201         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3202         sizeof(*quantum_scanline));
3203
3204       if (quantum_scanline == (Quantum *) NULL)
3205         png_error(ping,"Memory allocation failed");
3206
3207       for (y=0; y < (ssize_t) image->rows; y++)
3208       {
3209         Quantum
3210            alpha;
3211
3212         if (num_passes > 1)
3213           row_offset=ping_rowbytes*y;
3214
3215         else
3216           row_offset=0;
3217
3218         png_read_row(ping,ping_pixels+row_offset,NULL);
3219
3220         if (pass < num_passes-1)
3221           continue;
3222
3223         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3224
3225         if (q == (Quantum *) NULL)
3226           break;
3227
3228         p=ping_pixels+row_offset;
3229         r=quantum_scanline;
3230
3231         switch (ping_bit_depth)
3232         {
3233           case 8:
3234           {
3235
3236             if (ping_color_type == 4)
3237               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3238               {
3239                 *r++=*p++;
3240
3241                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3242
3243                 SetPixelAlpha(image,alpha,q);
3244
3245                 if (alpha != OpaqueAlpha)
3246                   found_transparent_pixel = MagickTrue;
3247
3248                 q+=GetPixelChannels(image);
3249               }
3250
3251             else
3252               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3253                 *r++=*p++;
3254
3255             break;
3256           }
3257
3258           case 16:
3259           {
3260             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3261             {
3262 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3263               size_t
3264                 quantum;
3265
3266               if (image->colors > 256)
3267                 quantum=((*p++) << 8);
3268
3269               else
3270                 quantum=0;
3271
3272               quantum|=(*p++);
3273               *r=ScaleShortToQuantum(quantum);
3274               r++;
3275
3276               if (ping_color_type == 4)
3277                 {
3278                   if (image->colors > 256)
3279                     quantum=((*p++) << 8);
3280                   else
3281                     quantum=0;
3282
3283                   quantum|=(*p++);
3284
3285                   alpha=ScaleShortToQuantum(quantum);
3286                   SetPixelAlpha(image,alpha,q);
3287
3288                   if (alpha != OpaqueAlpha)
3289                     found_transparent_pixel = MagickTrue;
3290
3291                   q+=GetPixelChannels(image);
3292                 }
3293
3294 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3295               *r++=(*p++);
3296               p++; /* strip low byte */
3297
3298               if (ping_color_type == 4)
3299                 {
3300                   SetPixelAlpha(image,*p++,q);
3301
3302                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3303                     found_transparent_pixel = MagickTrue;
3304
3305                   p++;
3306                   q+=GetPixelChannels(image);
3307                 }
3308 #endif
3309             }
3310
3311             break;
3312           }
3313
3314           default:
3315             break;
3316         }
3317
3318         /*
3319           Transfer image scanline.
3320         */
3321         r=quantum_scanline;
3322
3323         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3324
3325         if (q == (Quantum *) NULL)
3326           break;
3327         for (x=0; x < (ssize_t) image->columns; x++)
3328         {
3329           SetPixelIndex(image,*r++,q);
3330           q+=GetPixelChannels(image);
3331         }
3332
3333         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3334           break;
3335
3336         if ((image->previous == (Image *) NULL) && (num_passes == 1))
3337           {
3338             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3339               image->rows);
3340
3341             if (status == MagickFalse)
3342               break;
3343           }
3344       }
3345
3346       if ((image->previous == (Image *) NULL) && (num_passes != 1))
3347         {
3348           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3349
3350           if (status == MagickFalse)
3351             break;
3352         }
3353
3354       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3355     }
3356
3357     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3358       UndefinedPixelTrait;
3359
3360     if (logging != MagickFalse)
3361       {
3362         if (found_transparent_pixel != MagickFalse)
3363           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3364             "    Found transparent pixel");
3365         else
3366           {
3367             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3368               "    No transparent pixel was found");
3369
3370             ping_color_type&=0x03;
3371           }
3372       }
3373     }
3374
3375   if (quantum_info != (QuantumInfo *) NULL)
3376     quantum_info=DestroyQuantumInfo(quantum_info);
3377
3378   if (image->storage_class == PseudoClass)
3379     {
3380       PixelTrait
3381         alpha_trait;
3382
3383       alpha_trait=image->alpha_trait;
3384       image->alpha_trait=UndefinedPixelTrait;
3385       (void) SyncImage(image,exception);
3386       image->alpha_trait=alpha_trait;
3387     }
3388
3389   png_read_end(ping,end_info);
3390
3391   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3392       (ssize_t) image_info->first_scene && image->delay != 0)
3393     {
3394       png_destroy_read_struct(&ping,&ping_info,&end_info);
3395       ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3396       image->colors=2;
3397       (void) SetImageBackgroundColor(image,exception);
3398 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3399       UnlockSemaphoreInfo(ping_semaphore);
3400 #endif
3401       if (logging != MagickFalse)
3402         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3403           "  exit ReadOnePNGImage() early.");
3404       return(image);
3405     }
3406
3407   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3408     {
3409       ClassType
3410         storage_class;
3411
3412       /*
3413         Image has a transparent background.
3414       */
3415       storage_class=image->storage_class;
3416       image->alpha_trait=BlendPixelTrait;
3417
3418 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3419
3420       if (storage_class == PseudoClass)
3421         {
3422           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3423             {
3424               for (x=0; x < ping_num_trans; x++)
3425               {
3426                  image->colormap[x].alpha_trait=BlendPixelTrait;
3427                  image->colormap[x].alpha =
3428                    ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3429               }
3430             }
3431
3432           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3433             {
3434               for (x=0; x < (int) image->colors; x++)
3435               {
3436                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3437                      transparent_color.alpha)
3438                  {
3439                     image->colormap[x].alpha_trait=BlendPixelTrait;
3440                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3441                  }
3442               }
3443             }
3444           (void) SyncImage(image,exception);
3445         }
3446
3447 #if 1 /* Should have already been done above, but glennrp problem P10
3448        * needs this.
3449        */
3450       else
3451         {
3452           for (y=0; y < (ssize_t) image->rows; y++)
3453           {
3454             image->storage_class=storage_class;
3455             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3456
3457             if (q == (Quantum *) NULL)
3458               break;
3459
3460
3461             /* Caution: on a Q8 build, this does not distinguish between
3462              * 16-bit colors that differ only in the low byte
3463              */
3464             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3465             {
3466               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3467                   transparent_color.red &&
3468                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3469                   transparent_color.green &&
3470                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3471                   transparent_color.blue)
3472                 {
3473                   SetPixelAlpha(image,TransparentAlpha,q);
3474                 }
3475
3476 #if 0 /* I have not found a case where this is needed. */
3477               else
3478                 {
3479                   SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3480                 }
3481 #endif
3482
3483               q+=GetPixelChannels(image);
3484             }
3485
3486             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3487                break;
3488           }
3489         }
3490 #endif
3491
3492       image->storage_class=DirectClass;
3493     }
3494
3495   for (j = 0; j < 2; j++)
3496   {
3497     if (j == 0)
3498       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3499           MagickTrue : MagickFalse;
3500     else
3501       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3502           MagickTrue : MagickFalse;
3503
3504     if (status != MagickFalse)
3505       for (i=0; i < (ssize_t) num_text; i++)
3506       {
3507         /* Check for a profile */
3508
3509         if (logging != MagickFalse)
3510           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3511             "    Reading PNG text chunk");
3512
3513         if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3514           {
3515             (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3516                (int) i,exception);
3517             num_raw_profiles++;
3518           }
3519
3520         else
3521           {
3522             char
3523               *value;
3524
3525             length=text[i].text_length;
3526             value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3527               sizeof(*value));
3528             if (value == (char *) NULL)
3529               {
3530                 png_error(ping,"Memory allocation failed");
3531                 break;
3532               }
3533             *value='\0';
3534             (void) ConcatenateMagickString(value,text[i].text,length+2);
3535
3536             /* Don't save "density" or "units" property if we have a pHYs
3537              * chunk
3538              */
3539             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3540                 (LocaleCompare(text[i].key,"density") != 0 &&
3541                 LocaleCompare(text[i].key,"units") != 0))
3542                (void) SetImageProperty(image,text[i].key,value,exception);
3543
3544             if (logging != MagickFalse)
3545             {
3546               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3547                 "      length: %lu",(unsigned long) length);
3548               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3549                 "      Keyword: %s",text[i].key);
3550             }
3551
3552             value=DestroyString(value);
3553           }
3554       }
3555       num_text_total += num_text;
3556     }
3557
3558 #ifdef MNG_OBJECT_BUFFERS
3559   /*
3560     Store the object if necessary.
3561   */
3562   if (object_id && !mng_info->frozen[object_id])
3563     {
3564       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3565         {
3566           /*
3567             create a new object buffer.
3568           */
3569           mng_info->ob[object_id]=(MngBuffer *)
3570             AcquireMagickMemory(sizeof(MngBuffer));
3571
3572           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3573             {
3574               mng_info->ob[object_id]->image=(Image *) NULL;
3575               mng_info->ob[object_id]->reference_count=1;
3576             }
3577         }
3578
3579       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3580           mng_info->ob[object_id]->frozen)
3581         {
3582           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3583              png_error(ping,"Memory allocation failed");
3584
3585           if (mng_info->ob[object_id]->frozen)
3586             png_error(ping,"Cannot overwrite frozen MNG object buffer");
3587         }
3588
3589       else
3590         {
3591
3592           if (mng_info->ob[object_id]->image != (Image *) NULL)
3593             mng_info->ob[object_id]->image=DestroyImage
3594                 (mng_info->ob[object_id]->image);
3595
3596           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3597             exception);
3598
3599           if (mng_info->ob[object_id]->image != (Image *) NULL)
3600             mng_info->ob[object_id]->image->file=(FILE *) NULL;
3601
3602           else
3603             png_error(ping, "Cloning image for object buffer failed");
3604
3605           if (ping_width > 250000L || ping_height > 250000L)
3606              png_error(ping,"PNG Image dimensions are too large.");
3607
3608           mng_info->ob[object_id]->width=ping_width;
3609           mng_info->ob[object_id]->height=ping_height;
3610           mng_info->ob[object_id]->color_type=ping_color_type;
3611           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3612           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3613           mng_info->ob[object_id]->compression_method=
3614              ping_compression_method;
3615           mng_info->ob[object_id]->filter_method=ping_filter_method;
3616
3617           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3618             {
3619               png_colorp
3620                 plte;
3621
3622               /*
3623                 Copy the PLTE to the object buffer.
3624               */
3625               png_get_PLTE(ping,ping_info,&plte,&number_colors);
3626               mng_info->ob[object_id]->plte_length=number_colors;
3627
3628               for (i=0; i < number_colors; i++)
3629               {
3630                 mng_info->ob[object_id]->plte[i]=plte[i];
3631               }
3632             }
3633
3634           else
3635               mng_info->ob[object_id]->plte_length=0;
3636         }
3637     }
3638 #endif
3639
3640    /* Set image->alpha_trait to MagickTrue if the input colortype supports
3641     * alpha or if a valid tRNS chunk is present, no matter whether there
3642     * is actual transparency present.
3643     */
3644     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3645         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3646         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3647         BlendPixelTrait : UndefinedPixelTrait;
3648
3649 #if 0  /* I'm not sure what's wrong here but it does not work. */
3650     if (image->alpha_trait == BlendPixelTrait)
3651     {
3652       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3653         (void) SetImageType(image,GrayscaleMatteType,exception);
3654
3655       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3656         (void) SetImageType(image,PaletteMatteType,exception);
3657
3658       else
3659         (void) SetImageType(image,TrueColorMatteType,exception);
3660     }
3661
3662     else
3663     {
3664       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3665         (void) SetImageType(image,GrayscaleType,exception);
3666
3667       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3668         (void) SetImageType(image,PaletteType,exception);
3669
3670       else
3671         (void) SetImageType(image,TrueColorType,exception);
3672     }
3673 #endif
3674
3675    /* Set more properties for identify to retrieve */
3676    {
3677      char
3678        msg[MaxTextExtent];
3679
3680      if (num_text_total != 0)
3681        {
3682          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3683          (void) FormatLocaleString(msg,MaxTextExtent,
3684             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3685          (void) SetImageProperty(image,"png:text                 ",msg,
3686                 exception);
3687        }
3688
3689      if (num_raw_profiles != 0)
3690        {
3691          (void) FormatLocaleString(msg,MaxTextExtent,
3692             "%d were found", num_raw_profiles);
3693          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3694                 exception);
3695        }
3696
3697      if (ping_found_cHRM != MagickFalse)
3698        {
3699          (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3700             "chunk was found (see Chromaticity, above)");
3701          (void) SetImageProperty(image,"png:cHRM                 ",msg,
3702                 exception);
3703        }
3704
3705      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3706        {
3707          (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3708             "chunk was found (see Background color, above)");
3709          (void) SetImageProperty(image,"png:bKGD                 ",msg,
3710                 exception);
3711        }
3712
3713      (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3714         "chunk was found");
3715
3716 #if defined(PNG_iCCP_SUPPORTED)
3717      if (ping_found_iCCP != MagickFalse)
3718         (void) SetImageProperty(image,"png:iCCP                 ",msg,
3719                 exception);
3720 #endif
3721
3722      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3723         (void) SetImageProperty(image,"png:tRNS                 ",msg,
3724                 exception);
3725
3726 #if defined(PNG_sRGB_SUPPORTED)
3727      if (ping_found_sRGB != MagickFalse)
3728        {
3729          (void) FormatLocaleString(msg,MaxTextExtent,
3730             "intent=%d (%s)",
3731             (int) intent,
3732             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3733          (void) SetImageProperty(image,"png:sRGB                 ",msg,
3734                  exception);
3735        }
3736 #endif
3737
3738      if (ping_found_gAMA != MagickFalse)
3739        {
3740          (void) FormatLocaleString(msg,MaxTextExtent,
3741             "gamma=%.8g (See Gamma, above)",
3742             file_gamma);
3743          (void) SetImageProperty(image,"png:gAMA                 ",msg,
3744                 exception);
3745        }
3746
3747 #if defined(PNG_pHYs_SUPPORTED)
3748      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3749        {
3750          (void) FormatLocaleString(msg,MaxTextExtent,
3751             "x_res=%.10g, y_res=%.10g, units=%d",
3752             (double) x_resolution,(double) y_resolution, unit_type);
3753          (void) SetImageProperty(image,"png:pHYs                 ",msg,
3754                 exception);
3755        }
3756 #endif
3757
3758 #if defined(PNG_oFFs_SUPPORTED)
3759      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3760        {
3761          (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3762             (double) image->page.x,(double) image->page.y);
3763          (void) SetImageProperty(image,"png:oFFs                 ",msg,
3764                 exception);
3765        }
3766 #endif
3767
3768      if ((image->page.width != 0 && image->page.width != image->columns) ||
3769          (image->page.height != 0 && image->page.height != image->rows))
3770        {
3771          (void) FormatLocaleString(msg,MaxTextExtent,
3772             "width=%.20g, height=%.20g",
3773             (double) image->page.width,(double) image->page.height);
3774          (void) SetImageProperty(image,"png:vpAg                 ",msg,
3775                 exception);
3776        }
3777    }
3778
3779   /*
3780     Relinquish resources.
3781   */
3782   png_destroy_read_struct(&ping,&ping_info,&end_info);
3783
3784   ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3785
3786   if (logging != MagickFalse)
3787     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3788       "  exit ReadOnePNGImage()");
3789
3790 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3791   UnlockSemaphoreInfo(ping_semaphore);
3792 #endif
3793
3794   /* }  for navigation to beginning of SETJMP-protected block, revert to
3795    *    Throwing an Exception when an error occurs.
3796    */
3797
3798   return(image);
3799
3800 /* end of reading one PNG image */
3801 }
3802
3803 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3804 {
3805   Image
3806     *image,
3807     *previous;
3808
3809   MagickBooleanType
3810     have_mng_structure,
3811     logging,
3812     status;
3813
3814   MngInfo
3815     *mng_info;
3816
3817   char
3818     magic_number[MaxTextExtent];
3819
3820   ssize_t
3821     count;
3822
3823   /*
3824     Open image file.
3825   */
3826   assert(image_info != (const ImageInfo *) NULL);
3827   assert(image_info->signature == MagickSignature);
3828
3829   if (image_info->debug != MagickFalse)
3830     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3831       image_info->filename);
3832
3833   assert(exception != (ExceptionInfo *) NULL);
3834   assert(exception->signature == MagickSignature);
3835   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3836   image=AcquireImage(image_info,exception);
3837   mng_info=(MngInfo *) NULL;
3838   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3839
3840   if (status == MagickFalse)
3841     ThrowReaderException(FileOpenError,"UnableToOpenFile");
3842
3843   /*
3844     Verify PNG signature.
3845   */
3846   count=ReadBlob(image,8,(unsigned char *) magic_number);
3847
3848   if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3849     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3850
3851   /*
3852     Allocate a MngInfo structure.
3853   */
3854   have_mng_structure=MagickFalse;
3855   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3856
3857   if (mng_info == (MngInfo *) NULL)
3858     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3859
3860   /*
3861     Initialize members of the MngInfo structure.
3862   */
3863   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3864   mng_info->image=image;
3865   have_mng_structure=MagickTrue;
3866
3867   previous=image;
3868   image=ReadOnePNGImage(mng_info,image_info,exception);
3869   MngInfoFreeStruct(mng_info,&have_mng_structure);
3870
3871   if (image == (Image *) NULL)
3872     {
3873       if (previous != (Image *) NULL)
3874         {
3875           if (previous->signature != MagickSignature)
3876             ThrowReaderException(CorruptImageError,"CorruptImage");
3877
3878           (void) CloseBlob(previous);
3879           (void) DestroyImageList(previous);
3880         }
3881
3882       if (logging != MagickFalse)
3883         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3884           "exit ReadPNGImage() with error");
3885
3886       return((Image *) NULL);
3887     }
3888
3889   (void) CloseBlob(image);
3890
3891   if ((image->columns == 0) || (image->rows == 0))
3892     {
3893       if (logging != MagickFalse)
3894         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3895           "exit ReadPNGImage() with error.");
3896
3897       ThrowReaderException(CorruptImageError,"CorruptImage");
3898     }
3899
3900   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
3901       ((image->gamma < .45) || (image->gamma > .46)) &&
3902            !(image->chromaticity.red_primary.x>0.6399f &&
3903            image->chromaticity.red_primary.x<0.6401f &&
3904            image->chromaticity.red_primary.y>0.3299f &&
3905            image->chromaticity.red_primary.y<0.3301f &&
3906            image->chromaticity.green_primary.x>0.2999f &&
3907            image->chromaticity.green_primary.x<0.3001f &&
3908            image->chromaticity.green_primary.y>0.5999f &&
3909            image->chromaticity.green_primary.y<0.6001f &&
3910            image->chromaticity.blue_primary.x>0.1499f &&
3911            image->chromaticity.blue_primary.x<0.1501f &&
3912            image->chromaticity.blue_primary.y>0.0599f &&
3913            image->chromaticity.blue_primary.y<0.0601f &&
3914            image->chromaticity.white_point.x>0.3126f &&
3915            image->chromaticity.white_point.x<0.3128f &&
3916            image->chromaticity.white_point.y>0.3289f &&
3917            image->chromaticity.white_point.y<0.3291f))
3918     SetImageColorspace(image,RGBColorspace,exception);
3919
3920   if (logging != MagickFalse)
3921     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3922         "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3923             (double) image->page.width,(double) image->page.height,
3924             (double) image->page.x,(double) image->page.y);
3925
3926   if (logging != MagickFalse)
3927     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3928
3929   return(image);
3930 }
3931
3932
3933
3934 #if defined(JNG_SUPPORTED)
3935 /*
3936 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3937 %                                                                             %
3938 %                                                                             %
3939 %                                                                             %
3940 %   R e a d O n e J N G I m a g e                                             %
3941 %                                                                             %
3942 %                                                                             %
3943 %                                                                             %
3944 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3945 %
3946 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3947 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
3948 %  necessary for the new Image structure and returns a pointer to the new
3949 %  image.
3950 %
3951 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
3952 %
3953 %  The format of the ReadOneJNGImage method is:
3954 %
3955 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3956 %         ExceptionInfo *exception)
3957 %
3958 %  A description of each parameter follows:
3959 %
3960 %    o mng_info: Specifies a pointer to a MngInfo structure.
3961 %
3962 %    o image_info: the image info.
3963 %
3964 %    o exception: return any errors or warnings in this structure.
3965 %
3966 */
3967 static Image *ReadOneJNGImage(MngInfo *mng_info,
3968     const ImageInfo *image_info, ExceptionInfo *exception)
3969 {
3970   Image
3971     *alpha_image,
3972     *color_image,
3973     *image,
3974     *jng_image;
3975
3976   ImageInfo
3977     *alpha_image_info,
3978     *color_image_info;
3979
3980   MagickBooleanType
3981     logging;
3982
3983   ssize_t
3984     y;
3985
3986   MagickBooleanType
3987     status;
3988
3989   png_uint_32
3990     jng_height,
3991     jng_width;
3992
3993   png_byte
3994     jng_color_type,
3995     jng_image_sample_depth,
3996     jng_image_compression_method,
3997     jng_image_interlace_method,
3998     jng_alpha_sample_depth,
3999     jng_alpha_compression_method,
4000     jng_alpha_filter_method,
4001     jng_alpha_interlace_method;
4002
4003   register const Quantum
4004     *s;
4005
4006   register ssize_t
4007     i,
4008     x;
4009
4010   register Quantum
4011     *q;
4012
4013   register unsigned char
4014     *p;
4015
4016   unsigned int
4017     read_JSEP,
4018     reading_idat,
4019     skip_to_iend;
4020
4021   size_t
4022     length;
4023
4024   jng_alpha_compression_method=0;
4025   jng_alpha_sample_depth=8;
4026   jng_color_type=0;
4027   jng_height=0;
4028   jng_width=0;
4029   alpha_image=(Image *) NULL;
4030   color_image=(Image *) NULL;
4031   alpha_image_info=(ImageInfo *) NULL;
4032   color_image_info=(ImageInfo *) NULL;
4033
4034   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4035     "  Enter ReadOneJNGImage()");
4036
4037   image=mng_info->image;
4038
4039   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4040     {
4041       /*
4042         Allocate next image structure.
4043       */
4044       if (logging != MagickFalse)
4045         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4046            "  AcquireNextImage()");
4047
4048       AcquireNextImage(image_info,image,exception);
4049
4050       if (GetNextImageInList(image) == (Image *) NULL)
4051         return((Image *) NULL);
4052
4053       image=SyncNextImageInList(image);
4054     }
4055   mng_info->image=image;
4056
4057   /*
4058     Signature bytes have already been read.
4059   */
4060
4061   read_JSEP=MagickFalse;
4062   reading_idat=MagickFalse;
4063   skip_to_iend=MagickFalse;
4064   for (;;)
4065   {
4066     char
4067       type[MaxTextExtent];
4068
4069     unsigned char
4070       *chunk;
4071
4072     unsigned int
4073       count;
4074
4075     /*
4076       Read a new JNG chunk.
4077     */
4078     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4079       2*GetBlobSize(image));
4080
4081     if (status == MagickFalse)
4082       break;
4083
4084     type[0]='\0';
4085     (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4086     length=ReadBlobMSBLong(image);
4087     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4088
4089     if (logging != MagickFalse)
4090       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4091         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4092         type[0],type[1],type[2],type[3],(double) length);
4093
4094     if (length > PNG_UINT_31_MAX || count == 0)
4095       ThrowReaderException(CorruptImageError,"CorruptImage");
4096
4097     p=NULL;
4098     chunk=(unsigned char *) NULL;
4099
4100     if (length)
4101       {
4102         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4103
4104         if (chunk == (unsigned char *) NULL)
4105           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4106
4107         for (i=0; i < (ssize_t) length; i++)
4108           chunk[i]=(unsigned char) ReadBlobByte(image);
4109
4110         p=chunk;
4111       }
4112
4113     (void) ReadBlobMSBLong(image);  /* read crc word */
4114
4115     if (skip_to_iend)
4116       {
4117         if (length)
4118           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4119
4120         continue;
4121       }
4122
4123     if (memcmp(type,mng_JHDR,4) == 0)
4124       {
4125         if (length == 16)
4126           {
4127             jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4128                 (p[2] << 8) | p[3]);
4129             jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4130                 (p[6] << 8) | p[7]);
4131             jng_color_type=p[8];
4132             jng_image_sample_depth=p[9];
4133             jng_image_compression_method=p[10];
4134             jng_image_interlace_method=p[11];
4135
4136             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4137               NoInterlace;
4138
4139             jng_alpha_sample_depth=p[12];
4140             jng_alpha_compression_method=p[13];
4141             jng_alpha_filter_method=p[14];
4142             jng_alpha_interlace_method=p[15];
4143
4144             if (logging != MagickFalse)
4145               {
4146                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4147                   "    jng_width:      %16lu",(unsigned long) jng_width);
4148
4149                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4150                   "    jng_width:      %16lu",(unsigned long) jng_height);
4151
4152                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4153                   "    jng_color_type: %16d",jng_color_type);
4154
4155                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4156                   "    jng_image_sample_depth:      %3d",
4157                   jng_image_sample_depth);
4158
4159                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4160                   "    jng_image_compression_method:%3d",
4161                   jng_image_compression_method);
4162
4163                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4164                   "    jng_image_interlace_method:  %3d",
4165                   jng_image_interlace_method);
4166
4167                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4168                   "    jng_alpha_sample_depth:      %3d",
4169                   jng_alpha_sample_depth);
4170
4171                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4172                   "    jng_alpha_compression_method:%3d",
4173                   jng_alpha_compression_method);
4174
4175                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4176                   "    jng_alpha_filter_method:     %3d",
4177                   jng_alpha_filter_method);
4178
4179                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4180                   "    jng_alpha_interlace_method:  %3d",
4181                   jng_alpha_interlace_method);
4182               }
4183           }
4184
4185         if (length)
4186           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4187
4188         continue;
4189       }
4190
4191
4192     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4193         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4194          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4195       {
4196         /*
4197            o create color_image
4198            o open color_blob, attached to color_image
4199            o if (color type has alpha)
4200                open alpha_blob, attached to alpha_image
4201         */
4202
4203         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4204
4205         if (color_image_info == (ImageInfo *) NULL)
4206           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4207
4208         GetImageInfo(color_image_info);
4209         color_image=AcquireImage(color_image_info,exception);
4210
4211         if (color_image == (Image *) NULL)
4212           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4213
4214         if (logging != MagickFalse)
4215           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4216             "    Creating color_blob.");
4217
4218         (void) AcquireUniqueFilename(color_image->filename);
4219         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4220           exception);
4221
4222         if (status == MagickFalse)
4223           return((Image *) NULL);
4224
4225         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4226           {
4227             alpha_image_info=(ImageInfo *)
4228               AcquireMagickMemory(sizeof(ImageInfo));
4229
4230             if (alpha_image_info == (ImageInfo *) NULL)
4231               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4232
4233             GetImageInfo(alpha_image_info);
4234             alpha_image=AcquireImage(alpha_image_info,exception);
4235
4236             if (alpha_image == (Image *) NULL)
4237               {
4238                 alpha_image=DestroyImage(alpha_image);
4239                 ThrowReaderException(ResourceLimitError,
4240                   "MemoryAllocationFailed");
4241               }
4242
4243             if (logging != MagickFalse)
4244               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4245                 "    Creating alpha_blob.");
4246
4247             (void) AcquireUniqueFilename(alpha_image->filename);
4248             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4249               exception);
4250
4251             if (status == MagickFalse)
4252               return((Image *) NULL);
4253
4254             if (jng_alpha_compression_method == 0)
4255               {
4256                 unsigned char
4257                   data[18];
4258
4259                 if (logging != MagickFalse)
4260                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4261                     "    Writing IHDR chunk to alpha_blob.");
4262
4263                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4264                   "\211PNG\r\n\032\n");
4265
4266                 (void) WriteBlobMSBULong(alpha_image,13L);
4267                 PNGType(data,mng_IHDR);
4268                 LogPNGChunk(logging,mng_IHDR,13L);
4269                 PNGLong(data+4,jng_width);
4270                 PNGLong(data+8,jng_height);
4271                 data[12]=jng_alpha_sample_depth;
4272                 data[13]=0; /* color_type gray */
4273                 data[14]=0; /* compression method 0 */
4274                 data[15]=0; /* filter_method 0 */
4275                 data[16]=0; /* interlace_method 0 */
4276                 (void) WriteBlob(alpha_image,17,data);
4277                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4278               }
4279           }
4280         reading_idat=MagickTrue;
4281       }
4282
4283     if (memcmp(type,mng_JDAT,4) == 0)
4284       {
4285         /* Copy chunk to color_image->blob */
4286
4287         if (logging != MagickFalse)
4288           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4289             "    Copying JDAT chunk data to color_blob.");
4290
4291         (void) WriteBlob(color_image,length,chunk);
4292
4293         if (length)
4294           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4295
4296         continue;
4297       }
4298
4299     if (memcmp(type,mng_IDAT,4) == 0)
4300       {
4301         png_byte
4302            data[5];
4303
4304         /* Copy IDAT header and chunk data to alpha_image->blob */
4305
4306         if (image_info->ping == MagickFalse)
4307           {
4308             if (logging != MagickFalse)
4309               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4310                 "    Copying IDAT chunk data to alpha_blob.");
4311
4312             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4313             PNGType(data,mng_IDAT);
4314             LogPNGChunk(logging,mng_IDAT,length);
4315             (void) WriteBlob(alpha_image,4,data);
4316             (void) WriteBlob(alpha_image,length,chunk);
4317             (void) WriteBlobMSBULong(alpha_image,
4318               crc32(crc32(0,data,4),chunk,(uInt) length));
4319           }
4320
4321         if (length)
4322           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4323
4324         continue;
4325       }
4326
4327     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4328       {
4329         /* Copy chunk data to alpha_image->blob */
4330
4331         if (image_info->ping == MagickFalse)
4332           {
4333             if (logging != MagickFalse)
4334               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4335                 "    Copying JDAA chunk data to alpha_blob.");
4336
4337             (void) WriteBlob(alpha_image,length,chunk);
4338           }
4339
4340         if (length)
4341           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4342
4343         continue;
4344       }
4345
4346     if (memcmp(type,mng_JSEP,4) == 0)
4347       {
4348         read_JSEP=MagickTrue;
4349
4350         if (length)
4351           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4352
4353         continue;
4354       }
4355
4356     if (memcmp(type,mng_bKGD,4) == 0)
4357       {
4358         if (length == 2)
4359           {
4360             image->background_color.red=ScaleCharToQuantum(p[1]);
4361             image->background_color.green=image->background_color.red;
4362             image->background_color.blue=image->background_color.red;
4363           }
4364
4365         if (length == 6)
4366           {
4367             image->background_color.red=ScaleCharToQuantum(p[1]);
4368             image->background_color.green=ScaleCharToQuantum(p[3]);
4369             image->background_color.blue=ScaleCharToQuantum(p[5]);
4370           }
4371
4372         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4373         continue;
4374       }
4375
4376     if (memcmp(type,mng_gAMA,4) == 0)
4377       {
4378         if (length == 4)
4379           image->gamma=((float) mng_get_long(p))*0.00001;
4380
4381         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4382         continue;
4383       }
4384
4385     if (memcmp(type,mng_cHRM,4) == 0)
4386       {
4387         if (length == 32)
4388           {
4389             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4390             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4391             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4392             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4393             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4394             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4395             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4396             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4397           }
4398
4399         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4400         continue;
4401       }
4402
4403     if (memcmp(type,mng_sRGB,4) == 0)
4404       {
4405         if (length == 1)
4406           {
4407             image->rendering_intent=
4408               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4409             image->gamma=1.000f/2.200f;
4410             image->chromaticity.red_primary.x=0.6400f;
4411             image->chromaticity.red_primary.y=0.3300f;
4412             image->chromaticity.green_primary.x=0.3000f;
4413             image->chromaticity.green_primary.y=0.6000f;
4414             image->chromaticity.blue_primary.x=0.1500f;
4415             image->chromaticity.blue_primary.y=0.0600f;
4416             image->chromaticity.white_point.x=0.3127f;
4417             image->chromaticity.white_point.y=0.3290f;
4418           }
4419
4420         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4421         continue;
4422       }
4423
4424     if (memcmp(type,mng_oFFs,4) == 0)
4425       {
4426         if (length > 8)
4427           {
4428             image->page.x=(ssize_t) mng_get_long(p);
4429             image->page.y=(ssize_t) mng_get_long(&p[4]);
4430
4431             if ((int) p[8] != 0)
4432               {
4433                 image->page.x/=10000;
4434                 image->page.y/=10000;
4435               }
4436           }
4437
4438         if (length)
4439           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4440
4441         continue;
4442       }
4443
4444     if (memcmp(type,mng_pHYs,4) == 0)
4445       {
4446         if (length > 8)
4447           {
4448             image->resolution.x=(double) mng_get_long(p);
4449             image->resolution.y=(double) mng_get_long(&p[4]);
4450             if ((int) p[8] == PNG_RESOLUTION_METER)
4451               {
4452                 image->units=PixelsPerCentimeterResolution;
4453                 image->resolution.x=image->resolution.x/100.0f;
4454                 image->resolution.y=image->resolution.y/100.0f;
4455               }
4456           }
4457
4458         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4459         continue;
4460       }
4461
4462 #if 0
4463     if (memcmp(type,mng_iCCP,4) == 0)
4464       {
4465         /* To do: */
4466         if (length)
4467           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4468
4469         continue;
4470       }
4471 #endif
4472
4473     if (length)
4474       chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4475
4476     if (memcmp(type,mng_IEND,4))
4477       continue;
4478
4479     break;
4480   }
4481
4482
4483   /* IEND found */
4484
4485   /*
4486     Finish up reading image data:
4487
4488        o read main image from color_blob.
4489
4490        o close color_blob.
4491
4492        o if (color_type has alpha)
4493             if alpha_encoding is PNG
4494                read secondary image from alpha_blob via ReadPNG
4495             if alpha_encoding is JPEG
4496                read secondary image from alpha_blob via ReadJPEG
4497
4498        o close alpha_blob.
4499
4500        o copy intensity of secondary image into
4501          alpha samples of main image.
4502
4503        o destroy the secondary image.
4504   */
4505
4506   (void) CloseBlob(color_image);
4507
4508   if (logging != MagickFalse)
4509     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4510       "    Reading jng_image from color_blob.");
4511
4512   (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4513     color_image->filename);
4514
4515   color_image_info->ping=MagickFalse;   /* To do: avoid this */
4516   jng_image=ReadImage(color_image_info,exception);
4517
4518   if (jng_image == (Image *) NULL)
4519     return((Image *) NULL);
4520
4521   (void) RelinquishUniqueFileResource(color_image->filename);
4522   color_image=DestroyImage(color_image);
4523   color_image_info=DestroyImageInfo(color_image_info);
4524
4525   if (jng_image == (Image *) NULL)
4526     return((Image *) NULL);
4527
4528   if (logging != MagickFalse)
4529     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4530       "    Copying jng_image pixels to main image.");
4531
4532   image->rows=jng_height;
4533   image->columns=jng_width;
4534
4535   for (y=0; y < (ssize_t) image->rows; y++)
4536   {
4537     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4538     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4539     for (x=(ssize_t) image->columns; x != 0; x--)
4540     {
4541       SetPixelRed(image,GetPixelRed(jng_image,s),q);
4542       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4543       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4544       q+=GetPixelChannels(image);
4545       s+=GetPixelChannels(jng_image);
4546     }
4547
4548     if (SyncAuthenticPixels(image,exception) == MagickFalse)
4549       break;
4550   }
4551
4552   jng_image=DestroyImage(jng_image);
4553
4554   if (image_info->ping == MagickFalse)
4555     {
4556      if (jng_color_type >= 12)
4557        {
4558          if (jng_alpha_compression_method == 0)
4559            {
4560              png_byte
4561                data[5];
4562              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4563              PNGType(data,mng_IEND);
4564              LogPNGChunk(logging,mng_IEND,0L);
4565              (void) WriteBlob(alpha_image,4,data);
4566              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4567            }
4568
4569          (void) CloseBlob(alpha_image);
4570
4571          if (logging != MagickFalse)
4572            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4573              "    Reading alpha from alpha_blob.");
4574
4575          (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4576            "%s",alpha_image->filename);
4577
4578          jng_image=ReadImage(alpha_image_info,exception);
4579
4580          if (jng_image != (Image *) NULL)
4581            for (y=0; y < (ssize_t) image->rows; y++)
4582            {
4583              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4584                exception);
4585              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4586
4587              if (image->alpha_trait == BlendPixelTrait)
4588                for (x=(ssize_t) image->columns; x != 0; x--)
4589                {
4590                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4591                   q+=GetPixelChannels(image);
4592                   s+=GetPixelChannels(jng_image);
4593                }
4594
4595              else
4596                for (x=(ssize_t) image->columns; x != 0; x--)
4597                {
4598                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4599                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
4600                     image->alpha_trait=BlendPixelTrait;
4601                   q+=GetPixelChannels(image);
4602                   s+=GetPixelChannels(jng_image);
4603                }
4604
4605              if (SyncAuthenticPixels(image,exception) == MagickFalse)
4606                break;
4607            }
4608          (void) RelinquishUniqueFileResource(alpha_image->filename);
4609          alpha_image=DestroyImage(alpha_image);
4610          alpha_image_info=DestroyImageInfo(alpha_image_info);
4611          if (jng_image != (Image *) NULL)
4612            jng_image=DestroyImage(jng_image);
4613        }
4614     }
4615
4616   /* Read the JNG image.  */
4617
4618   if (mng_info->mng_type == 0)
4619     {
4620       mng_info->mng_width=jng_width;
4621       mng_info->mng_height=jng_height;
4622     }
4623
4624   if (image->page.width == 0 && image->page.height == 0)
4625     {
4626       image->page.width=jng_width;
4627       image->page.height=jng_height;
4628     }
4629
4630   if (image->page.x == 0 && image->page.y == 0)
4631     {
4632       image->page.x=mng_info->x_off[mng_info->object_id];
4633       image->page.y=mng_info->y_off[mng_info->object_id];
4634     }
4635
4636   else
4637     {
4638       image->page.y=mng_info->y_off[mng_info->object_id];
4639     }
4640
4641   mng_info->image_found++;
4642   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4643     2*GetBlobSize(image));
4644
4645   if (logging != MagickFalse)
4646     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4647       "  exit ReadOneJNGImage()");
4648
4649   return(image);
4650 }
4651
4652 /*
4653 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4654 %                                                                             %
4655 %                                                                             %
4656 %                                                                             %
4657 %   R e a d J N G I m a g e                                                   %
4658 %                                                                             %
4659 %                                                                             %
4660 %                                                                             %
4661 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4662 %
4663 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4664 %  (including the 8-byte signature)  and returns it.  It allocates the memory
4665 %  necessary for the new Image structure and returns a pointer to the new
4666 %  image.
4667 %
4668 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4669 %
4670 %  The format of the ReadJNGImage method is:
4671 %
4672 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4673 %         *exception)
4674 %
4675 %  A description of each parameter follows:
4676 %
4677 %    o image_info: the image info.
4678 %
4679 %    o exception: return any errors or warnings in this structure.
4680 %
4681 */
4682
4683 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4684 {
4685   Image
4686     *image,
4687     *previous;
4688
4689   MagickBooleanType
4690     have_mng_structure,
4691     logging,
4692     status;
4693
4694   MngInfo
4695     *mng_info;
4696
4697   char
4698     magic_number[MaxTextExtent];
4699
4700   size_t
4701     count;
4702
4703   /*
4704     Open image file.
4705   */
4706   assert(image_info != (const ImageInfo *) NULL);
4707   assert(image_info->signature == MagickSignature);
4708   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4709   assert(exception != (ExceptionInfo *) NULL);
4710   assert(exception->signature == MagickSignature);
4711   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4712   image=AcquireImage(image_info,exception);
4713   mng_info=(MngInfo *) NULL;
4714   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4715
4716   if (status == MagickFalse)
4717     return((Image *) NULL);
4718
4719   if (LocaleCompare(image_info->magick,"JNG") != 0)
4720     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4721
4722   /* Verify JNG signature.  */
4723
4724   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4725
4726   if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4727     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4728
4729   /* Allocate a MngInfo structure.  */
4730
4731   have_mng_structure=MagickFalse;
4732   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4733
4734   if (mng_info == (MngInfo *) NULL)
4735     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4736
4737   /* Initialize members of the MngInfo structure.  */
4738
4739   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4740   have_mng_structure=MagickTrue;
4741
4742   mng_info->image=image;
4743   previous=image;
4744   image=ReadOneJNGImage(mng_info,image_info,exception);
4745   MngInfoFreeStruct(mng_info,&have_mng_structure);
4746
4747   if (image == (Image *) NULL)
4748     {
4749       if (IsImageObject(previous) != MagickFalse)
4750         {
4751           (void) CloseBlob(previous);
4752           (void) DestroyImageList(previous);
4753         }
4754
4755       if (logging != MagickFalse)
4756         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4757           "exit ReadJNGImage() with error");
4758
4759       return((Image *) NULL);
4760     }
4761   (void) CloseBlob(image);
4762
4763   if (image->columns == 0 || image->rows == 0)
4764     {
4765       if (logging != MagickFalse)
4766         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4767           "exit ReadJNGImage() with error");
4768
4769       ThrowReaderException(CorruptImageError,"CorruptImage");
4770     }
4771
4772   if (logging != MagickFalse)
4773     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4774
4775   return(image);
4776 }
4777 #endif
4778
4779 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4780 {
4781   char
4782     page_geometry[MaxTextExtent];
4783
4784   Image
4785     *image,
4786     *previous;
4787
4788   MagickBooleanType
4789     logging,
4790     have_mng_structure;
4791
4792   volatile int
4793     first_mng_object,
4794     object_id,
4795     term_chunk_found,
4796     skip_to_iend;
4797
4798   volatile ssize_t
4799     image_count=0;
4800
4801   MagickBooleanType
4802     status;
4803
4804   MagickOffsetType
4805     offset;
4806
4807   MngInfo
4808     *mng_info;
4809
4810   MngBox
4811     default_fb,
4812     fb,
4813     previous_fb;
4814
4815 #if defined(MNG_INSERT_LAYERS)
4816   PixelInfo
4817     mng_background_color;
4818 #endif
4819
4820   register unsigned char
4821     *p;
4822
4823   register ssize_t
4824     i;
4825
4826   size_t
4827     count;
4828
4829   ssize_t
4830     loop_level;
4831
4832   volatile short
4833     skipping_loop;
4834
4835 #if defined(MNG_INSERT_LAYERS)
4836   unsigned int
4837     mandatory_back=0;
4838 #endif
4839
4840   volatile unsigned int
4841 #ifdef MNG_OBJECT_BUFFERS
4842     mng_background_object=0,
4843 #endif
4844     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4845
4846   size_t
4847     default_frame_timeout,
4848     frame_timeout,
4849 #if defined(MNG_INSERT_LAYERS)
4850     image_height,
4851     image_width,
4852 #endif
4853     length;
4854
4855   /* These delays are all measured in image ticks_per_second,
4856    * not in MNG ticks_per_second
4857    */
4858   volatile size_t
4859     default_frame_delay,
4860     final_delay,
4861     final_image_delay,
4862     frame_delay,
4863 #if defined(MNG_INSERT_LAYERS)
4864     insert_layers,
4865 #endif
4866     mng_iterations=1,
4867     simplicity=0,
4868     subframe_height=0,
4869     subframe_width=0;
4870
4871   previous_fb.top=0;
4872   previous_fb.bottom=0;
4873   previous_fb.left=0;
4874   previous_fb.right=0;
4875   default_fb.top=0;
4876   default_fb.bottom=0;
4877   default_fb.left=0;
4878   default_fb.right=0;
4879
4880   /* Open image file.  */
4881
4882   assert(image_info != (const ImageInfo *) NULL);
4883   assert(image_info->signature == MagickSignature);
4884   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4885   assert(exception != (ExceptionInfo *) NULL);
4886   assert(exception->signature == MagickSignature);
4887   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4888   image=AcquireImage(image_info,exception);
4889   mng_info=(MngInfo *) NULL;
4890   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4891
4892   if (status == MagickFalse)
4893     return((Image *) NULL);
4894
4895   first_mng_object=MagickFalse;
4896   skipping_loop=(-1);
4897   have_mng_structure=MagickFalse;
4898
4899   /* Allocate a MngInfo structure.  */
4900
4901   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4902
4903   if (mng_info == (MngInfo *) NULL)
4904     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4905
4906   /* Initialize members of the MngInfo structure.  */
4907
4908   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4909   mng_info->image=image;
4910   have_mng_structure=MagickTrue;
4911
4912   if (LocaleCompare(image_info->magick,"MNG") == 0)
4913     {
4914       char
4915         magic_number[MaxTextExtent];
4916
4917       /* Verify MNG signature.  */
4918       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4919       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4920         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4921
4922       /* Initialize some nonzero members of the MngInfo structure.  */
4923       for (i=0; i < MNG_MAX_OBJECTS; i++)
4924       {
4925         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4926         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4927       }
4928       mng_info->exists[0]=MagickTrue;
4929     }
4930
4931   first_mng_object=MagickTrue;
4932   mng_type=0;
4933 #if defined(MNG_INSERT_LAYERS)
4934   insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4935 #endif
4936   default_frame_delay=0;
4937   default_frame_timeout=0;
4938   frame_delay=0;
4939   final_delay=1;
4940   mng_info->ticks_per_second=1UL*image->ticks_per_second;
4941   object_id=0;
4942   skip_to_iend=MagickFalse;
4943   term_chunk_found=MagickFalse;
4944   mng_info->framing_mode=1;
4945 #if defined(MNG_INSERT_LAYERS)
4946   mandatory_back=MagickFalse;
4947 #endif
4948 #if defined(MNG_INSERT_LAYERS)
4949   mng_background_color=image->background_color;
4950 #endif
4951   default_fb=mng_info->frame;
4952   previous_fb=mng_info->frame;
4953   do
4954   {
4955     char
4956       type[MaxTextExtent];
4957
4958     if (LocaleCompare(image_info->magick,"MNG") == 0)
4959       {
4960         unsigned char
4961           *chunk;
4962
4963         /*
4964           Read a new chunk.
4965         */
4966         type[0]='\0';
4967         (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4968         length=ReadBlobMSBLong(image);
4969         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4970
4971         if (logging != MagickFalse)
4972           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4973            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
4974            type[0],type[1],type[2],type[3],(double) length);
4975
4976         if (length > PNG_UINT_31_MAX)
4977           status=MagickFalse;
4978
4979         if (count == 0)
4980           ThrowReaderException(CorruptImageError,"CorruptImage");
4981
4982         p=NULL;
4983         chunk=(unsigned char *) NULL;
4984
4985         if (length)
4986           {
4987             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4988
4989             if (chunk == (unsigned char *) NULL)
4990               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4991
4992             for (i=0; i < (ssize_t) length; i++)
4993               chunk[i]=(unsigned char) ReadBlobByte(image);
4994
4995             p=chunk;
4996           }
4997
4998         (void) ReadBlobMSBLong(image);  /* read crc word */
4999
5000 #if !defined(JNG_SUPPORTED)
5001         if (memcmp(type,mng_JHDR,4) == 0)
5002           {
5003             skip_to_iend=MagickTrue;
5004
5005             if (mng_info->jhdr_warning == 0)
5006               (void) ThrowMagickException(exception,GetMagickModule(),
5007                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5008
5009             mng_info->jhdr_warning++;
5010           }
5011 #endif
5012         if (memcmp(type,mng_DHDR,4) == 0)
5013           {
5014             skip_to_iend=MagickTrue;
5015
5016             if (mng_info->dhdr_warning == 0)
5017               (void) ThrowMagickException(exception,GetMagickModule(),
5018                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5019
5020             mng_info->dhdr_warning++;
5021           }
5022         if (memcmp(type,mng_MEND,4) == 0)
5023           break;
5024
5025         if (skip_to_iend)
5026           {
5027             if (memcmp(type,mng_IEND,4) == 0)
5028               skip_to_iend=MagickFalse;
5029
5030             if (length)
5031               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5032
5033             if (logging != MagickFalse)
5034               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5035                 "  Skip to IEND.");
5036
5037             continue;
5038           }
5039
5040         if (memcmp(type,mng_MHDR,4) == 0)
5041           {
5042             mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5043                 (p[2] << 8) | p[3]);
5044
5045             mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5046                 (p[6] << 8) | p[7]);
5047
5048             if (logging != MagickFalse)
5049               {
5050                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5051                   "  MNG width: %.20g",(double) mng_info->mng_width);
5052                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5053                   "  MNG height: %.20g",(double) mng_info->mng_height);
5054               }
5055
5056             p+=8;
5057             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5058
5059             if (mng_info->ticks_per_second == 0)
5060               default_frame_delay=0;
5061
5062             else
5063               default_frame_delay=1UL*image->ticks_per_second/
5064                 mng_info->ticks_per_second;
5065
5066             frame_delay=default_frame_delay;
5067             simplicity=0;
5068
5069             if (length > 16)
5070               {
5071                 p+=16;
5072                 simplicity=(size_t) mng_get_long(p);
5073               }
5074
5075             mng_type=1;    /* Full MNG */
5076
5077             if ((simplicity != 0) && ((simplicity | 11) == 11))
5078               mng_type=2; /* LC */
5079
5080             if ((simplicity != 0) && ((simplicity | 9) == 9))
5081               mng_type=3; /* VLC */
5082
5083 #if defined(MNG_INSERT_LAYERS)
5084             if (mng_type != 3)
5085               insert_layers=MagickTrue;
5086 #endif
5087             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5088               {
5089                 /* Allocate next image structure.  */
5090                 AcquireNextImage(image_info,image,exception);
5091
5092                 if (GetNextImageInList(image) == (Image *) NULL)
5093                   return((Image *) NULL);
5094
5095                 image=SyncNextImageInList(image);
5096                 mng_info->image=image;
5097               }
5098
5099             if ((mng_info->mng_width > 65535L) ||
5100                 (mng_info->mng_height > 65535L))
5101               ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5102
5103             (void) FormatLocaleString(page_geometry,MaxTextExtent,
5104               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5105               mng_info->mng_height);
5106
5107             mng_info->frame.left=0;
5108             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5109             mng_info->frame.top=0;
5110             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5111             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5112
5113             for (i=0; i < MNG_MAX_OBJECTS; i++)
5114               mng_info->object_clip[i]=mng_info->frame;
5115
5116             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5117             continue;
5118           }
5119
5120         if (memcmp(type,mng_TERM,4) == 0)
5121           {
5122             int
5123               repeat=0;
5124
5125
5126             if (length)
5127               repeat=p[0];
5128
5129             if (repeat == 3)
5130               {
5131                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5132                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5133
5134                 if (mng_iterations == PNG_UINT_31_MAX)
5135                   mng_iterations=0;
5136
5137                 image->iterations=mng_iterations;
5138                 term_chunk_found=MagickTrue;
5139               }
5140
5141             if (logging != MagickFalse)
5142               {
5143                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5144                   "    repeat=%d",repeat);
5145
5146                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5147                   "    final_delay=%.20g",(double) final_delay);
5148
5149                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5150                   "    image->iterations=%.20g",(double) image->iterations);
5151               }
5152
5153             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5154             continue;
5155           }
5156         if (memcmp(type,mng_DEFI,4) == 0)
5157           {
5158             if (mng_type == 3)
5159               (void) ThrowMagickException(exception,GetMagickModule(),
5160                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5161                 image->filename);
5162
5163             object_id=(p[0] << 8) | p[1];
5164
5165             if (mng_type == 2 && object_id != 0)
5166               (void) ThrowMagickException(exception,GetMagickModule(),
5167                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5168                 image->filename);
5169
5170             if (object_id > MNG_MAX_OBJECTS)
5171               {
5172                 /*
5173                   Instead of using a warning we should allocate a larger
5174                   MngInfo structure and continue.
5175                 */
5176                 (void) ThrowMagickException(exception,GetMagickModule(),
5177                   CoderError,"object id too large","`%s'",image->filename);
5178                 object_id=MNG_MAX_OBJECTS;
5179               }
5180
5181             if (mng_info->exists[object_id])
5182               if (mng_info->frozen[object_id])
5183                 {
5184                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5185                   (void) ThrowMagickException(exception,
5186                     GetMagickModule(),CoderError,
5187                     "DEFI cannot redefine a frozen MNG object","`%s'",
5188                     image->filename);
5189                   continue;
5190                 }
5191
5192             mng_info->exists[object_id]=MagickTrue;
5193
5194             if (length > 2)
5195               mng_info->invisible[object_id]=p[2];
5196
5197             /*
5198               Extract object offset info.
5199             */
5200             if (length > 11)
5201               {
5202                 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5203                     (p[5] << 16) | (p[6] << 8) | p[7]);
5204
5205                 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5206                     (p[9] << 16) | (p[10] << 8) | p[11]);
5207
5208                 if (logging != MagickFalse)
5209                   {
5210                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5211                       "  x_off[%d]: %.20g",object_id,(double)
5212                       mng_info->x_off[object_id]);
5213
5214                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5215                       "  y_off[%d]: %.20g",object_id,(double)
5216                       mng_info->y_off[object_id]);
5217                   }
5218               }
5219
5220             /*
5221               Extract object clipping info.
5222             */
5223             if (length > 27)
5224               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5225                 &p[12]);
5226
5227             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5228             continue;
5229           }
5230         if (memcmp(type,mng_bKGD,4) == 0)
5231           {
5232             mng_info->have_global_bkgd=MagickFalse;
5233
5234             if (length > 5)
5235               {
5236                 mng_info->mng_global_bkgd.red=
5237                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5238
5239                 mng_info->mng_global_bkgd.green=
5240                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5241
5242                 mng_info->mng_global_bkgd.blue=
5243                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5244
5245                 mng_info->have_global_bkgd=MagickTrue;
5246               }
5247
5248             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5249             continue;
5250           }
5251         if (memcmp(type,mng_BACK,4) == 0)
5252           {
5253 #if defined(MNG_INSERT_LAYERS)
5254             if (length > 6)
5255               mandatory_back=p[6];
5256
5257             else
5258               mandatory_back=0;
5259
5260             if (mandatory_back && length > 5)
5261               {
5262                 mng_background_color.red=
5263                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5264
5265                 mng_background_color.green=
5266                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5267
5268                 mng_background_color.blue=
5269                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5270
5271                 mng_background_color.alpha=OpaqueAlpha;
5272               }
5273
5274 #ifdef MNG_OBJECT_BUFFERS
5275             if (length > 8)
5276               mng_background_object=(p[7] << 8) | p[8];
5277 #endif
5278 #endif
5279             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5280             continue;
5281           }
5282
5283         if (memcmp(type,mng_PLTE,4) == 0)
5284           {
5285             /* Read global PLTE.  */
5286
5287             if (length && (length < 769))
5288               {
5289                 if (mng_info->global_plte == (png_colorp) NULL)
5290                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5291                     sizeof(*mng_info->global_plte));
5292
5293                 for (i=0; i < (ssize_t) (length/3); i++)
5294                 {
5295                   mng_info->global_plte[i].red=p[3*i];
5296                   mng_info->global_plte[i].green=p[3*i+1];
5297                   mng_info->global_plte[i].blue=p[3*i+2];
5298                 }
5299
5300                 mng_info->global_plte_length=(unsigned int) (length/3);
5301               }
5302 #ifdef MNG_LOOSE
5303             for ( ; i < 256; i++)
5304             {
5305               mng_info->global_plte[i].red=i;
5306               mng_info->global_plte[i].green=i;
5307               mng_info->global_plte[i].blue=i;
5308             }
5309
5310             if (length)
5311               mng_info->global_plte_length=256;
5312 #endif
5313             else
5314               mng_info->global_plte_length=0;
5315
5316             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5317             continue;
5318           }
5319
5320         if (memcmp(type,mng_tRNS,4) == 0)
5321           {
5322             /* read global tRNS */
5323
5324             if (length < 257)
5325               for (i=0; i < (ssize_t) length; i++)
5326                 mng_info->global_trns[i]=p[i];
5327
5328 #ifdef MNG_LOOSE
5329             for ( ; i < 256; i++)
5330               mng_info->global_trns[i]=255;
5331 #endif
5332             mng_info->global_trns_length=(unsigned int) length;
5333             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5334             continue;
5335           }
5336         if (memcmp(type,mng_gAMA,4) == 0)
5337           {
5338             if (length == 4)
5339               {
5340                 ssize_t
5341                   igamma;
5342
5343                 igamma=mng_get_long(p);
5344                 mng_info->global_gamma=((float) igamma)*0.00001;
5345                 mng_info->have_global_gama=MagickTrue;
5346               }
5347
5348             else
5349               mng_info->have_global_gama=MagickFalse;
5350
5351             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5352             continue;
5353           }
5354
5355         if (memcmp(type,mng_cHRM,4) == 0)
5356           {
5357             /* Read global cHRM */
5358
5359             if (length == 32)
5360               {
5361                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5362                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5363                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5364                 mng_info->global_chrm.red_primary.y=0.00001*
5365                   mng_get_long(&p[12]);
5366                 mng_info->global_chrm.green_primary.x=0.00001*
5367                   mng_get_long(&p[16]);
5368                 mng_info->global_chrm.green_primary.y=0.00001*
5369                   mng_get_long(&p[20]);
5370                 mng_info->global_chrm.blue_primary.x=0.00001*
5371                   mng_get_long(&p[24]);
5372                 mng_info->global_chrm.blue_primary.y=0.00001*
5373                   mng_get_long(&p[28]);
5374                 mng_info->have_global_chrm=MagickTrue;
5375               }
5376             else
5377               mng_info->have_global_chrm=MagickFalse;
5378
5379             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5380             continue;
5381           }
5382
5383         if (memcmp(type,mng_sRGB,4) == 0)
5384           {
5385             /*
5386               Read global sRGB.
5387             */
5388             if (length)
5389               {
5390                 mng_info->global_srgb_intent=
5391                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5392                 mng_info->have_global_srgb=MagickTrue;
5393               }
5394             else
5395               mng_info->have_global_srgb=MagickFalse;
5396
5397             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5398             continue;
5399           }
5400
5401         if (memcmp(type,mng_iCCP,4) == 0)
5402           {
5403             /* To do: */
5404
5405             /*
5406               Read global iCCP.
5407             */
5408             if (length)
5409               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5410
5411             continue;
5412           }
5413
5414         if (memcmp(type,mng_FRAM,4) == 0)
5415           {
5416             if (mng_type == 3)
5417               (void) ThrowMagickException(exception,GetMagickModule(),
5418                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5419                 image->filename);
5420
5421             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5422               image->delay=frame_delay;
5423
5424             frame_delay=default_frame_delay;
5425             frame_timeout=default_frame_timeout;
5426             fb=default_fb;
5427
5428             if (length)
5429               if (p[0])
5430                 mng_info->framing_mode=p[0];
5431
5432             if (logging != MagickFalse)
5433               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5434                 "    Framing_mode=%d",mng_info->framing_mode);
5435
5436             if (length > 6)
5437               {
5438                 /* Note the delay and frame clipping boundaries.  */
5439
5440                 p++; /* framing mode */
5441
5442                 while (*p && ((p-chunk) < (ssize_t) length))
5443                   p++;  /* frame name */
5444
5445                 p++;  /* frame name terminator */
5446
5447                 if ((p-chunk) < (ssize_t) (length-4))
5448                   {
5449                     int
5450                       change_delay,
5451                       change_timeout,
5452                       change_clipping;
5453
5454                     change_delay=(*p++);
5455                     change_timeout=(*p++);
5456                     change_clipping=(*p++);
5457                     p++; /* change_sync */
5458
5459                     if (change_delay)
5460                       {
5461                         frame_delay=1UL*image->ticks_per_second*
5462                           mng_get_long(p);
5463
5464                         if (mng_info->ticks_per_second != 0)
5465                           frame_delay/=mng_info->ticks_per_second;
5466
5467                         else
5468                           frame_delay=PNG_UINT_31_MAX;
5469
5470                         if (change_delay == 2)
5471                           default_frame_delay=frame_delay;
5472
5473                         p+=4;
5474
5475                         if (logging != MagickFalse)
5476                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5477                             "    Framing_delay=%.20g",(double) frame_delay);
5478                       }
5479
5480                     if (change_timeout)
5481                       {
5482                         frame_timeout=1UL*image->ticks_per_second*
5483                           mng_get_long(p);
5484
5485                         if (mng_info->ticks_per_second != 0)
5486                           frame_timeout/=mng_info->ticks_per_second;
5487
5488                         else
5489                           frame_timeout=PNG_UINT_31_MAX;
5490
5491                         if (change_delay == 2)
5492                           default_frame_timeout=frame_timeout;
5493
5494                         p+=4;
5495
5496                         if (logging != MagickFalse)
5497                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5498                             "    Framing_timeout=%.20g",(double) frame_timeout);
5499                       }
5500
5501                     if (change_clipping)
5502                       {
5503                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5504                         p+=17;
5505                         previous_fb=fb;
5506
5507                         if (logging != MagickFalse)
5508                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5509                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5510                             (double) fb.left,(double) fb.right,(double) fb.top,
5511                             (double) fb.bottom);
5512
5513                         if (change_clipping == 2)
5514                           default_fb=fb;
5515                       }
5516                   }
5517               }
5518             mng_info->clip=fb;
5519             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5520
5521             subframe_width=(size_t) (mng_info->clip.right
5522                -mng_info->clip.left);
5523
5524             subframe_height=(size_t) (mng_info->clip.bottom
5525                -mng_info->clip.top);
5526             /*
5527               Insert a background layer behind the frame if framing_mode is 4.
5528             */
5529 #if defined(MNG_INSERT_LAYERS)
5530             if (logging != MagickFalse)
5531               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5532                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
5533                 subframe_width,(double) subframe_height);
5534
5535             if (insert_layers && (mng_info->framing_mode == 4) &&
5536                 (subframe_width) && (subframe_height))
5537               {
5538                 /* Allocate next image structure.  */
5539                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5540                   {
5541                     AcquireNextImage(image_info,image,exception);
5542
5543                     if (GetNextImageInList(image) == (Image *) NULL)
5544                       {
5545                         image=DestroyImageList(image);
5546                         MngInfoFreeStruct(mng_info,&have_mng_structure);
5547                         return((Image *) NULL);
5548                       }
5549
5550                     image=SyncNextImageInList(image);
5551                   }
5552
5553                 mng_info->image=image;
5554
5555                 if (term_chunk_found)
5556                   {
5557                     image->start_loop=MagickTrue;
5558                     image->iterations=mng_iterations;
5559                     term_chunk_found=MagickFalse;
5560                   }
5561
5562                 else
5563                     image->start_loop=MagickFalse;
5564
5565                 image->columns=subframe_width;
5566                 image->rows=subframe_height;
5567                 image->page.width=subframe_width;
5568                 image->page.height=subframe_height;
5569                 image->page.x=mng_info->clip.left;
5570                 image->page.y=mng_info->clip.top;
5571                 image->background_color=mng_background_color;
5572                 image->alpha_trait=UndefinedPixelTrait;
5573                 image->delay=0;
5574                 (void) SetImageBackgroundColor(image,exception);
5575
5576                 if (logging != MagickFalse)
5577                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5578                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5579                     (double) mng_info->clip.left,(double) mng_info->clip.right,
5580                     (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5581               }
5582 #endif
5583             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5584             continue;
5585           }
5586         if (memcmp(type,mng_CLIP,4) == 0)
5587           {
5588             unsigned int
5589               first_object,
5590               last_object;
5591
5592             /*
5593               Read CLIP.
5594             */
5595             first_object=(p[0] << 8) | p[1];
5596             last_object=(p[2] << 8) | p[3];
5597
5598             for (i=(int) first_object; i <= (int) last_object; i++)
5599             {
5600               if (mng_info->exists[i] && !mng_info->frozen[i])
5601                 {
5602                   MngBox
5603                     box;
5604
5605                   box=mng_info->object_clip[i];
5606                   mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5607                 }
5608             }
5609
5610             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5611             continue;
5612           }
5613         if (memcmp(type,mng_SAVE,4) == 0)
5614           {
5615             for (i=1; i < MNG_MAX_OBJECTS; i++)
5616               if (mng_info->exists[i])
5617                 {
5618                  mng_info->frozen[i]=MagickTrue;
5619 #ifdef MNG_OBJECT_BUFFERS
5620                  if (mng_info->ob[i] != (MngBuffer *) NULL)
5621                     mng_info->ob[i]->frozen=MagickTrue;
5622 #endif
5623                 }
5624
5625             if (length)
5626               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5627
5628             continue;
5629           }
5630
5631         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5632           {
5633             /* Read DISC or SEEK.  */
5634
5635             if ((length == 0) || !memcmp(type,mng_SEEK,4))
5636               {
5637                 for (i=1; i < MNG_MAX_OBJECTS; i++)
5638                   MngInfoDiscardObject(mng_info,i);
5639               }
5640
5641             else
5642               {
5643                 register ssize_t
5644                   j;
5645
5646                 for (j=0; j < (ssize_t) length; j+=2)
5647                 {
5648                   i=p[j] << 8 | p[j+1];
5649                   MngInfoDiscardObject(mng_info,i);
5650                 }
5651               }
5652
5653             if (length)
5654               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5655
5656             continue;
5657           }
5658
5659         if (memcmp(type,mng_MOVE,4) == 0)
5660           {
5661             size_t
5662               first_object,
5663               last_object;
5664
5665             /* read MOVE */
5666
5667             first_object=(p[0] << 8) | p[1];
5668             last_object=(p[2] << 8) | p[3];
5669             for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5670             {
5671               if (mng_info->exists[i] && !mng_info->frozen[i])
5672                 {
5673                   MngPair
5674                     new_pair;
5675
5676                   MngPair
5677                     old_pair;
5678
5679                   old_pair.a=mng_info->x_off[i];
5680                   old_pair.b=mng_info->y_off[i];
5681                   new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5682                   mng_info->x_off[i]=new_pair.a;
5683                   mng_info->y_off[i]=new_pair.b;
5684                 }
5685             }
5686
5687             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5688             continue;
5689           }
5690
5691         if (memcmp(type,mng_LOOP,4) == 0)
5692           {
5693             ssize_t loop_iters=1;
5694             loop_level=chunk[0];
5695             mng_info->loop_active[loop_level]=1;  /* mark loop active */
5696
5697             /* Record starting point.  */
5698             loop_iters=mng_get_long(&chunk[1]);
5699
5700             if (logging != MagickFalse)
5701               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5702                 "  LOOP level %.20g has %.20g iterations ",(double) loop_level,
5703                 (double) loop_iters);
5704
5705             if (loop_iters == 0)
5706               skipping_loop=loop_level;
5707
5708             else
5709               {
5710                 mng_info->loop_jump[loop_level]=TellBlob(image);
5711                 mng_info->loop_count[loop_level]=loop_iters;
5712               }
5713
5714             mng_info->loop_iteration[loop_level]=0;
5715             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5716             continue;
5717           }
5718
5719         if (memcmp(type,mng_ENDL,4) == 0)
5720           {
5721             loop_level=chunk[0];
5722
5723             if (skipping_loop > 0)
5724               {
5725                 if (skipping_loop == loop_level)
5726                   {
5727                     /*
5728                       Found end of zero-iteration loop.
5729                     */
5730                     skipping_loop=(-1);
5731                     mng_info->loop_active[loop_level]=0;
5732                   }
5733               }
5734
5735             else
5736               {
5737                 if (mng_info->loop_active[loop_level] == 1)
5738                   {
5739                     mng_info->loop_count[loop_level]--;
5740                     mng_info->loop_iteration[loop_level]++;
5741
5742                     if (logging != MagickFalse)
5743                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5744                         "  ENDL: LOOP level %.20g has %.20g remaining iters ",
5745                         (double) loop_level,(double)
5746                         mng_info->loop_count[loop_level]);
5747
5748                     if (mng_info->loop_count[loop_level] != 0)
5749                       {
5750                         offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5751                           SEEK_SET);
5752
5753                         if (offset < 0)
5754                           ThrowReaderException(CorruptImageError,
5755                             "ImproperImageHeader");
5756                       }
5757
5758                     else
5759                       {
5760                         short
5761                           last_level;
5762
5763                         /*
5764                           Finished loop.
5765                         */
5766                         mng_info->loop_active[loop_level]=0;
5767                         last_level=(-1);
5768                         for (i=0; i < loop_level; i++)
5769                           if (mng_info->loop_active[i] == 1)
5770                             last_level=(short) i;
5771                         loop_level=last_level;
5772                       }
5773                   }
5774               }
5775
5776             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5777             continue;
5778           }
5779
5780         if (memcmp(type,mng_CLON,4) == 0)
5781           {
5782             if (mng_info->clon_warning == 0)
5783               (void) ThrowMagickException(exception,GetMagickModule(),
5784                 CoderError,"CLON is not implemented yet","`%s'",
5785                 image->filename);
5786
5787             mng_info->clon_warning++;
5788           }
5789
5790         if (memcmp(type,mng_MAGN,4) == 0)
5791           {
5792             png_uint_16
5793               magn_first,
5794               magn_last,
5795               magn_mb,
5796               magn_ml,
5797               magn_mr,
5798               magn_mt,
5799               magn_mx,
5800               magn_my,
5801               magn_methx,
5802               magn_methy;
5803
5804             if (length > 1)
5805               magn_first=(p[0] << 8) | p[1];
5806
5807             else
5808               magn_first=0;
5809
5810             if (length > 3)
5811               magn_last=(p[2] << 8) | p[3];
5812
5813             else
5814               magn_last=magn_first;
5815 #ifndef MNG_OBJECT_BUFFERS
5816             if (magn_first || magn_last)
5817               if (mng_info->magn_warning == 0)
5818                 {
5819                   (void) ThrowMagickException(exception,
5820                      GetMagickModule(),CoderError,
5821                      "MAGN is not implemented yet for nonzero objects",
5822                      "`%s'",image->filename);
5823
5824                    mng_info->magn_warning++;
5825                 }
5826 #endif
5827             if (length > 4)
5828               magn_methx=p[4];
5829
5830             else
5831               magn_methx=0;
5832
5833             if (length > 6)
5834               magn_mx=(p[5] << 8) | p[6];
5835
5836             else
5837               magn_mx=1;
5838
5839             if (magn_mx == 0)
5840               magn_mx=1;
5841
5842             if (length > 8)
5843               magn_my=(p[7] << 8) | p[8];
5844
5845             else
5846               magn_my=magn_mx;
5847
5848             if (magn_my == 0)
5849               magn_my=1;
5850
5851             if (length > 10)
5852               magn_ml=(p[9] << 8) | p[10];
5853
5854             else
5855               magn_ml=magn_mx;
5856
5857             if (magn_ml == 0)
5858               magn_ml=1;
5859
5860             if (length > 12)
5861               magn_mr=(p[11] << 8) | p[12];
5862
5863             else
5864               magn_mr=magn_mx;
5865
5866             if (magn_mr == 0)
5867               magn_mr=1;
5868
5869             if (length > 14)
5870               magn_mt=(p[13] << 8) | p[14];
5871
5872             else
5873               magn_mt=magn_my;
5874
5875             if (magn_mt == 0)
5876               magn_mt=1;
5877
5878             if (length > 16)
5879               magn_mb=(p[15] << 8) | p[16];
5880
5881             else
5882               magn_mb=magn_my;
5883
5884             if (magn_mb == 0)
5885               magn_mb=1;
5886
5887             if (length > 17)
5888               magn_methy=p[17];
5889
5890             else
5891               magn_methy=magn_methx;
5892
5893
5894             if (magn_methx > 5 || magn_methy > 5)
5895               if (mng_info->magn_warning == 0)
5896                 {
5897                   (void) ThrowMagickException(exception,
5898                      GetMagickModule(),CoderError,
5899                      "Unknown MAGN method in MNG datastream","`%s'",
5900                      image->filename);
5901
5902                    mng_info->magn_warning++;
5903                 }
5904 #ifdef MNG_OBJECT_BUFFERS
5905           /* Magnify existing objects in the range magn_first to magn_last */
5906 #endif
5907             if (magn_first == 0 || magn_last == 0)
5908               {
5909                 /* Save the magnification factors for object 0 */
5910                 mng_info->magn_mb=magn_mb;
5911                 mng_info->magn_ml=magn_ml;
5912                 mng_info->magn_mr=magn_mr;
5913                 mng_info->magn_mt=magn_mt;
5914                 mng_info->magn_mx=magn_mx;
5915                 mng_info->magn_my=magn_my;
5916                 mng_info->magn_methx=magn_methx;
5917                 mng_info->magn_methy=magn_methy;
5918               }
5919           }
5920
5921         if (memcmp(type,mng_PAST,4) == 0)
5922           {
5923             if (mng_info->past_warning == 0)
5924               (void) ThrowMagickException(exception,GetMagickModule(),
5925                 CoderError,"PAST is not implemented yet","`%s'",
5926                 image->filename);
5927
5928             mng_info->past_warning++;
5929           }
5930
5931         if (memcmp(type,mng_SHOW,4) == 0)
5932           {
5933             if (mng_info->show_warning == 0)
5934               (void) ThrowMagickException(exception,GetMagickModule(),
5935                 CoderError,"SHOW is not implemented yet","`%s'",
5936                 image->filename);
5937
5938             mng_info->show_warning++;
5939           }
5940
5941         if (memcmp(type,mng_sBIT,4) == 0)
5942           {
5943             if (length < 4)
5944               mng_info->have_global_sbit=MagickFalse;
5945
5946             else
5947               {
5948                 mng_info->global_sbit.gray=p[0];
5949                 mng_info->global_sbit.red=p[0];
5950                 mng_info->global_sbit.green=p[1];
5951                 mng_info->global_sbit.blue=p[2];
5952                 mng_info->global_sbit.alpha=p[3];
5953                 mng_info->have_global_sbit=MagickTrue;
5954              }
5955           }
5956         if (memcmp(type,mng_pHYs,4) == 0)
5957           {
5958             if (length > 8)
5959               {
5960                 mng_info->global_x_pixels_per_unit=
5961                     (size_t) mng_get_long(p);
5962                 mng_info->global_y_pixels_per_unit=
5963                     (size_t) mng_get_long(&p[4]);
5964                 mng_info->global_phys_unit_type=p[8];
5965                 mng_info->have_global_phys=MagickTrue;
5966               }
5967
5968             else
5969               mng_info->have_global_phys=MagickFalse;
5970           }
5971         if (memcmp(type,mng_pHYg,4) == 0)
5972           {
5973             if (mng_info->phyg_warning == 0)
5974               (void) ThrowMagickException(exception,GetMagickModule(),
5975                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5976
5977             mng_info->phyg_warning++;
5978           }
5979         if (memcmp(type,mng_BASI,4) == 0)
5980           {
5981             skip_to_iend=MagickTrue;
5982
5983             if (mng_info->basi_warning == 0)
5984               (void) ThrowMagickException(exception,GetMagickModule(),
5985                 CoderError,"BASI is not implemented yet","`%s'",
5986                 image->filename);
5987
5988             mng_info->basi_warning++;
5989 #ifdef MNG_BASI_SUPPORTED
5990             basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5991                (p[2] << 8) | p[3]);
5992             basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5993                (p[6] << 8) | p[7]);
5994             basi_color_type=p[8];
5995             basi_compression_method=p[9];
5996             basi_filter_type=p[10];
5997             basi_interlace_method=p[11];
5998             if (length > 11)
5999               basi_red=(p[12] << 8) & p[13];
6000
6001             else
6002               basi_red=0;
6003
6004             if (length > 13)
6005               basi_green=(p[14] << 8) & p[15];
6006
6007             else
6008               basi_green=0;
6009
6010             if (length > 15)
6011               basi_blue=(p[16] << 8) & p[17];
6012
6013             else
6014               basi_blue=0;
6015
6016             if (length > 17)
6017               basi_alpha=(p[18] << 8) & p[19];
6018
6019             else
6020               {
6021                 if (basi_sample_depth == 16)
6022                   basi_alpha=65535L;
6023                 else
6024                   basi_alpha=255;
6025               }
6026
6027             if (length > 19)
6028               basi_viewable=p[20];
6029
6030             else
6031               basi_viewable=0;
6032
6033 #endif
6034             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6035             continue;
6036           }
6037
6038         if (memcmp(type,mng_IHDR,4)
6039 #if defined(JNG_SUPPORTED)
6040             && memcmp(type,mng_JHDR,4)
6041 #endif
6042             )
6043           {
6044             /* Not an IHDR or JHDR chunk */
6045             if (length)
6046               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6047
6048             continue;
6049           }
6050 /* Process IHDR */
6051         if (logging != MagickFalse)
6052           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6053             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6054
6055         mng_info->exists[object_id]=MagickTrue;
6056         mng_info->viewable[object_id]=MagickTrue;
6057
6058         if (mng_info->invisible[object_id])
6059           {
6060             if (logging != MagickFalse)
6061               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6062                 "  Skipping invisible object");
6063
6064             skip_to_iend=MagickTrue;
6065             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6066             continue;
6067           }
6068 #if defined(MNG_INSERT_LAYERS)
6069         if (length < 8)
6070           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6071
6072         image_width=(size_t) mng_get_long(p);
6073         image_height=(size_t) mng_get_long(&p[4]);
6074 #endif
6075         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6076
6077         /*
6078           Insert a transparent background layer behind the entire animation
6079           if it is not full screen.
6080         */
6081 #if defined(MNG_INSERT_LAYERS)
6082         if (insert_layers && mng_type && first_mng_object)
6083           {
6084             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6085                 (image_width < mng_info->mng_width) ||
6086                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6087                 (image_height < mng_info->mng_height) ||
6088                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6089               {
6090                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6091                   {
6092                     /*
6093                       Allocate next image structure.
6094                     */
6095                     AcquireNextImage(image_info,image,exception);
6096
6097                     if (GetNextImageInList(image) == (Image *) NULL)
6098                       {
6099                         image=DestroyImageList(image);
6100                         MngInfoFreeStruct(mng_info,&have_mng_structure);
6101                         return((Image *) NULL);
6102                       }
6103
6104                     image=SyncNextImageInList(image);
6105                   }
6106                 mng_info->image=image;
6107
6108                 if (term_chunk_found)
6109                   {
6110                     image->start_loop=MagickTrue;
6111                     image->iterations=mng_iterations;
6112                     term_chunk_found=MagickFalse;
6113                   }
6114
6115                 else
6116                     image->start_loop=MagickFalse;
6117
6118                 /* Make a background rectangle.  */
6119
6120                 image->delay=0;
6121                 image->columns=mng_info->mng_width;
6122                 image->rows=mng_info->mng_height;
6123                 image->page.width=mng_info->mng_width;
6124                 image->page.height=mng_info->mng_height;
6125                 image->page.x=0;
6126                 image->page.y=0;
6127                 image->background_color=mng_background_color;
6128                 (void) SetImageBackgroundColor(image,exception);
6129                 if (logging != MagickFalse)
6130                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6131                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6132                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6133               }
6134           }
6135         /*
6136           Insert a background layer behind the upcoming image if
6137           framing_mode is 3, and we haven't already inserted one.
6138         */
6139         if (insert_layers && (mng_info->framing_mode == 3) &&
6140                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6141                 (simplicity & 0x08)))
6142           {
6143             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6144             {
6145               /*
6146                 Allocate next image structure.
6147               */
6148               AcquireNextImage(image_info,image,exception);
6149
6150               if (GetNextImageInList(image) == (Image *) NULL)
6151                 {
6152                   image=DestroyImageList(image);
6153                   MngInfoFreeStruct(mng_info,&have_mng_structure);
6154                   return((Image *) NULL);
6155                 }
6156
6157               image=SyncNextImageInList(image);
6158             }
6159
6160             mng_info->image=image;
6161
6162             if (term_chunk_found)
6163               {
6164                 image->start_loop=MagickTrue;
6165                 image->iterations=mng_iterations;
6166                 term_chunk_found=MagickFalse;
6167               }
6168
6169             else
6170                 image->start_loop=MagickFalse;
6171
6172             image->delay=0;
6173             image->columns=subframe_width;
6174             image->rows=subframe_height;
6175             image->page.width=subframe_width;
6176             image->page.height=subframe_height;
6177             image->page.x=mng_info->clip.left;
6178             image->page.y=mng_info->clip.top;
6179             image->background_color=mng_background_color;
6180             image->alpha_trait=UndefinedPixelTrait;
6181             (void) SetImageBackgroundColor(image,exception);
6182
6183             if (logging != MagickFalse)
6184               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6185                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6186                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6187                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6188           }
6189 #endif /* MNG_INSERT_LAYERS */
6190         first_mng_object=MagickFalse;
6191
6192         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6193           {
6194             /*
6195               Allocate next image structure.
6196             */
6197             AcquireNextImage(image_info,image,exception);
6198
6199             if (GetNextImageInList(image) == (Image *) NULL)
6200               {
6201                 image=DestroyImageList(image);
6202                 MngInfoFreeStruct(mng_info,&have_mng_structure);
6203                 return((Image *) NULL);
6204               }
6205
6206             image=SyncNextImageInList(image);
6207           }
6208         mng_info->image=image;
6209         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6210           GetBlobSize(image));
6211
6212         if (status == MagickFalse)
6213           break;
6214
6215         if (term_chunk_found)
6216           {
6217             image->start_loop=MagickTrue;
6218             term_chunk_found=MagickFalse;
6219           }
6220
6221         else
6222             image->start_loop=MagickFalse;
6223
6224         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6225           {
6226             image->delay=frame_delay;
6227             frame_delay=default_frame_delay;
6228           }
6229
6230         else
6231           image->delay=0;
6232
6233         image->page.width=mng_info->mng_width;
6234         image->page.height=mng_info->mng_height;
6235         image->page.x=mng_info->x_off[object_id];
6236         image->page.y=mng_info->y_off[object_id];
6237         image->iterations=mng_iterations;
6238
6239         /*
6240           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6241         */
6242
6243         if (logging != MagickFalse)
6244           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6245             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6246             type[2],type[3]);
6247
6248         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6249
6250         if (offset < 0)
6251           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6252       }
6253
6254     previous=image;
6255     mng_info->image=image;
6256     mng_info->mng_type=mng_type;
6257     mng_info->object_id=object_id;
6258
6259     if (memcmp(type,mng_IHDR,4) == 0)
6260       image=ReadOnePNGImage(mng_info,image_info,exception);
6261
6262 #if defined(JNG_SUPPORTED)
6263     else
6264       image=ReadOneJNGImage(mng_info,image_info,exception);
6265 #endif
6266
6267     if (image == (Image *) NULL)
6268       {
6269         if (IsImageObject(previous) != MagickFalse)
6270           {
6271             (void) DestroyImageList(previous);
6272             (void) CloseBlob(previous);
6273           }
6274
6275         MngInfoFreeStruct(mng_info,&have_mng_structure);
6276         return((Image *) NULL);
6277       }
6278
6279     if (image->columns == 0 || image->rows == 0)
6280       {
6281         (void) CloseBlob(image);
6282         image=DestroyImageList(image);
6283         MngInfoFreeStruct(mng_info,&have_mng_structure);
6284         return((Image *) NULL);
6285       }
6286
6287     mng_info->image=image;
6288
6289     if (mng_type)
6290       {
6291         MngBox
6292           crop_box;
6293
6294         if (mng_info->magn_methx || mng_info->magn_methy)
6295           {
6296             png_uint_32
6297                magnified_height,
6298                magnified_width;
6299
6300             if (logging != MagickFalse)
6301               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6302                 "  Processing MNG MAGN chunk");
6303
6304             if (mng_info->magn_methx == 1)
6305               {
6306                 magnified_width=mng_info->magn_ml;
6307
6308                 if (image->columns > 1)
6309                    magnified_width += mng_info->magn_mr;
6310
6311                 if (image->columns > 2)
6312                    magnified_width += (png_uint_32)
6313                       ((image->columns-2)*(mng_info->magn_mx));
6314               }
6315
6316             else
6317               {
6318                 magnified_width=(png_uint_32) image->columns;
6319
6320                 if (image->columns > 1)
6321                    magnified_width += mng_info->magn_ml-1;
6322
6323                 if (image->columns > 2)
6324                    magnified_width += mng_info->magn_mr-1;
6325
6326                 if (image->columns > 3)
6327                    magnified_width += (png_uint_32)
6328                       ((image->columns-3)*(mng_info->magn_mx-1));
6329               }
6330
6331             if (mng_info->magn_methy == 1)
6332               {
6333                 magnified_height=mng_info->magn_mt;
6334
6335                 if (image->rows > 1)
6336                    magnified_height += mng_info->magn_mb;
6337
6338                 if (image->rows > 2)
6339                    magnified_height += (png_uint_32)
6340                       ((image->rows-2)*(mng_info->magn_my));
6341               }
6342
6343             else
6344               {
6345                 magnified_height=(png_uint_32) image->rows;
6346
6347                 if (image->rows > 1)
6348                    magnified_height += mng_info->magn_mt-1;
6349
6350                 if (image->rows > 2)
6351                    magnified_height += mng_info->magn_mb-1;
6352
6353                 if (image->rows > 3)
6354                    magnified_height += (png_uint_32)
6355                       ((image->rows-3)*(mng_info->magn_my-1));
6356               }
6357
6358             if (magnified_height > image->rows ||
6359                 magnified_width > image->columns)
6360               {
6361                 Image
6362                   *large_image;
6363
6364                 int
6365                   yy;
6366
6367                 Quantum
6368                   *next,
6369                   *prev;
6370
6371                 png_uint_16
6372                   magn_methx,
6373                   magn_methy;
6374
6375                 ssize_t
6376                   m,
6377                   y;
6378
6379                 register Quantum
6380                   *n,
6381                   *q;
6382
6383                 register ssize_t
6384                   x;
6385
6386                 /* Allocate next image structure.  */
6387
6388                 if (logging != MagickFalse)
6389                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6390                     "    Allocate magnified image");
6391
6392                 AcquireNextImage(image_info,image,exception);
6393
6394                 if (GetNextImageInList(image) == (Image *) NULL)
6395                   {
6396                     image=DestroyImageList(image);
6397                     MngInfoFreeStruct(mng_info,&have_mng_structure);
6398                     return((Image *) NULL);
6399                   }
6400
6401                 large_image=SyncNextImageInList(image);
6402
6403                 large_image->columns=magnified_width;
6404                 large_image->rows=magnified_height;
6405
6406                 magn_methx=mng_info->magn_methx;
6407                 magn_methy=mng_info->magn_methy;
6408
6409 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6410 #define QM unsigned short
6411                 if (magn_methx != 1 || magn_methy != 1)
6412                   {
6413                   /*
6414                      Scale pixels to unsigned shorts to prevent
6415                      overflow of intermediate values of interpolations
6416                   */
6417                      for (y=0; y < (ssize_t) image->rows; y++)
6418                      {
6419                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6420                           exception);
6421
6422                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6423                        {
6424                           SetPixelRed(image,ScaleQuantumToShort(
6425                             GetPixelRed(image,q)),q);
6426                           SetPixelGreen(image,ScaleQuantumToShort(
6427                             GetPixelGreen(image,q)),q);
6428                           SetPixelBlue(image,ScaleQuantumToShort(
6429                             GetPixelBlue(image,q)),q);
6430                           SetPixelAlpha(image,ScaleQuantumToShort(
6431                             GetPixelAlpha(image,q)),q);
6432                           q+=GetPixelChannels(image);
6433                        }
6434
6435                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6436                          break;
6437                      }
6438                   }
6439 #else
6440 #define QM Quantum
6441 #endif
6442
6443                 if (image->alpha_trait == BlendPixelTrait)
6444                    (void) SetImageBackgroundColor(large_image,exception);
6445
6446                 else
6447                   {
6448                     large_image->background_color.alpha=OpaqueAlpha;
6449                     (void) SetImageBackgroundColor(large_image,exception);
6450
6451                     if (magn_methx == 4)
6452                       magn_methx=2;
6453
6454                     if (magn_methx == 5)
6455                       magn_methx=3;
6456
6457                     if (magn_methy == 4)
6458                       magn_methy=2;
6459
6460                     if (magn_methy == 5)
6461                       magn_methy=3;
6462                   }
6463
6464                 /* magnify the rows into the right side of the large image */
6465
6466                 if (logging != MagickFalse)
6467                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6468                     "    Magnify the rows to %.20g",(double) large_image->rows);
6469                 m=(ssize_t) mng_info->magn_mt;
6470                 yy=0;
6471                 length=(size_t) image->columns*GetPixelChannels(image);
6472                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6473                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6474
6475                 if ((prev == (Quantum *) NULL) ||
6476                     (next == (Quantum *) NULL))
6477                   {
6478                      image=DestroyImageList(image);
6479                      MngInfoFreeStruct(mng_info,&have_mng_structure);
6480                      ThrowReaderException(ResourceLimitError,
6481                        "MemoryAllocationFailed");
6482                   }
6483
6484                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6485                 (void) CopyMagickMemory(next,n,length);
6486
6487                 for (y=0; y < (ssize_t) image->rows; y++)
6488                 {
6489                   if (y == 0)
6490                     m=(ssize_t) mng_info->magn_mt;
6491
6492                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6493                     m=(ssize_t) mng_info->magn_mb;
6494
6495                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6496                     m=(ssize_t) mng_info->magn_mb;
6497
6498                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6499                     m=1;
6500
6501                   else
6502                     m=(ssize_t) mng_info->magn_my;
6503
6504                   n=prev;
6505                   prev=next;
6506                   next=n;
6507
6508                   if (y < (ssize_t) image->rows-1)
6509                     {
6510                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6511                           exception);
6512                       (void) CopyMagickMemory(next,n,length);
6513                     }
6514
6515                   for (i=0; i < m; i++, yy++)
6516                   {
6517                     register Quantum
6518                       *pixels;
6519
6520                     assert(yy < (ssize_t) large_image->rows);
6521                     pixels=prev;
6522                     n=next;
6523                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6524                       1,exception);
6525                     q+=(large_image->columns-image->columns)*
6526                       GetPixelChannels(large_image);
6527
6528                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
6529                     {
6530                       /* To do: get color as function of indexes[x] */
6531                       /*
6532                       if (image->storage_class == PseudoClass)
6533                         {
6534                         }
6535                       */
6536
6537                       if (magn_methy <= 1)
6538                         {
6539                           /* replicate previous */
6540                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6541                           SetPixelGreen(large_image,GetPixelGreen(image,
6542                              pixels),q);
6543                           SetPixelBlue(large_image,GetPixelBlue(image,
6544                              pixels),q);
6545                           SetPixelAlpha(large_image,GetPixelAlpha(image,
6546                              pixels),q);
6547                         }
6548
6549                       else if (magn_methy == 2 || magn_methy == 4)
6550                         {
6551                           if (i == 0)
6552                             {
6553                               SetPixelRed(large_image,GetPixelRed(image,
6554                                  pixels),q);
6555                               SetPixelGreen(large_image,GetPixelGreen(image,
6556                                  pixels),q);
6557                               SetPixelBlue(large_image,GetPixelBlue(image,
6558                                  pixels),q);
6559                               SetPixelAlpha(large_image,GetPixelAlpha(image,
6560                                  pixels),q);
6561                             }
6562
6563                           else
6564                             {
6565                               /* Interpolate */
6566                               SetPixelRed(large_image,((QM) (((ssize_t)
6567                                  (2*i*(GetPixelRed(image,n)
6568                                  -GetPixelRed(image,pixels)+m))/
6569                                  ((ssize_t) (m*2))
6570                                  +GetPixelRed(image,pixels)))),q);
6571                               SetPixelGreen(large_image,((QM) (((ssize_t)
6572                                  (2*i*(GetPixelGreen(image,n)
6573                                  -GetPixelGreen(image,pixels)+m))/
6574                                  ((ssize_t) (m*2))
6575                                  +GetPixelGreen(image,pixels)))),q);
6576                               SetPixelBlue(large_image,((QM) (((ssize_t)
6577                                  (2*i*(GetPixelBlue(image,n)
6578                                  -GetPixelBlue(image,pixels)+m))/
6579                                  ((ssize_t) (m*2))
6580                                  +GetPixelBlue(image,pixels)))),q);
6581
6582                               if (image->alpha_trait == BlendPixelTrait)
6583                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
6584                                     (2*i*(GetPixelAlpha(image,n)
6585                                     -GetPixelAlpha(image,pixels)+m))
6586                                     /((ssize_t) (m*2))+
6587                                    GetPixelAlpha(image,pixels)))),q);
6588                             }
6589
6590                           if (magn_methy == 4)
6591                             {
6592                               /* Replicate nearest */
6593                               if (i <= ((m+1) << 1))
6594                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6595                                     pixels),q);
6596                               else
6597                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6598                                     n),q);
6599                             }
6600                         }
6601
6602                       else /* if (magn_methy == 3 || magn_methy == 5) */
6603                         {
6604                           /* Replicate nearest */
6605                           if (i <= ((m+1) << 1))
6606                           {
6607                              SetPixelRed(large_image,GetPixelRed(image,
6608                                     pixels),q);
6609                              SetPixelGreen(large_image,GetPixelGreen(image,
6610                                     pixels),q);
6611                              SetPixelBlue(large_image,GetPixelBlue(image,
6612                                     pixels),q);
6613                              SetPixelAlpha(large_image,GetPixelAlpha(image,
6614                                     pixels),q);
6615                           }
6616
6617                           else
6618                           {
6619                              SetPixelRed(large_image,GetPixelRed(image,n),q);
6620                              SetPixelGreen(large_image,GetPixelGreen(image,n),
6621                                     q);
6622                              SetPixelBlue(large_image,GetPixelBlue(image,n),
6623                                     q);
6624                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6625                                     q);
6626                           }
6627
6628                           if (magn_methy == 5)
6629                             {
6630                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6631                                  (GetPixelAlpha(image,n)
6632                                  -GetPixelAlpha(image,pixels))
6633                                  +m))/((ssize_t) (m*2))
6634                                  +GetPixelAlpha(image,pixels)),q);
6635                             }
6636                         }
6637                       n+=GetPixelChannels(image);
6638                       q+=GetPixelChannels(large_image);
6639                       pixels+=GetPixelChannels(image);
6640                     } /* x */
6641
6642                     if (SyncAuthenticPixels(large_image,exception) == 0)
6643                       break;
6644
6645                   } /* i */
6646                 } /* y */
6647
6648                 prev=(Quantum *) RelinquishMagickMemory(prev);
6649                 next=(Quantum *) RelinquishMagickMemory(next);
6650
6651                 length=image->columns;
6652
6653                 if (logging != MagickFalse)
6654                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6655                     "    Delete original image");
6656
6657                 DeleteImageFromList(&image);
6658
6659                 image=large_image;
6660
6661                 mng_info->image=image;
6662
6663                 /* magnify the columns */
6664                 if (logging != MagickFalse)
6665                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6666                     "    Magnify the columns to %.20g",(double) image->columns);
6667
6668                 for (y=0; y < (ssize_t) image->rows; y++)
6669                 {
6670                   register Quantum
6671                     *pixels;
6672
6673                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6674                   pixels=q+(image->columns-length)*GetPixelChannels(image);
6675                   n=pixels+GetPixelChannels(image);
6676
6677                   for (x=(ssize_t) (image->columns-length);
6678                     x < (ssize_t) image->columns; x++)
6679                   {
6680                     /* To do: Rewrite using Get/Set***PixelChannel() */
6681
6682                     if (x == (ssize_t) (image->columns-length))
6683                       m=(ssize_t) mng_info->magn_ml;
6684
6685                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6686                       m=(ssize_t) mng_info->magn_mr;
6687
6688                     else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6689                       m=(ssize_t) mng_info->magn_mr;
6690
6691                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6692                       m=1;
6693
6694                     else
6695                       m=(ssize_t) mng_info->magn_mx;
6696
6697                     for (i=0; i < m; i++)
6698                     {
6699                       if (magn_methx <= 1)
6700                         {
6701                           /* replicate previous */
6702                           SetPixelRed(image,GetPixelRed(image,pixels),q);
6703                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6704                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6705                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6706                         }
6707
6708                       else if (magn_methx == 2 || magn_methx == 4)
6709                         {
6710                           if (i == 0)
6711                           {
6712                             SetPixelRed(image,GetPixelRed(image,pixels),q);
6713                             SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6714                             SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6715                             SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6716                           }
6717
6718                           /* To do: Rewrite using Get/Set***PixelChannel() */
6719                           else
6720                             {
6721                               /* Interpolate */
6722                               SetPixelRed(image,(QM) ((2*i*(
6723                                  GetPixelRed(image,n)
6724                                  -GetPixelRed(image,pixels))+m)
6725                                  /((ssize_t) (m*2))+
6726                                  GetPixelRed(image,pixels)),q);
6727
6728                               SetPixelGreen(image,(QM) ((2*i*(
6729                                  GetPixelGreen(image,n)
6730                                  -GetPixelGreen(image,pixels))+m)
6731                                  /((ssize_t) (m*2))+
6732                                  GetPixelGreen(image,pixels)),q);
6733
6734                               SetPixelBlue(image,(QM) ((2*i*(
6735                                  GetPixelBlue(image,n)
6736                                  -GetPixelBlue(image,pixels))+m)
6737                                  /((ssize_t) (m*2))+
6738                                  GetPixelBlue(image,pixels)),q);
6739                               if (image->alpha_trait == BlendPixelTrait)
6740                                  SetPixelAlpha(image,(QM) ((2*i*(
6741                                    GetPixelAlpha(image,n)
6742                                    -GetPixelAlpha(image,pixels))+m)
6743                                    /((ssize_t) (m*2))+
6744                                    GetPixelAlpha(image,pixels)),q);
6745                             }
6746
6747                           if (magn_methx == 4)
6748                             {
6749                               /* Replicate nearest */
6750                               if (i <= ((m+1) << 1))
6751                               {
6752                                  SetPixelAlpha(image,
6753                                    GetPixelAlpha(image,pixels)+0,q);
6754                               }
6755                               else
6756                               {
6757                                  SetPixelAlpha(image,
6758                                    GetPixelAlpha(image,n)+0,q);
6759                               }
6760                             }
6761                         }
6762
6763                       else /* if (magn_methx == 3 || magn_methx == 5) */
6764                         {
6765                           /* Replicate nearest */
6766                           if (i <= ((m+1) << 1))
6767                           {
6768                              SetPixelRed(image,GetPixelRed(image,pixels),q);
6769                              SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6770                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6771                              SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6772                           }
6773
6774                           else
6775                           {
6776                              SetPixelRed(image,GetPixelRed(image,n),q);
6777                              SetPixelGreen(image,GetPixelGreen(image,n),q);
6778                              SetPixelBlue(image,GetPixelBlue(image,n),q);
6779                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6780                           }
6781
6782                           if (magn_methx == 5)
6783                             {
6784                               /* Interpolate */
6785                               SetPixelAlpha(image,
6786                                  (QM) ((2*i*( GetPixelAlpha(image,n)
6787                                  -GetPixelAlpha(image,pixels))+m)/
6788                                  ((ssize_t) (m*2))
6789                                  +GetPixelAlpha(image,pixels)),q);
6790                             }
6791                         }
6792                       q+=GetPixelChannels(image);
6793                     }
6794                     n+=GetPixelChannels(image);
6795                     p+=GetPixelChannels(image);
6796                   }
6797
6798                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
6799                     break;
6800                 }
6801 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6802               if (magn_methx != 1 || magn_methy != 1)
6803                 {
6804                 /*
6805                    Rescale pixels to Quantum
6806                 */
6807                    for (y=0; y < (ssize_t) image->rows; y++)
6808                    {
6809                      q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6810
6811                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
6812                      {
6813                         SetPixelRed(image,ScaleShortToQuantum(
6814                           GetPixelRed(image,q)),q);
6815                         SetPixelGreen(image,ScaleShortToQuantum(
6816                           GetPixelGreen(image,q)),q);
6817                         SetPixelBlue(image,ScaleShortToQuantum(
6818                           GetPixelBlue(image,q)),q);
6819                         SetPixelAlpha(image,ScaleShortToQuantum(
6820                           GetPixelAlpha(image,q)),q);
6821                         q+=GetPixelChannels(image);
6822                      }
6823
6824                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
6825                        break;
6826                    }
6827                 }
6828 #endif
6829                 if (logging != MagickFalse)
6830                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6831                     "  Finished MAGN processing");
6832               }
6833           }
6834
6835         /*
6836           Crop_box is with respect to the upper left corner of the MNG.
6837         */
6838         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6839         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6840         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6841         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6842         crop_box=mng_minimum_box(crop_box,mng_info->clip);
6843         crop_box=mng_minimum_box(crop_box,mng_info->frame);
6844         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6845         if ((crop_box.left != (mng_info->image_box.left
6846             +mng_info->x_off[object_id])) ||
6847             (crop_box.right != (mng_info->image_box.right
6848             +mng_info->x_off[object_id])) ||
6849             (crop_box.top != (mng_info->image_box.top
6850             +mng_info->y_off[object_id])) ||
6851             (crop_box.bottom != (mng_info->image_box.bottom
6852             +mng_info->y_off[object_id])))
6853           {
6854             if (logging != MagickFalse)
6855               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6856                 "  Crop the PNG image");
6857
6858             if ((crop_box.left < crop_box.right) &&
6859                 (crop_box.top < crop_box.bottom))
6860               {
6861                 Image
6862                   *im;
6863
6864                 RectangleInfo
6865                   crop_info;
6866
6867                 /*
6868                   Crop_info is with respect to the upper left corner of
6869                   the image.
6870                 */
6871                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6872                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6873                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6874                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6875                 image->page.width=image->columns;
6876                 image->page.height=image->rows;
6877                 image->page.x=0;
6878                 image->page.y=0;
6879                 im=CropImage(image,&crop_info,exception);
6880
6881                 if (im != (Image *) NULL)
6882                   {
6883                     image->columns=im->columns;
6884                     image->rows=im->rows;
6885                     im=DestroyImage(im);
6886                     image->page.width=image->columns;
6887                     image->page.height=image->rows;
6888                     image->page.x=crop_box.left;
6889                     image->page.y=crop_box.top;
6890                   }
6891               }
6892
6893             else
6894               {
6895                 /*
6896                   No pixels in crop area.  The MNG spec still requires
6897                   a layer, though, so make a single transparent pixel in
6898                   the top left corner.
6899                 */
6900                 image->columns=1;
6901                 image->rows=1;
6902                 image->colors=2;
6903                 (void) SetImageBackgroundColor(image,exception);
6904                 image->page.width=1;
6905                 image->page.height=1;
6906                 image->page.x=0;
6907                 image->page.y=0;
6908               }
6909           }
6910 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6911         image=mng_info->image;
6912 #endif
6913       }
6914
6915 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6916       /* PNG does not handle depths greater than 16 so reduce it even
6917        * if lossy.
6918        */
6919       if (image->depth > 16)
6920          image->depth=16;
6921 #endif
6922
6923 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6924       if (image->depth > 8)
6925         {
6926           /* To do: fill low byte properly */
6927           image->depth=16;
6928         }
6929
6930       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
6931          image->depth = 8;
6932 #endif
6933
6934       if (image_info->number_scenes != 0)
6935         {
6936           if (mng_info->scenes_found >
6937              (ssize_t) (image_info->first_scene+image_info->number_scenes))
6938             break;
6939         }
6940
6941       if (logging != MagickFalse)
6942         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6943           "  Finished reading image datastream.");
6944
6945   } while (LocaleCompare(image_info->magick,"MNG") == 0);
6946
6947   (void) CloseBlob(image);
6948
6949   if (logging != MagickFalse)
6950     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6951       "  Finished reading all image datastreams.");
6952
6953 #if defined(MNG_INSERT_LAYERS)
6954   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6955        (mng_info->mng_height))
6956     {
6957       /*
6958         Insert a background layer if nothing else was found.
6959       */
6960       if (logging != MagickFalse)
6961         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6962           "  No images found.  Inserting a background layer.");
6963
6964       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6965         {
6966           /*
6967             Allocate next image structure.
6968           */
6969           AcquireNextImage(image_info,image,exception);
6970           if (GetNextImageInList(image) == (Image *) NULL)
6971             {
6972               image=DestroyImageList(image);
6973               MngInfoFreeStruct(mng_info,&have_mng_structure);
6974
6975               if (logging != MagickFalse)
6976                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6977                   "  Allocation failed, returning NULL.");
6978
6979               return((Image *) NULL);
6980             }
6981           image=SyncNextImageInList(image);
6982         }
6983       image->columns=mng_info->mng_width;
6984       image->rows=mng_info->mng_height;
6985       image->page.width=mng_info->mng_width;
6986       image->page.height=mng_info->mng_height;
6987       image->page.x=0;
6988       image->page.y=0;
6989       image->background_color=mng_background_color;
6990       image->alpha_trait=UndefinedPixelTrait;
6991
6992       if (image_info->ping == MagickFalse)
6993         (void) SetImageBackgroundColor(image,exception);
6994
6995       mng_info->image_found++;
6996     }
6997 #endif
6998   image->iterations=mng_iterations;
6999
7000   if (mng_iterations == 1)
7001     image->start_loop=MagickTrue;
7002
7003   while (GetPreviousImageInList(image) != (Image *) NULL)
7004   {
7005     image_count++;
7006     if (image_count > 10*mng_info->image_found)
7007       {
7008         if (logging != MagickFalse)
7009           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7010
7011         (void) ThrowMagickException(exception,GetMagickModule(),
7012           CoderError,"Linked list is corrupted, beginning of list not found",
7013           "`%s'",image_info->filename);
7014
7015         return((Image *) NULL);
7016       }
7017
7018     image=GetPreviousImageInList(image);
7019
7020     if (GetNextImageInList(image) == (Image *) NULL)
7021       {
7022         if (logging != MagickFalse)
7023           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7024
7025         (void) ThrowMagickException(exception,GetMagickModule(),
7026           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7027           image_info->filename);
7028       }
7029   }
7030
7031   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7032              GetNextImageInList(image) ==
7033      (Image *) NULL)
7034     {
7035       if (logging != MagickFalse)
7036         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7037             "  First image null");
7038
7039       (void) ThrowMagickException(exception,GetMagickModule(),
7040         CoderError,"image->next for first image is NULL but shouldn't be.",
7041         "`%s'",image_info->filename);
7042     }
7043
7044   if (mng_info->image_found == 0)
7045     {
7046       if (logging != MagickFalse)
7047         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7048           "  No visible images found.");
7049
7050       (void) ThrowMagickException(exception,GetMagickModule(),
7051         CoderError,"No visible images in file","`%s'",image_info->filename);
7052
7053       if (image != (Image *) NULL)
7054         image=DestroyImageList(image);
7055
7056       MngInfoFreeStruct(mng_info,&have_mng_structure);
7057       return((Image *) NULL);
7058     }
7059
7060   if (mng_info->ticks_per_second)
7061     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7062             final_delay/mng_info->ticks_per_second;
7063
7064   else
7065     image->start_loop=MagickTrue;
7066
7067   /* Find final nonzero image delay */
7068   final_image_delay=0;
7069
7070   while (GetNextImageInList(image) != (Image *) NULL)
7071     {
7072       if (image->delay)
7073         final_image_delay=image->delay;
7074
7075       image=GetNextImageInList(image);
7076     }
7077
7078   if (final_delay < final_image_delay)
7079     final_delay=final_image_delay;
7080
7081   image->delay=final_delay;
7082
7083   if (logging != MagickFalse)
7084       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7085         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7086         (double) final_delay);
7087
7088   if (logging != MagickFalse)
7089     {
7090       int
7091         scene;
7092
7093       scene=0;
7094       image=GetFirstImageInList(image);
7095
7096       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7097         "  Before coalesce:");
7098
7099       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7100         "    scene 0 delay=%.20g",(double) image->delay);
7101
7102       while (GetNextImageInList(image) != (Image *) NULL)
7103       {
7104         image=GetNextImageInList(image);
7105         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7106           "    scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7107       }
7108     }
7109
7110   image=GetFirstImageInList(image);
7111 #ifdef MNG_COALESCE_LAYERS
7112   if (insert_layers)
7113     {
7114       Image
7115         *next_image,
7116         *next;
7117
7118       size_t
7119         scene;
7120
7121       if (logging != MagickFalse)
7122         (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Coalesce Images");
7123
7124       scene=image->scene;
7125       next_image=CoalesceImages(image,exception);
7126
7127       if (next_image == (Image *) NULL)
7128         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7129
7130       image=DestroyImageList(image);
7131       image=next_image;
7132
7133       for (next=image; next != (Image *) NULL; next=next_image)
7134       {
7135          next->page.width=mng_info->mng_width;
7136          next->page.height=mng_info->mng_height;
7137          next->page.x=0;
7138          next->page.y=0;
7139          next->scene=scene++;
7140          next_image=GetNextImageInList(next);
7141
7142          if (next_image == (Image *) NULL)
7143            break;
7144
7145          if (next->delay == 0)
7146            {
7147              scene--;
7148              next_image->previous=GetPreviousImageInList(next);
7149              if (GetPreviousImageInList(next) == (Image *) NULL)
7150                image=next_image;
7151              else
7152                next->previous->next=next_image;
7153              next=DestroyImage(next);
7154            }
7155       }
7156     }
7157 #endif
7158
7159   while (GetNextImageInList(image) != (Image *) NULL)
7160       image=GetNextImageInList(image);
7161
7162   image->dispose=BackgroundDispose;
7163
7164   if (logging != MagickFalse)
7165     {
7166       int
7167         scene;
7168
7169       scene=0;
7170       image=GetFirstImageInList(image);
7171
7172       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7173         "  After coalesce:");
7174
7175       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7176         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7177         (double) image->dispose);
7178
7179       while (GetNextImageInList(image) != (Image *) NULL)
7180       {
7181         image=GetNextImageInList(image);
7182
7183         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7184           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7185           (double) image->delay,(double) image->dispose);
7186       }
7187    }
7188
7189   image=GetFirstImageInList(image);
7190   MngInfoFreeStruct(mng_info,&have_mng_structure);
7191   have_mng_structure=MagickFalse;
7192
7193   if (logging != MagickFalse)
7194     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7195
7196   return(GetFirstImageInList(image));
7197 }
7198 #else /* PNG_LIBPNG_VER > 10011 */
7199 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7200 {
7201   printf("Your PNG library is too old: You have libpng-%s\n",
7202      PNG_LIBPNG_VER_STRING);
7203
7204   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7205     "PNG library is too old","`%s'",image_info->filename);
7206
7207   return(Image *) NULL;
7208 }
7209
7210 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7211 {
7212   return(ReadPNGImage(image_info,exception));
7213 }
7214 #endif /* PNG_LIBPNG_VER > 10011 */
7215 #endif
7216 \f
7217 /*
7218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7219 %                                                                             %
7220 %                                                                             %
7221 %                                                                             %
7222 %   R e g i s t e r P N G I m a g e                                           %
7223 %                                                                             %
7224 %                                                                             %
7225 %                                                                             %
7226 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7227 %
7228 %  RegisterPNGImage() adds properties for the PNG image format to
7229 %  the list of supported formats.  The properties include the image format
7230 %  tag, a method to read and/or write the format, whether the format
7231 %  supports the saving of more than one frame to the same file or blob,
7232 %  whether the format supports native in-memory I/O, and a brief
7233 %  description of the format.
7234 %
7235 %  The format of the RegisterPNGImage method is:
7236 %
7237 %      size_t RegisterPNGImage(void)
7238 %
7239 */
7240 ModuleExport size_t RegisterPNGImage(void)
7241 {
7242   char
7243     version[MaxTextExtent];
7244
7245   MagickInfo
7246     *entry;
7247
7248   static const char
7249     *PNGNote=
7250     {
7251       "See http://www.libpng.org/ for details about the PNG format."
7252     },
7253
7254     *JNGNote=
7255     {
7256       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7257       "format."
7258     },
7259
7260     *MNGNote=
7261     {
7262       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7263       "format."
7264     };
7265
7266   *version='\0';
7267
7268 #if defined(PNG_LIBPNG_VER_STRING)
7269   (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7270   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7271
7272   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7273     {
7274       (void) ConcatenateMagickString(version,",",MaxTextExtent);
7275       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7276             MaxTextExtent);
7277     }
7278 #endif
7279
7280   entry=SetMagickInfo("MNG");
7281   entry->seekable_stream=MagickTrue;  /* To do: eliminate this. */
7282
7283 #if defined(MAGICKCORE_PNG_DELEGATE)
7284   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7285   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7286 #endif
7287
7288   entry->magick=(IsImageFormatHandler *) IsMNG;
7289   entry->description=ConstantString("Multiple-image Network Graphics");
7290
7291   if (*version != '\0')
7292     entry->version=ConstantString(version);
7293
7294   entry->module=ConstantString("PNG");
7295   entry->note=ConstantString(MNGNote);
7296   (void) RegisterMagickInfo(entry);
7297
7298   entry=SetMagickInfo("PNG");
7299
7300 #if defined(MAGICKCORE_PNG_DELEGATE)
7301   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7302   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7303 #endif
7304
7305   entry->magick=(IsImageFormatHandler *) IsPNG;
7306   entry->adjoin=MagickFalse;
7307   entry->description=ConstantString("Portable Network Graphics");
7308   entry->module=ConstantString("PNG");
7309
7310   if (*version != '\0')
7311     entry->version=ConstantString(version);
7312
7313   entry->note=ConstantString(PNGNote);
7314   (void) RegisterMagickInfo(entry);
7315
7316   entry=SetMagickInfo("PNG8");
7317
7318 #if defined(MAGICKCORE_PNG_DELEGATE)
7319   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7320   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7321 #endif
7322
7323   entry->magick=(IsImageFormatHandler *) IsPNG;
7324   entry->adjoin=MagickFalse;
7325   entry->description=ConstantString(
7326             "8-bit indexed with optional binary transparency");
7327   entry->module=ConstantString("PNG");
7328   (void) RegisterMagickInfo(entry);
7329
7330   entry=SetMagickInfo("PNG24");
7331   *version='\0';
7332
7333 #if defined(ZLIB_VERSION)
7334   (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7335   (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7336
7337   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7338     {
7339       (void) ConcatenateMagickString(version,",",MaxTextExtent);
7340       (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7341     }
7342 #endif
7343
7344   if (*version != '\0')
7345     entry->version=ConstantString(version);
7346
7347 #if defined(MAGICKCORE_PNG_DELEGATE)
7348   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7349   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7350 #endif
7351
7352   entry->magick=(IsImageFormatHandler *) IsPNG;
7353   entry->adjoin=MagickFalse;
7354   entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
7355   entry->module=ConstantString("PNG");
7356   (void) RegisterMagickInfo(entry);
7357
7358   entry=SetMagickInfo("PNG32");
7359
7360 #if defined(MAGICKCORE_PNG_DELEGATE)
7361   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7362   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7363 #endif
7364
7365   entry->magick=(IsImageFormatHandler *) IsPNG;
7366   entry->adjoin=MagickFalse;
7367   entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7368   entry->module=ConstantString("PNG");
7369   (void) RegisterMagickInfo(entry);
7370
7371   entry=SetMagickInfo("PNG48");
7372
7373 #if defined(MAGICKCORE_PNG_DELEGATE)
7374   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7375   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7376 #endif
7377
7378   entry->magick=(IsImageFormatHandler *) IsPNG;
7379   entry->adjoin=MagickFalse;
7380   entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7381   entry->module=ConstantString("PNG");
7382   (void) RegisterMagickInfo(entry);
7383
7384   entry=SetMagickInfo("PNG64");
7385
7386 #if defined(MAGICKCORE_PNG_DELEGATE)
7387   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7388   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7389 #endif
7390
7391   entry->magick=(IsImageFormatHandler *) IsPNG;
7392   entry->adjoin=MagickFalse;
7393   entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7394   entry->module=ConstantString("PNG");
7395   (void) RegisterMagickInfo(entry);
7396
7397   entry=SetMagickInfo("PNG00");
7398
7399 #if defined(MAGICKCORE_PNG_DELEGATE)
7400   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7401   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7402 #endif
7403
7404   entry->magick=(IsImageFormatHandler *) IsPNG;
7405   entry->adjoin=MagickFalse;
7406   entry->description=ConstantString(
7407     "PNG inheriting bit-depth and color-type from original");
7408   entry->module=ConstantString("PNG");
7409   (void) RegisterMagickInfo(entry);
7410
7411   entry=SetMagickInfo("JNG");
7412
7413 #if defined(JNG_SUPPORTED)
7414 #if defined(MAGICKCORE_PNG_DELEGATE)
7415   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7416   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7417 #endif
7418 #endif
7419
7420   entry->magick=(IsImageFormatHandler *) IsJNG;
7421   entry->adjoin=MagickFalse;
7422   entry->description=ConstantString("JPEG Network Graphics");
7423   entry->module=ConstantString("PNG");
7424   entry->note=ConstantString(JNGNote);
7425   (void) RegisterMagickInfo(entry);
7426
7427 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7428   ping_semaphore=AllocateSemaphoreInfo();
7429 #endif
7430
7431   return(MagickImageCoderSignature);
7432 }
7433 \f
7434 /*
7435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7436 %                                                                             %
7437 %                                                                             %
7438 %                                                                             %
7439 %   U n r e g i s t e r P N G I m a g e                                       %
7440 %                                                                             %
7441 %                                                                             %
7442 %                                                                             %
7443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7444 %
7445 %  UnregisterPNGImage() removes format registrations made by the
7446 %  PNG module from the list of supported formats.
7447 %
7448 %  The format of the UnregisterPNGImage method is:
7449 %
7450 %      UnregisterPNGImage(void)
7451 %
7452 */
7453 ModuleExport void UnregisterPNGImage(void)
7454 {
7455   (void) UnregisterMagickInfo("MNG");
7456   (void) UnregisterMagickInfo("PNG");
7457   (void) UnregisterMagickInfo("PNG8");
7458   (void) UnregisterMagickInfo("PNG24");
7459   (void) UnregisterMagickInfo("PNG32");
7460   (void) UnregisterMagickInfo("PNG48");
7461   (void) UnregisterMagickInfo("PNG64");
7462   (void) UnregisterMagickInfo("PNG00");
7463   (void) UnregisterMagickInfo("JNG");
7464
7465 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7466   if (ping_semaphore != (SemaphoreInfo *) NULL)
7467     DestroySemaphoreInfo(&ping_semaphore);
7468 #endif
7469 }
7470 \f
7471 #if defined(MAGICKCORE_PNG_DELEGATE)
7472 #if PNG_LIBPNG_VER > 10011
7473 /*
7474 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7475 %                                                                             %
7476 %                                                                             %
7477 %                                                                             %
7478 %   W r i t e M N G I m a g e                                                 %
7479 %                                                                             %
7480 %                                                                             %
7481 %                                                                             %
7482 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7483 %
7484 %  WriteMNGImage() writes an image in the Portable Network Graphics
7485 %  Group's "Multiple-image Network Graphics" encoded image format.
7486 %
7487 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
7488 %
7489 %  The format of the WriteMNGImage method is:
7490 %
7491 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7492 %        Image *image,ExceptionInfo *exception)
7493 %
7494 %  A description of each parameter follows.
7495 %
7496 %    o image_info: the image info.
7497 %
7498 %    o image:  The image.
7499 %
7500 %    o exception: return any errors or warnings in this structure.
7501 %
7502 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7503 %    "To do" under ReadPNGImage):
7504 %
7505 %    Preserve all unknown and not-yet-handled known chunks found in input
7506 %    PNG file and copy them  into output PNG files according to the PNG
7507 %    copying rules.
7508 %
7509 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
7510 %
7511 %    Improve selection of color type (use indexed-colour or indexed-colour
7512 %    with tRNS when 256 or fewer unique RGBA values are present).
7513 %
7514 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7515 %    This will be complicated if we limit ourselves to generating MNG-LC
7516 %    files.  For now we ignore disposal method 3 and simply overlay the next
7517 %    image on it.
7518 %
7519 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
7520 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
7521 %    [mostly done 15 June 1999 but still need to take care of tRNS]
7522 %
7523 %    Check for identical sRGB and replace with a global sRGB (and remove
7524 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7525 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7526 %    local gAMA/cHRM with local sRGB if appropriate).
7527 %
7528 %    Check for identical sBIT chunks and write global ones.
7529 %
7530 %    Provide option to skip writing the signature tEXt chunks.
7531 %
7532 %    Use signatures to detect identical objects and reuse the first
7533 %    instance of such objects instead of writing duplicate objects.
7534 %
7535 %    Use a smaller-than-32k value of compression window size when
7536 %    appropriate.
7537 %
7538 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
7539 %    ancillary text chunks and save profiles.
7540 %
7541 %    Provide an option to force LC files (to ensure exact framing rate)
7542 %    instead of VLC.
7543 %
7544 %    Provide an option to force VLC files instead of LC, even when offsets
7545 %    are present.  This will involve expanding the embedded images with a
7546 %    transparent region at the top and/or left.
7547 */
7548
7549 static void
7550 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7551    png_info *ping_info, unsigned char *profile_type, unsigned char
7552    *profile_description, unsigned char *profile_data, png_uint_32 length)
7553 {
7554    png_textp
7555      text;
7556
7557    register ssize_t
7558      i;
7559
7560    unsigned char
7561      *sp;
7562
7563    png_charp
7564      dp;
7565
7566    png_uint_32
7567      allocated_length,
7568      description_length;
7569
7570    unsigned char
7571      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7572
7573    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7574       return;
7575
7576    if (image_info->verbose)
7577      {
7578        (void) printf("writing raw profile: type=%s, length=%.20g\n",
7579          (char *) profile_type, (double) length);
7580      }
7581
7582 #if PNG_LIBPNG_VER >= 14000
7583    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7584 #else
7585    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7586 #endif
7587    description_length=(png_uint_32) strlen((const char *) profile_description);
7588    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7589       + description_length);
7590 #if PNG_LIBPNG_VER >= 14000
7591    text[0].text=(png_charp) png_malloc(ping,
7592       (png_alloc_size_t) allocated_length);
7593    text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7594 #else
7595    text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7596    text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7597 #endif
7598    text[0].key[0]='\0';
7599    (void) ConcatenateMagickString(text[0].key,
7600       "Raw profile type ",MaxTextExtent);
7601    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7602    sp=profile_data;
7603    dp=text[0].text;
7604    *dp++='\n';
7605    (void) CopyMagickString(dp,(const char *) profile_description,
7606      allocated_length);
7607    dp+=description_length;
7608    *dp++='\n';
7609    (void) FormatLocaleString(dp,allocated_length-
7610      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7611    dp+=8;
7612
7613    for (i=0; i < (ssize_t) length; i++)
7614    {
7615      if (i%36 == 0)
7616        *dp++='\n';
7617      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7618      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7619    }
7620
7621    *dp++='\n';
7622    *dp='\0';
7623    text[0].text_length=(png_size_t) (dp-text[0].text);
7624    text[0].compression=image_info->compression == NoCompression ||
7625      (image_info->compression == UndefinedCompression &&
7626      text[0].text_length < 128) ? -1 : 0;
7627
7628    if (text[0].text_length <= allocated_length)
7629      png_set_text(ping,ping_info,text,1);
7630
7631    png_free(ping,text[0].text);
7632    png_free(ping,text[0].key);
7633    png_free(ping,text);
7634 }
7635
7636 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7637   const char *string, MagickBooleanType logging)
7638 {
7639   char
7640     *name;
7641
7642   const StringInfo
7643     *profile;
7644
7645   unsigned char
7646     *data;
7647
7648   png_uint_32 length;
7649
7650   ResetImageProfileIterator(image);
7651
7652   for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7653   {
7654     profile=GetImageProfile(image,name);
7655
7656     if (profile != (const StringInfo *) NULL)
7657       {
7658         StringInfo
7659           *ping_profile;
7660
7661         if (LocaleNCompare(name,string,11) == 0)
7662           {
7663             if (logging != MagickFalse)
7664                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7665                    "  Found %s profile",name);
7666
7667             ping_profile=CloneStringInfo(profile);
7668             data=GetStringInfoDatum(ping_profile),
7669             length=(png_uint_32) GetStringInfoLength(ping_profile);
7670             data[4]=data[3];
7671             data[3]=data[2];
7672             data[2]=data[1];
7673             data[1]=data[0];
7674             (void) WriteBlobMSBULong(image,length-5);  /* data length */
7675             (void) WriteBlob(image,length-1,data+1);
7676             (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7677             ping_profile=DestroyStringInfo(ping_profile);
7678           }
7679       }
7680
7681       name=GetNextImageProfile(image);
7682    }
7683
7684    return(MagickTrue);
7685 }
7686
7687
7688 /* Write one PNG image */
7689 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7690   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7691 {
7692   Image
7693     *image;
7694
7695   ImageInfo
7696     *image_info;
7697
7698   char
7699     s[2];
7700
7701   const char
7702     *name,
7703     *property,
7704     *value;
7705
7706   const StringInfo
7707     *profile;
7708
7709   int
7710     num_passes,
7711     pass;
7712
7713   png_byte
7714      ping_trans_alpha[256];
7715
7716   png_color
7717      palette[257];
7718
7719   png_color_16
7720     ping_background,
7721     ping_trans_color;
7722
7723   png_info
7724     *ping_info;
7725
7726   png_struct
7727     *ping;
7728
7729   png_uint_32
7730     ping_height,
7731     ping_width;
7732
7733   ssize_t
7734     y;
7735
7736   MagickBooleanType
7737     image_matte,
7738     logging,
7739     matte,
7740
7741     ping_have_blob,
7742     ping_have_cheap_transparency,
7743     ping_have_color,
7744     ping_have_non_bw,
7745     ping_have_PLTE,
7746     ping_have_bKGD,
7747     ping_have_pHYs,
7748     ping_have_tRNS,
7749
7750     ping_exclude_bKGD,
7751     ping_exclude_cHRM,
7752     ping_exclude_date,
7753     /* ping_exclude_EXIF, */
7754     ping_exclude_gAMA,
7755     ping_exclude_iCCP,
7756     /* ping_exclude_iTXt, */
7757     ping_exclude_oFFs,
7758     ping_exclude_pHYs,
7759     ping_exclude_sRGB,
7760     ping_exclude_tEXt,
7761     /* ping_exclude_tRNS, */
7762     ping_exclude_vpAg,
7763     ping_exclude_zCCP, /* hex-encoded iCCP */
7764     ping_exclude_zTXt,
7765
7766     ping_preserve_colormap,
7767     ping_need_colortype_warning,
7768
7769     status,
7770     tried_332,
7771     tried_333,
7772     tried_444;
7773
7774   QuantumInfo
7775     *quantum_info;
7776
7777   PNGErrorInfo
7778     error_info;
7779
7780   register ssize_t
7781     i,
7782     x;
7783
7784   unsigned char
7785     *volatile ping_pixels;
7786
7787   volatile int
7788     image_colors,
7789     ping_bit_depth,
7790     ping_color_type,
7791     ping_interlace_method,
7792     ping_compression_method,
7793     ping_filter_method,
7794     ping_num_trans;
7795
7796   volatile size_t
7797     image_depth,
7798     old_bit_depth;
7799
7800   size_t
7801     quality,
7802     rowbytes,
7803     save_image_depth;
7804
7805   int
7806     j,
7807     number_colors,
7808     number_opaque,
7809     number_semitransparent,
7810     number_transparent,
7811     ping_pHYs_unit_type;
7812
7813   png_uint_32
7814     ping_pHYs_x_resolution,
7815     ping_pHYs_y_resolution;
7816
7817   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7818     "  Enter WriteOnePNGImage()");
7819
7820   image = CloneImage(IMimage,0,0,MagickFalse,exception);
7821   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7822   if (image_info == (ImageInfo *) NULL)
7823      ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7824
7825   /* Initialize some stuff */
7826   ping_bit_depth=0,
7827   ping_color_type=0,
7828   ping_interlace_method=0,
7829   ping_compression_method=0,
7830   ping_filter_method=0,
7831   ping_num_trans = 0;
7832
7833   ping_background.red = 0;
7834   ping_background.green = 0;
7835   ping_background.blue = 0;
7836   ping_background.gray = 0;
7837   ping_background.index = 0;
7838
7839   ping_trans_color.red=0;
7840   ping_trans_color.green=0;
7841   ping_trans_color.blue=0;
7842   ping_trans_color.gray=0;
7843
7844   ping_pHYs_unit_type = 0;
7845   ping_pHYs_x_resolution = 0;
7846   ping_pHYs_y_resolution = 0;
7847
7848   ping_have_blob=MagickFalse;
7849   ping_have_color=MagickTrue;
7850   ping_have_non_bw=MagickTrue;
7851   ping_have_PLTE=MagickFalse;
7852   ping_have_bKGD=MagickFalse;
7853   ping_have_pHYs=MagickFalse;
7854   ping_have_tRNS=MagickFalse;
7855
7856   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7857   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7858   ping_exclude_date=mng_info->ping_exclude_date;
7859   /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7860   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7861   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7862   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7863   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7864   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7865   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7866   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7867   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7868   ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7869   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7870   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7871
7872   ping_preserve_colormap = mng_info->ping_preserve_colormap;
7873   ping_need_colortype_warning = MagickFalse;
7874
7875   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7876    * i.e., eliminate the ICC profile and set image->rendering_intent.
7877    * Note that this will not involve any changes to the actual pixels
7878    * but merely passes information to applications that read the resulting
7879    * PNG image.
7880    */
7881    if (ping_exclude_sRGB == MagickFalse)
7882    {
7883       char
7884         *name;
7885
7886       const StringInfo
7887         *profile;
7888
7889       ResetImageProfileIterator(image);
7890       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7891       {
7892         profile=GetImageProfile(image,name);
7893
7894         if (profile != (StringInfo *) NULL)
7895           {
7896             if ((LocaleCompare(name,"ICC") == 0) ||
7897                (LocaleCompare(name,"ICM") == 0))
7898               {
7899                  int
7900                    icheck;
7901
7902                  /* 0: not a known sRGB profile
7903                   * 1: HP-Microsoft sRGB v2
7904                   * 2: ICC sRGB v4 perceptual
7905                   * 3: ICC sRGB v2 perceptual no black-compensation
7906                   */
7907                  png_uint_32
7908                    check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7909                    check_len[4] = {0, 3144, 60960, 3052};
7910
7911                  png_uint_32
7912                    length,
7913                    profile_crc;
7914
7915                  unsigned char
7916                    *data;
7917
7918                  length=(png_uint_32) GetStringInfoLength(profile);
7919
7920                  for (icheck=3; icheck > 0; icheck--)
7921                  {
7922                    if (length == check_len[icheck])
7923                    {
7924                      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7925                          "    Got a %lu-byte ICC profile (potentially sRGB)",
7926                          (unsigned long) length);
7927
7928                      data=GetStringInfoDatum(profile);
7929                      profile_crc=crc32(0,data,length);
7930
7931                      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7932                          "      with crc=%8x",(unsigned int) profile_crc);
7933
7934                      if (profile_crc == check_crc[icheck])
7935                      {
7936                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7937                             "      It is sRGB.");
7938                         if (image->rendering_intent==UndefinedIntent)
7939                           image->rendering_intent=PerceptualIntent;
7940                         break;
7941                      }
7942                    }
7943                  }
7944                  if (icheck == 0)
7945                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7946                         "    Got a %lu-byte ICC profile",
7947                         (unsigned long) length);
7948               }
7949           }
7950         name=GetNextImageProfile(image);
7951       }
7952   }
7953
7954   number_opaque = 0;
7955   number_semitransparent = 0;
7956   number_transparent = 0;
7957
7958   if (logging != MagickFalse)
7959     {
7960       if (image->storage_class == UndefinedClass)
7961           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7962           "    storage_class=UndefinedClass");
7963       if (image->storage_class == DirectClass)
7964           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7965           "    storage_class=DirectClass");
7966       if (image->storage_class == PseudoClass)
7967           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7968           "    storage_class=PseudoClass");
7969     }
7970
7971   if (image->storage_class == PseudoClass &&
7972      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7973      mng_info->write_png48 || mng_info->write_png64 ||
7974      (mng_info->write_png_colortype != 1 &&
7975      mng_info->write_png_colortype != 5)))
7976     {
7977       (void) SyncImage(image,exception);
7978       image->storage_class = DirectClass;
7979     }
7980
7981   if (ping_preserve_colormap == MagickFalse)
7982     {
7983       if (image->storage_class != PseudoClass && image->colormap != NULL)
7984         {
7985           /* Free the bogus colormap; it can cause trouble later */
7986            if (logging != MagickFalse)
7987               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7988               "    Freeing bogus colormap");
7989            (void) RelinquishMagickMemory(image->colormap);
7990            image->colormap=NULL;
7991         }
7992     }
7993
7994   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
7995     (void) TransformImageColorspace(image,sRGBColorspace,exception);
7996
7997   /*
7998     Sometimes we get PseudoClass images whose RGB values don't match
7999     the colors in the colormap.  This code syncs the RGB values.
8000   */
8001   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8002      (void) SyncImage(image,exception);
8003
8004 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8005   if (image->depth > 8)
8006     {
8007       if (logging != MagickFalse)
8008         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8009           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8010
8011       image->depth=8;
8012     }
8013 #endif
8014
8015   /* Respect the -depth option */
8016   if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
8017     {
8018        register Quantum
8019          *r;
8020
8021        if (image->depth > 8)
8022          {
8023 #if MAGICKCORE_QUANTUM_DEPTH > 16
8024            /* Scale to 16-bit */
8025            LBR16PacketRGBO(image->background_color);
8026
8027            for (y=0; y < (ssize_t) image->rows; y++)
8028            {
8029              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8030
8031              if (r == (Quantum *) NULL)
8032                break;
8033
8034              for (x=0; x < (ssize_t) image->columns; x++)
8035              {
8036                 LBR16PixelRGBA(r);
8037                 r+=GetPixelChannels(image);
8038              }
8039
8040              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8041                 break;
8042            }
8043
8044            if (image->storage_class == PseudoClass && image->colormap != NULL)
8045            {
8046              for (i=0; i < (ssize_t) image->colors; i++)
8047              {
8048                LBR16PacketRGBO(image->colormap[i]);
8049              }
8050            }
8051 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
8052          }
8053
8054        else if (image->depth > 4)
8055          {
8056 #if MAGICKCORE_QUANTUM_DEPTH > 8
8057            /* Scale to 8-bit */
8058            LBR08PacketRGBO(image->background_color);
8059
8060            for (y=0; y < (ssize_t) image->rows; y++)
8061            {
8062              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8063
8064              if (r == (Quantum *) NULL)
8065                break;
8066
8067              for (x=0; x < (ssize_t) image->columns; x++)
8068              {
8069                 LBR08PixelRGBA(r);
8070                 r+=GetPixelChannels(image);
8071              }
8072
8073              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8074                 break;
8075            }
8076
8077            if (image->storage_class == PseudoClass && image->colormap != NULL)
8078            {
8079              for (i=0; i < (ssize_t) image->colors; i++)
8080              {
8081                LBR08PacketRGBO(image->colormap[i]);
8082              }
8083            }
8084 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
8085          }
8086        else
8087          if (image->depth > 2)
8088          {
8089            /* Scale to 4-bit */
8090            LBR04PacketRGBO(image->background_color);
8091
8092            for (y=0; y < (ssize_t) image->rows; y++)
8093            {
8094              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8095
8096              if (r == (Quantum *) NULL)
8097                break;
8098
8099              for (x=0; x < (ssize_t) image->columns; x++)
8100              {
8101                 LBR04PixelRGBA(r);
8102                 r+=GetPixelChannels(image);
8103              }
8104
8105              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8106                 break;
8107            }
8108
8109            if (image->storage_class == PseudoClass && image->colormap != NULL)
8110            {
8111              for (i=0; i < (ssize_t) image->colors; i++)
8112              {
8113                LBR04PacketRGBO(image->colormap[i]);
8114              }
8115            }
8116          }
8117
8118        else if (image->depth > 1)
8119          {
8120            /* Scale to 2-bit */
8121            LBR02PacketRGBO(image->background_color);
8122
8123            for (y=0; y < (ssize_t) image->rows; y++)
8124            {
8125              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8126
8127              if (r == (Quantum *) NULL)
8128                break;
8129
8130              for (x=0; x < (ssize_t) image->columns; x++)
8131              {
8132                 LBR02PixelRGBA(r);
8133                 r+=GetPixelChannels(image);
8134              }
8135
8136              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8137                 break;
8138            }
8139
8140            if (image->storage_class == PseudoClass && image->colormap != NULL)
8141            {
8142              for (i=0; i < (ssize_t) image->colors; i++)
8143              {
8144                LBR02PacketRGBO(image->colormap[i]);
8145              }
8146            }
8147          }
8148        else
8149          {
8150            /* Scale to 1-bit */
8151            LBR01PacketRGBO(image->background_color);
8152
8153            for (y=0; y < (ssize_t) image->rows; y++)
8154            {
8155              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8156
8157              if (r == (Quantum *) NULL)
8158                break;
8159
8160              for (x=0; x < (ssize_t) image->columns; x++)
8161              {
8162                 LBR01PixelRGBA(r);
8163                 r+=GetPixelChannels(image);
8164              }
8165
8166              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8167                 break;
8168            }
8169
8170            if (image->storage_class == PseudoClass && image->colormap != NULL)
8171            {
8172              for (i=0; i < (ssize_t) image->colors; i++)
8173              {
8174                LBR01PacketRGBO(image->colormap[i]);
8175              }
8176            }
8177          }
8178     }
8179
8180   /* To do: set to next higher multiple of 8 */
8181   if (image->depth < 8)
8182      image->depth=8;
8183
8184 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8185   /* PNG does not handle depths greater than 16 so reduce it even
8186    * if lossy
8187    */
8188   if (image->depth > 8)
8189       image->depth=16;
8190 #endif
8191
8192 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8193   if (image->depth > 8)
8194     {
8195       /* To do: fill low byte properly */
8196       image->depth=16;
8197     }
8198
8199   if (image->depth == 16 && mng_info->write_png_depth != 16)
8200     if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8201       image->depth = 8;
8202 #endif
8203
8204   if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
8205      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8206      mng_info->write_png_colortype < 4 &&
8207      image->alpha_trait != BlendPixelTrait)))
8208   {
8209      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8210       * are not going to need the result.
8211       */
8212      image_colors = (int) image->colors;
8213      number_opaque = (int) image->colors;
8214      if (mng_info->write_png_colortype == 1 ||
8215         mng_info->write_png_colortype == 5)
8216        ping_have_color=MagickFalse;
8217      else
8218        ping_have_color=MagickTrue;
8219      ping_have_non_bw=MagickFalse;
8220
8221      if (image->alpha_trait == BlendPixelTrait)
8222        {
8223          number_transparent = 2;
8224          number_semitransparent = 1;
8225        }
8226
8227      else
8228        {
8229          number_transparent = 0;
8230          number_semitransparent = 0;
8231        }
8232   }
8233
8234   else
8235   {
8236   /* BUILD_PALETTE
8237    *
8238    * Normally we run this just once, but in the case of writing PNG8
8239    * we reduce the transparency to binary and run again, then if there
8240    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8241    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8242    * palette.  Then (To do) we take care of a final reduction that is only
8243    * needed if there are still 256 colors present and one of them has both
8244    * transparent and opaque instances.
8245    */
8246
8247   tried_332 = MagickFalse;
8248   tried_333 = MagickFalse;
8249   tried_444 = MagickFalse;
8250
8251   for (j=0; j<6; j++)
8252   {
8253     /*
8254      * Sometimes we get DirectClass images that have 256 colors or fewer.
8255      * This code will build a colormap.
8256      *
8257      * Also, sometimes we get PseudoClass images with an out-of-date
8258      * colormap.  This code will replace the colormap with a new one.
8259      * Sometimes we get PseudoClass images that have more than 256 colors.
8260      * This code will delete the colormap and change the image to
8261      * DirectClass.
8262      *
8263      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8264      * even though it sometimes contains left-over non-opaque values.
8265      *
8266      * Also we gather some information (number of opaque, transparent,
8267      * and semitransparent pixels, and whether the image has any non-gray
8268      * pixels or only black-and-white pixels) that we might need later.
8269      *
8270      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8271      * we need to check for bogus non-opaque values, at least.
8272      */
8273
8274    int
8275      n;
8276
8277    PixelInfo
8278      opaque[260],
8279      semitransparent[260],
8280      transparent[260];
8281
8282    register const Quantum
8283      *s;
8284
8285    register Quantum
8286      *q,
8287      *r;
8288
8289    if (logging != MagickFalse)
8290      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8291          "    Enter BUILD_PALETTE:");
8292
8293    if (logging != MagickFalse)
8294      {
8295        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8296              "      image->columns=%.20g",(double) image->columns);
8297        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8298              "      image->rows=%.20g",(double) image->rows);
8299        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8300              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
8301        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8302              "      image->depth=%.20g",(double) image->depth);
8303
8304        if (image->storage_class == PseudoClass && image->colormap != NULL)
8305        {
8306          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8307              "      Original colormap:");
8308          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8309              "        i    (red,green,blue,alpha)");
8310
8311          for (i=0; i < 256; i++)
8312          {
8313                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8314                    "        %d    (%d,%d,%d,%d)",
8315                     (int) i,
8316                     (int) image->colormap[i].red,
8317                     (int) image->colormap[i].green,
8318                     (int) image->colormap[i].blue,
8319                     (int) image->colormap[i].alpha);
8320          }
8321
8322          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8323          {
8324            if (i > 255)
8325              {
8326                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8327                    "        %d    (%d,%d,%d,%d)",
8328                     (int) i,
8329                     (int) image->colormap[i].red,
8330                     (int) image->colormap[i].green,
8331                     (int) image->colormap[i].blue,
8332                     (int) image->colormap[i].alpha);
8333              }
8334          }
8335        }
8336
8337        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8338            "      image->colors=%d",(int) image->colors);
8339
8340        if (image->colors == 0)
8341          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8342              "        (zero means unknown)");
8343
8344        if (ping_preserve_colormap == MagickFalse)
8345          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8346               "      Regenerate the colormap");
8347      }
8348
8349      image_colors=0;
8350      number_opaque = 0;
8351      number_semitransparent = 0;
8352      number_transparent = 0;
8353
8354      for (y=0; y < (ssize_t) image->rows; y++)
8355      {
8356        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8357
8358        if (q == (Quantum *) NULL)
8359          break;
8360
8361        for (x=0; x < (ssize_t) image->columns; x++)
8362        {
8363            if (image->alpha_trait != BlendPixelTrait ||
8364               GetPixelAlpha(image,q) == OpaqueAlpha)
8365              {
8366                if (number_opaque < 259)
8367                  {
8368                    if (number_opaque == 0)
8369                      {
8370                        GetPixelInfoPixel(image, q, opaque);
8371                        opaque[0].alpha=OpaqueAlpha;
8372                        number_opaque=1;
8373                      }
8374
8375                    for (i=0; i< (ssize_t) number_opaque; i++)
8376                      {
8377                        if (IsPixelEquivalent(image,q, opaque+i))
8378                          break;
8379                      }
8380
8381                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
8382                      {
8383                        number_opaque++;
8384                        GetPixelInfoPixel(image, q, opaque+i);
8385                        opaque[i].alpha=OpaqueAlpha;
8386                      }
8387                  }
8388              }
8389            else if (GetPixelAlpha(image,q) == TransparentAlpha)
8390              {
8391                if (number_transparent < 259)
8392                  {
8393                    if (number_transparent == 0)
8394                      {
8395                        GetPixelInfoPixel(image, q, transparent);
8396                        ping_trans_color.red=(unsigned short)
8397                          GetPixelRed(image,q);
8398                        ping_trans_color.green=(unsigned short)
8399                          GetPixelGreen(image,q);
8400                        ping_trans_color.blue=(unsigned short)
8401                          GetPixelBlue(image,q);
8402                        ping_trans_color.gray=(unsigned short)
8403                          GetPixelGray(image,q);
8404                        number_transparent = 1;
8405                      }
8406
8407                    for (i=0; i< (ssize_t) number_transparent; i++)
8408                      {
8409                        if (IsPixelEquivalent(image,q, transparent+i))
8410                          break;
8411                      }
8412
8413                    if (i ==  (ssize_t) number_transparent &&
8414                        number_transparent < 259)
8415                      {
8416                        number_transparent++;
8417                        GetPixelInfoPixel(image,q,transparent+i);
8418                      }
8419                  }
8420              }
8421            else
8422              {
8423                if (number_semitransparent < 259)
8424                  {
8425                    if (number_semitransparent == 0)
8426                      {
8427                        GetPixelInfoPixel(image,q,semitransparent);
8428                        number_semitransparent = 1;
8429                      }
8430
8431                    for (i=0; i< (ssize_t) number_semitransparent; i++)
8432                      {
8433                        if (IsPixelEquivalent(image,q, semitransparent+i)
8434                            && GetPixelAlpha(image,q) ==
8435                            semitransparent[i].alpha)
8436                          break;
8437                      }
8438
8439                    if (i ==  (ssize_t) number_semitransparent &&
8440                        number_semitransparent < 259)
8441                      {
8442                        number_semitransparent++;
8443                        GetPixelInfoPixel(image, q, semitransparent+i);
8444                      }
8445                  }
8446              }
8447            q+=GetPixelChannels(image);
8448         }
8449      }
8450
8451      if (mng_info->write_png8 == MagickFalse &&
8452          ping_exclude_bKGD == MagickFalse)
8453        {
8454          /* Add the background color to the palette, if it
8455           * isn't already there.
8456           */
8457           if (logging != MagickFalse)
8458             {
8459               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8460                   "      Check colormap for background (%d,%d,%d)",
8461                   (int) image->background_color.red,
8462                   (int) image->background_color.green,
8463                   (int) image->background_color.blue);
8464             }
8465           for (i=0; i<number_opaque; i++)
8466           {
8467              if (opaque[i].red == image->background_color.red &&
8468                  opaque[i].green == image->background_color.green &&
8469                  opaque[i].blue == image->background_color.blue)
8470                break;
8471           }
8472           if (number_opaque < 259 && i == number_opaque)
8473             {
8474                opaque[i] = image->background_color;
8475                ping_background.index = i;
8476                number_opaque++;
8477                if (logging != MagickFalse)
8478                  {
8479                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8480                        "      background_color index is %d",(int) i);
8481                  }
8482
8483             }
8484           else if (logging != MagickFalse)
8485               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8486                   "      No room in the colormap to add background color");
8487        }
8488
8489      image_colors=number_opaque+number_transparent+number_semitransparent;
8490
8491      if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8492        {
8493          /* No room for the background color; remove it. */
8494          number_opaque--;
8495          image_colors--;
8496        }
8497
8498      if (logging != MagickFalse)
8499        {
8500          if (image_colors > 256)
8501             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8502                   "      image has more than 256 colors");
8503
8504          else
8505             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8506                   "      image has %d colors",image_colors);
8507        }
8508
8509      if (ping_preserve_colormap != MagickFalse)
8510        break;
8511
8512      if (mng_info->write_png_colortype != 7) /* We won't need this info */
8513        {
8514          ping_have_color=MagickFalse;
8515          ping_have_non_bw=MagickFalse;
8516
8517          if ((IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) ||
8518              (IssRGBColorspace(image->colorspace) != MagickFalse))
8519          {
8520            ping_have_color=MagickTrue;
8521            ping_have_non_bw=MagickTrue;
8522          }
8523
8524          if(image_colors > 256)
8525            {
8526              for (y=0; y < (ssize_t) image->rows; y++)
8527              {
8528                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8529
8530                if (q == (Quantum *) NULL)
8531                  break;
8532
8533                s=q;
8534                for (x=0; x < (ssize_t) image->columns; x++)
8535                {
8536                  if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8537                      GetPixelRed(image,s) != GetPixelBlue(image,s))
8538                    {
8539                       ping_have_color=MagickTrue;
8540                       ping_have_non_bw=MagickTrue;
8541                       break;
8542                    }
8543                  s+=GetPixelChannels(image);
8544                }
8545
8546                if (ping_have_color != MagickFalse)
8547                  break;
8548
8549                /* Worst case is black-and-white; we are looking at every
8550                 * pixel twice.
8551                 */
8552
8553                if (ping_have_non_bw == MagickFalse)
8554                  {
8555                    s=q;
8556                    for (x=0; x < (ssize_t) image->columns; x++)
8557                    {
8558                      if (GetPixelRed(image,s) != 0 &&
8559                          GetPixelRed(image,s) != QuantumRange)
8560                        {
8561                          ping_have_non_bw=MagickTrue;
8562                          break;
8563                        }
8564                      s+=GetPixelChannels(image);
8565                    }
8566                }
8567              }
8568            }
8569        }
8570
8571      if (image_colors < 257)
8572        {
8573          PixelInfo
8574            colormap[260];
8575
8576          /*
8577           * Initialize image colormap.
8578           */
8579
8580          if (logging != MagickFalse)
8581             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8582                   "      Sort the new colormap");
8583
8584         /* Sort palette, transparent first */;
8585
8586          n = 0;
8587
8588          for (i=0; i<number_transparent; i++)
8589             colormap[n++] = transparent[i];
8590
8591          for (i=0; i<number_semitransparent; i++)
8592             colormap[n++] = semitransparent[i];
8593
8594          for (i=0; i<number_opaque; i++)
8595             colormap[n++] = opaque[i];
8596
8597          ping_background.index +=
8598            (number_transparent + number_semitransparent);
8599
8600          /* image_colors < 257; search the colormap instead of the pixels
8601           * to get ping_have_color and ping_have_non_bw
8602           */
8603          for (i=0; i<n; i++)
8604          {
8605            if (ping_have_color == MagickFalse)
8606              {
8607                 if (colormap[i].red != colormap[i].green ||
8608                     colormap[i].red != colormap[i].blue)
8609                   {
8610                      ping_have_color=MagickTrue;
8611                      ping_have_non_bw=MagickTrue;
8612                      break;
8613                   }
8614               }
8615
8616            if (ping_have_non_bw == MagickFalse)
8617              {
8618                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8619                    ping_have_non_bw=MagickTrue;
8620              }
8621           }
8622
8623         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8624             (number_transparent == 0 && number_semitransparent == 0)) &&
8625             (((mng_info->write_png_colortype-1) ==
8626             PNG_COLOR_TYPE_PALETTE) ||
8627             (mng_info->write_png_colortype == 0)))
8628           {
8629             if (logging != MagickFalse)
8630               {
8631                 if (n !=  (ssize_t) image_colors)
8632                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8633                    "   image_colors (%d) and n (%d)  don't match",
8634                    image_colors, n);
8635
8636                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8637                    "      AcquireImageColormap");
8638               }
8639
8640             image->colors = image_colors;
8641
8642             if (AcquireImageColormap(image,image_colors,exception) ==
8643                 MagickFalse)
8644                ThrowWriterException(ResourceLimitError,
8645                    "MemoryAllocationFailed");
8646
8647             for (i=0; i< (ssize_t) image_colors; i++)
8648                image->colormap[i] = colormap[i];
8649
8650             if (logging != MagickFalse)
8651               {
8652                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8653                       "      image->colors=%d (%d)",
8654                       (int) image->colors, image_colors);
8655
8656                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8657                       "      Update the pixel indexes");
8658               }
8659
8660             /* Sync the pixel indices with the new colormap */
8661
8662             for (y=0; y < (ssize_t) image->rows; y++)
8663             {
8664               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8665
8666               if (q == (Quantum *) NULL)
8667                 break;
8668
8669               for (x=0; x < (ssize_t) image->columns; x++)
8670               {
8671                 for (i=0; i< (ssize_t) image_colors; i++)
8672                 {
8673                   if ((image->alpha_trait != BlendPixelTrait ||
8674                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8675                       image->colormap[i].red == GetPixelRed(image,q) &&
8676                       image->colormap[i].green == GetPixelGreen(image,q) &&
8677                       image->colormap[i].blue == GetPixelBlue(image,q))
8678                   {
8679                     SetPixelIndex(image,i,q);
8680                     break;
8681                   }
8682                 }
8683                 q+=GetPixelChannels(image);
8684               }
8685
8686               if (SyncAuthenticPixels(image,exception) == MagickFalse)
8687                  break;
8688             }
8689           }
8690        }
8691
8692      if (logging != MagickFalse)
8693        {
8694          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8695             "      image->colors=%d", (int) image->colors);
8696
8697          if (image->colormap != NULL)
8698            {
8699              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8700                  "       i     (red,green,blue,alpha)");
8701
8702              for (i=0; i < (ssize_t) image->colors; i++)
8703              {
8704                if (i < 300 || i >= (ssize_t) image->colors - 10)
8705                  {
8706                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8707                        "       %d     (%d,%d,%d,%d)",
8708                         (int) i,
8709                         (int) image->colormap[i].red,
8710                         (int) image->colormap[i].green,
8711                         (int) image->colormap[i].blue,
8712                         (int) image->colormap[i].alpha);
8713                  }
8714              }
8715            }
8716
8717            if (number_transparent < 257)
8718              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8719                    "      number_transparent     = %d",
8720                    number_transparent);
8721            else
8722
8723              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8724                    "      number_transparent     > 256");
8725
8726            if (number_opaque < 257)
8727              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8728                    "      number_opaque          = %d",
8729                    number_opaque);
8730
8731            else
8732              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8733                    "      number_opaque          > 256");
8734
8735            if (number_semitransparent < 257)
8736              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8737                    "      number_semitransparent = %d",
8738                    number_semitransparent);
8739
8740            else
8741              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8742                    "      number_semitransparent > 256");
8743
8744            if (ping_have_non_bw == MagickFalse)
8745               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8746                     "      All pixels and the background are black or white");
8747
8748            else if (ping_have_color == MagickFalse)
8749               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8750                     "      All pixels and the background are gray");
8751
8752            else
8753               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8754                     "      At least one pixel or the background is non-gray");
8755
8756            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8757                "    Exit BUILD_PALETTE:");
8758        }
8759
8760    if (mng_info->write_png8 == MagickFalse)
8761       break;
8762
8763    /* Make any reductions necessary for the PNG8 format */
8764     if (image_colors <= 256 &&
8765         image_colors != 0 && image->colormap != NULL &&
8766         number_semitransparent == 0 &&
8767         number_transparent <= 1)
8768       break;
8769
8770     /* PNG8 can't have semitransparent colors so we threshold the
8771      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8772      * transparent color so if more than one is transparent we merge
8773      * them into image->background_color.
8774      */
8775     if (number_semitransparent != 0 || number_transparent > 1)
8776       {
8777         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8778             "    Thresholding the alpha channel to binary");
8779
8780         for (y=0; y < (ssize_t) image->rows; y++)
8781         {
8782           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8783
8784           if (r == (Quantum *) NULL)
8785             break;
8786
8787           for (x=0; x < (ssize_t) image->columns; x++)
8788           {
8789               if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8790                 {
8791                   SetPixelInfoPixel(image,&image->background_color,r);
8792                   SetPixelAlpha(image,TransparentAlpha,r);
8793                 }
8794               else
8795                   SetPixelAlpha(image,OpaqueAlpha,r);
8796               r+=GetPixelChannels(image);
8797           }
8798
8799           if (SyncAuthenticPixels(image,exception) == MagickFalse)
8800              break;
8801
8802           if (image_colors != 0 && image_colors <= 256 &&
8803              image->colormap != NULL)
8804             for (i=0; i<image_colors; i++)
8805                 image->colormap[i].alpha =
8806                     (image->colormap[i].alpha > TransparentAlpha/2 ?
8807                     TransparentAlpha : OpaqueAlpha);
8808         }
8809       continue;
8810     }
8811
8812     /* PNG8 can't have more than 256 colors so we quantize the pixels and
8813      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
8814      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8815      * colors or less.
8816      */
8817     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8818       {
8819         if (logging != MagickFalse)
8820            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8821                "    Quantizing the background color to 4-4-4");
8822
8823         tried_444 = MagickTrue;
8824
8825         LBR04PacketRGB(image->background_color);
8826
8827         if (logging != MagickFalse)
8828           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8829               "    Quantizing the pixel colors to 4-4-4");
8830
8831         if (image->colormap == NULL)
8832         {
8833           for (y=0; y < (ssize_t) image->rows; y++)
8834           {
8835             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8836
8837             if (r == (Quantum *) NULL)
8838               break;
8839
8840             for (x=0; x < (ssize_t) image->columns; x++)
8841             {
8842               if (GetPixelAlpha(image,r) == OpaqueAlpha)
8843                   LBR04PixelRGB(r);
8844               r+=GetPixelChannels(image);
8845             }
8846
8847             if (SyncAuthenticPixels(image,exception) == MagickFalse)
8848                break;
8849           }
8850         }
8851
8852         else /* Should not reach this; colormap already exists and
8853                 must be <= 256 */
8854         {
8855           if (logging != MagickFalse)
8856               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8857               "    Quantizing the colormap to 4-4-4");
8858
8859           for (i=0; i<image_colors; i++)
8860           {
8861             LBR04PacketRGB(image->colormap[i]);
8862           }
8863         }
8864         continue;
8865       }
8866
8867     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8868       {
8869         if (logging != MagickFalse)
8870            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8871                "    Quantizing the background color to 3-3-3");
8872
8873         tried_333 = MagickTrue;
8874
8875         LBR03PacketRGB(image->background_color);
8876
8877         if (logging != MagickFalse)
8878           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8879               "    Quantizing the pixel colors to 3-3-3-1");
8880
8881         if (image->colormap == NULL)
8882         {
8883           for (y=0; y < (ssize_t) image->rows; y++)
8884           {
8885             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8886
8887             if (r == (Quantum *) NULL)
8888               break;
8889
8890             for (x=0; x < (ssize_t) image->columns; x++)
8891             {
8892               if (GetPixelAlpha(image,r) == OpaqueAlpha)
8893                   LBR03RGB(r);
8894               r+=GetPixelChannels(image);
8895             }
8896
8897             if (SyncAuthenticPixels(image,exception) == MagickFalse)
8898                break;
8899           }
8900         }
8901
8902         else /* Should not reach this; colormap already exists and
8903                 must be <= 256 */
8904         {
8905           if (logging != MagickFalse)
8906               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8907               "    Quantizing the colormap to 3-3-3-1");
8908           for (i=0; i<image_colors; i++)
8909           {
8910               LBR03PacketRGB(image->colormap[i]);
8911           }
8912         }
8913         continue;
8914       }
8915
8916     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8917       {
8918         if (logging != MagickFalse)
8919            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8920                "    Quantizing the background color to 3-3-2");
8921
8922         tried_332 = MagickTrue;
8923
8924         /* Red and green were already done so we only quantize the blue
8925          * channel
8926          */
8927
8928         LBR02PacketBlue(image->background_color);
8929
8930         if (logging != MagickFalse)
8931           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8932               "    Quantizing the pixel colors to 3-3-2-1");
8933
8934         if (image->colormap == NULL)
8935         {
8936           for (y=0; y < (ssize_t) image->rows; y++)
8937           {
8938             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8939
8940             if (r == (Quantum *) NULL)
8941               break;
8942
8943             for (x=0; x < (ssize_t) image->columns; x++)
8944             {
8945               if (GetPixelAlpha(image,r) == OpaqueAlpha)
8946                   LBR02PixelBlue(r);
8947               r+=GetPixelChannels(image);
8948             }
8949
8950             if (SyncAuthenticPixels(image,exception) == MagickFalse)
8951                break;
8952           }
8953         }
8954
8955         else /* Should not reach this; colormap already exists and
8956                 must be <= 256 */
8957         {
8958           if (logging != MagickFalse)
8959               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8960               "    Quantizing the colormap to 3-3-2-1");
8961           for (i=0; i<image_colors; i++)
8962           {
8963               LBR02PacketBlue(image->colormap[i]);
8964           }
8965       }
8966       continue;
8967     }
8968     break;
8969
8970     if (image_colors == 0 || image_colors > 256)
8971     {
8972       /* Take care of special case with 256 colors + 1 transparent
8973        * color.  We don't need to quantize to 2-3-2-1; we only need to
8974        * eliminate one color, so we'll merge the two darkest red
8975        * colors (0x49, 0, 0) -> (0x24, 0, 0).
8976        */
8977       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8978           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8979           ScaleQuantumToChar(image->background_color.blue) == 0x00)
8980       {
8981          image->background_color.red=ScaleCharToQuantum(0x24);
8982       }
8983
8984       if (image->colormap == NULL)
8985       {
8986         for (y=0; y < (ssize_t) image->rows; y++)
8987         {
8988           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8989
8990           if (r == (Quantum *) NULL)
8991             break;
8992
8993           for (x=0; x < (ssize_t) image->columns; x++)
8994           {
8995             if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8996                 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8997                 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8998                 GetPixelAlpha(image,r) == OpaqueAlpha)
8999               {
9000                 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9001               }
9002             r+=GetPixelChannels(image);
9003           }
9004
9005           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9006              break;
9007
9008         }
9009       }
9010
9011       else
9012       {
9013          for (i=0; i<image_colors; i++)
9014          {
9015             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9016                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9017                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9018             {
9019                image->colormap[i].red=ScaleCharToQuantum(0x24);
9020             }
9021          }
9022       }
9023     }
9024   }
9025   }
9026   /* END OF BUILD_PALETTE */
9027
9028   /* If we are excluding the tRNS chunk and there is transparency,
9029    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9030    * PNG.
9031    */
9032   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9033      (number_transparent != 0 || number_semitransparent != 0))
9034     {
9035       unsigned int colortype=mng_info->write_png_colortype;
9036
9037       if (ping_have_color == MagickFalse)
9038         mng_info->write_png_colortype = 5;
9039
9040       else
9041         mng_info->write_png_colortype = 7;
9042
9043       if (colortype != 0 &&
9044          mng_info->write_png_colortype != colortype)
9045         ping_need_colortype_warning=MagickTrue;
9046
9047     }
9048
9049   /* See if cheap transparency is possible.  It is only possible
9050    * when there is a single transparent color, no semitransparent
9051    * color, and no opaque color that has the same RGB components
9052    * as the transparent color.  We only need this information if
9053    * we are writing a PNG with colortype 0 or 2, and we have not
9054    * excluded the tRNS chunk.
9055    */
9056   if (number_transparent == 1 &&
9057       mng_info->write_png_colortype < 4)
9058     {
9059        ping_have_cheap_transparency = MagickTrue;
9060
9061        if (number_semitransparent != 0)
9062          ping_have_cheap_transparency = MagickFalse;
9063
9064        else if (image_colors == 0 || image_colors > 256 ||
9065            image->colormap == NULL)
9066          {
9067            register const Quantum
9068              *q;
9069
9070            for (y=0; y < (ssize_t) image->rows; y++)
9071            {
9072              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9073
9074              if (q == (Quantum *) NULL)
9075                break;
9076
9077              for (x=0; x < (ssize_t) image->columns; x++)
9078              {
9079                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9080                      (unsigned short) GetPixelRed(image,q) ==
9081                                      ping_trans_color.red &&
9082                      (unsigned short) GetPixelGreen(image,q) ==
9083                                      ping_trans_color.green &&
9084                      (unsigned short) GetPixelBlue(image,q) ==
9085                                      ping_trans_color.blue)
9086                    {
9087                      ping_have_cheap_transparency = MagickFalse;
9088                      break;
9089                    }
9090
9091                  q+=GetPixelChannels(image);
9092              }
9093
9094              if (ping_have_cheap_transparency == MagickFalse)
9095                 break;
9096            }
9097          }
9098        else
9099          {
9100             /* Assuming that image->colormap[0] is the one transparent color
9101              * and that all others are opaque.
9102              */
9103             if (image_colors > 1)
9104               for (i=1; i<image_colors; i++)
9105                 if (image->colormap[i].red == image->colormap[0].red &&
9106                     image->colormap[i].green == image->colormap[0].green &&
9107                     image->colormap[i].blue == image->colormap[0].blue)
9108                   {
9109                      ping_have_cheap_transparency = MagickFalse;
9110                      break;
9111                   }
9112          }
9113
9114        if (logging != MagickFalse)
9115          {
9116            if (ping_have_cheap_transparency == MagickFalse)
9117              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9118                  "   Cheap transparency is not possible.");
9119
9120            else
9121              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9122                  "   Cheap transparency is possible.");
9123          }
9124      }
9125   else
9126     ping_have_cheap_transparency = MagickFalse;
9127
9128   image_depth=image->depth;
9129
9130   quantum_info = (QuantumInfo *) NULL;
9131   number_colors=0;
9132   image_colors=(int) image->colors;
9133   image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
9134
9135   mng_info->IsPalette=image->storage_class == PseudoClass &&
9136     image_colors <= 256 && image->colormap != NULL;
9137
9138   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9139      (image->colors == 0 || image->colormap == NULL))
9140     {
9141       image_info=DestroyImageInfo(image_info);
9142       image=DestroyImage(image);
9143       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9144           "Cannot write PNG8 or color-type 3; colormap is NULL",
9145           "`%s'",IMimage->filename);
9146       return(MagickFalse);
9147     }
9148
9149   /*
9150     Allocate the PNG structures
9151   */
9152 #ifdef PNG_USER_MEM_SUPPORTED
9153  error_info.image=image;
9154  error_info.exception=exception;
9155   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9156     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9157     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9158
9159 #else
9160   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9161     MagickPNGErrorHandler,MagickPNGWarningHandler);
9162
9163 #endif
9164   if (ping == (png_struct *) NULL)
9165     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9166
9167   ping_info=png_create_info_struct(ping);
9168
9169   if (ping_info == (png_info *) NULL)
9170     {
9171       png_destroy_write_struct(&ping,(png_info **) NULL);
9172       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9173     }
9174
9175   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9176   ping_pixels=(unsigned char *) NULL;
9177
9178   if (setjmp(png_jmpbuf(ping)))
9179     {
9180       /*
9181         PNG write failed.
9182       */
9183 #ifdef PNG_DEBUG
9184      if (image_info->verbose)
9185         (void) printf("PNG write has failed.\n");
9186 #endif
9187       png_destroy_write_struct(&ping,&ping_info);
9188 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9189       UnlockSemaphoreInfo(ping_semaphore);
9190 #endif
9191
9192       if (ping_pixels != (unsigned char *) NULL)
9193         ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9194
9195       if (quantum_info != (QuantumInfo *) NULL)
9196         quantum_info=DestroyQuantumInfo(quantum_info);
9197
9198       if (ping_have_blob != MagickFalse)
9199           (void) CloseBlob(image);
9200       image_info=DestroyImageInfo(image_info);
9201       image=DestroyImage(image);
9202       return(MagickFalse);
9203     }
9204
9205   /* {  For navigation to end of SETJMP-protected block.  Within this
9206    *    block, use png_error() instead of Throwing an Exception, to ensure
9207    *    that libpng is able to clean up, and that the semaphore is unlocked.
9208    */
9209
9210 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9211   LockSemaphoreInfo(ping_semaphore);
9212 #endif
9213
9214   /*
9215     Prepare PNG for writing.
9216   */
9217
9218 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9219   if (mng_info->write_mng)
9220   {
9221      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9222 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9223      /* Disable new libpng-1.5.10 feature when writing a MNG because
9224       * zero-length PLTE is OK
9225       */
9226      png_set_check_for_invalid_index (ping, 0);
9227 # endif
9228   }
9229
9230 #else
9231 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9232   if (mng_info->write_mng)
9233      png_permit_empty_plte(ping,MagickTrue);
9234
9235 # endif
9236 #endif
9237
9238   x=0;
9239
9240   ping_width=(png_uint_32) image->columns;
9241   ping_height=(png_uint_32) image->rows;
9242
9243   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9244      image_depth=8;
9245
9246   if (mng_info->write_png48 || mng_info->write_png64)
9247      image_depth=16;
9248
9249   if (mng_info->write_png_depth != 0)
9250      image_depth=mng_info->write_png_depth;
9251
9252   /* Adjust requested depth to next higher valid depth if necessary */
9253   if (image_depth > 8)
9254      image_depth=16;
9255
9256   if ((image_depth > 4) && (image_depth < 8))
9257      image_depth=8;
9258
9259   if (image_depth == 3)
9260      image_depth=4;
9261
9262   if (logging != MagickFalse)
9263     {
9264      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9265         "    width=%.20g",(double) ping_width);
9266      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9267         "    height=%.20g",(double) ping_height);
9268      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9269         "    image_matte=%.20g",(double) image->alpha_trait);
9270      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9271         "    image->depth=%.20g",(double) image->depth);
9272      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9273         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
9274     }
9275
9276   save_image_depth=image_depth;
9277   ping_bit_depth=(png_byte) save_image_depth;
9278
9279
9280 #if defined(PNG_pHYs_SUPPORTED)
9281   if (ping_exclude_pHYs == MagickFalse)
9282   {
9283   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9284       (!mng_info->write_mng || !mng_info->equal_physs))
9285     {
9286       if (logging != MagickFalse)
9287         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9288             "    Setting up pHYs chunk");
9289
9290       if (image->units == PixelsPerInchResolution)
9291         {
9292           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9293           ping_pHYs_x_resolution=
9294              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9295           ping_pHYs_y_resolution=
9296              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9297         }
9298
9299       else if (image->units == PixelsPerCentimeterResolution)
9300         {
9301           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9302           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9303           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9304         }
9305
9306       else
9307         {
9308           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9309           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9310           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9311         }
9312
9313       if (logging != MagickFalse)
9314         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9315           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9316           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9317           (int) ping_pHYs_unit_type);
9318        ping_have_pHYs = MagickTrue;
9319     }
9320   }
9321 #endif
9322
9323   if (ping_exclude_bKGD == MagickFalse)
9324   {
9325   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9326     {
9327        unsigned int
9328          mask;
9329
9330        mask=0xffff;
9331        if (ping_bit_depth == 8)
9332           mask=0x00ff;
9333
9334        if (ping_bit_depth == 4)
9335           mask=0x000f;
9336
9337        if (ping_bit_depth == 2)
9338           mask=0x0003;
9339
9340        if (ping_bit_depth == 1)
9341           mask=0x0001;
9342
9343        ping_background.red=(png_uint_16)
9344          (ScaleQuantumToShort(image->background_color.red) & mask);
9345
9346        ping_background.green=(png_uint_16)
9347          (ScaleQuantumToShort(image->background_color.green) & mask);
9348
9349        ping_background.blue=(png_uint_16)
9350          (ScaleQuantumToShort(image->background_color.blue) & mask);
9351
9352        ping_background.gray=(png_uint_16) ping_background.green;
9353     }
9354
9355   if (logging != MagickFalse)
9356     {
9357       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9358           "    Setting up bKGD chunk (1)");
9359       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9360           "      background_color index is %d",
9361           (int) ping_background.index);
9362
9363       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9364           "    ping_bit_depth=%d",ping_bit_depth);
9365     }
9366
9367   ping_have_bKGD = MagickTrue;
9368   }
9369
9370   /*
9371     Select the color type.
9372   */
9373   matte=image_matte;
9374   old_bit_depth=0;
9375
9376   if (mng_info->IsPalette && mng_info->write_png8)
9377     {
9378
9379       /* To do: make this a function cause it's used twice, except
9380          for reducing the sample depth from 8. */
9381
9382       number_colors=image_colors;
9383
9384       ping_have_tRNS=MagickFalse;
9385
9386       /*
9387         Set image palette.
9388       */
9389       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9390
9391       if (logging != MagickFalse)
9392         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9393             "  Setting up PLTE chunk with %d colors (%d)",
9394             number_colors, image_colors);
9395
9396       for (i=0; i < (ssize_t) number_colors; i++)
9397       {
9398         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9399         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9400         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9401         if (logging != MagickFalse)
9402           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9403 #if MAGICKCORE_QUANTUM_DEPTH == 8
9404             "    %3ld (%3d,%3d,%3d)",
9405 #else
9406             "    %5ld (%5d,%5d,%5d)",
9407 #endif
9408             (long) i,palette[i].red,palette[i].green,palette[i].blue);
9409
9410       }
9411
9412       ping_have_PLTE=MagickTrue;
9413       image_depth=ping_bit_depth;
9414       ping_num_trans=0;
9415
9416       if (matte != MagickFalse)
9417       {
9418           /*
9419             Identify which colormap entry is transparent.
9420           */
9421           assert(number_colors <= 256);
9422           assert(image->colormap != NULL);
9423
9424           for (i=0; i < (ssize_t) number_transparent; i++)
9425              ping_trans_alpha[i]=0;
9426
9427
9428           ping_num_trans=(unsigned short) (number_transparent +
9429              number_semitransparent);
9430
9431           if (ping_num_trans == 0)
9432              ping_have_tRNS=MagickFalse;
9433
9434           else
9435              ping_have_tRNS=MagickTrue;
9436       }
9437
9438       if (ping_exclude_bKGD == MagickFalse)
9439       {
9440        /*
9441         * Identify which colormap entry is the background color.
9442         */
9443
9444         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9445           if (IsPNGColorEqual(ping_background,image->colormap[i]))
9446             break;
9447
9448         ping_background.index=(png_byte) i;
9449
9450         if (logging != MagickFalse)
9451           {
9452             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9453                  "      background_color index is %d",
9454                  (int) ping_background.index);
9455           }
9456       }
9457     } /* end of write_png8 */
9458
9459   else if (mng_info->write_png_colortype == 1)
9460     {
9461       image_matte=MagickFalse;
9462       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9463     }
9464
9465   else if (mng_info->write_png24 || mng_info->write_png48 ||
9466       mng_info->write_png_colortype == 3)
9467     {
9468       image_matte=MagickFalse;
9469       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9470     }
9471
9472   else if (mng_info->write_png32 || mng_info->write_png64 ||
9473       mng_info->write_png_colortype == 7)
9474     {
9475       image_matte=MagickTrue;
9476       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9477     }
9478
9479   else /* mng_info->write_pngNN not specified */
9480     {
9481       image_depth=ping_bit_depth;
9482
9483       if (mng_info->write_png_colortype != 0)
9484         {
9485           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9486
9487           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9488               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9489             image_matte=MagickTrue;
9490
9491           else
9492             image_matte=MagickFalse;
9493
9494           if (logging != MagickFalse)
9495              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9496              "   PNG colortype %d was specified:",(int) ping_color_type);
9497         }
9498
9499       else /* write_png_colortype not specified */
9500         {
9501           if (logging != MagickFalse)
9502              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9503              "  Selecting PNG colortype:");
9504
9505           ping_color_type=(png_byte) ((matte != MagickFalse)?
9506             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9507
9508           if (image_info->type == TrueColorType)
9509             {
9510               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9511               image_matte=MagickFalse;
9512             }
9513
9514           if (image_info->type == TrueColorMatteType)
9515             {
9516               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9517               image_matte=MagickTrue;
9518             }
9519
9520           if (image_info->type == PaletteType ||
9521               image_info->type == PaletteMatteType)
9522             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9523
9524           if (mng_info->write_png_colortype == 0 &&
9525              (image_info->type == UndefinedType ||
9526              image_info->type == OptimizeType))
9527             {
9528               if (ping_have_color == MagickFalse)
9529                 {
9530                   if (image_matte == MagickFalse)
9531                     {
9532                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9533                       image_matte=MagickFalse;
9534                     }
9535
9536                   else
9537                     {
9538                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9539                       image_matte=MagickTrue;
9540                     }
9541                 }
9542               else
9543                 {
9544                   if (image_matte == MagickFalse)
9545                     {
9546                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9547                       image_matte=MagickFalse;
9548                     }
9549
9550                   else
9551                     {
9552                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9553                       image_matte=MagickTrue;
9554                     }
9555                  }
9556             }
9557
9558         }
9559
9560       if (logging != MagickFalse)
9561          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9562          "    Selected PNG colortype=%d",ping_color_type);
9563
9564       if (ping_bit_depth < 8)
9565         {
9566           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9567               ping_color_type == PNG_COLOR_TYPE_RGB ||
9568               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9569             ping_bit_depth=8;
9570         }
9571
9572       old_bit_depth=ping_bit_depth;
9573
9574       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9575         {
9576           if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
9577              ping_bit_depth=1;
9578         }
9579
9580       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9581         {
9582            size_t one = 1;
9583            ping_bit_depth=1;
9584
9585            if (image->colors == 0)
9586            {
9587               /* DO SOMETHING */
9588                 png_error(ping,"image has 0 colors");
9589            }
9590
9591            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9592              ping_bit_depth <<= 1;
9593         }
9594
9595       if (logging != MagickFalse)
9596          {
9597            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9598             "    Number of colors: %.20g",(double) image_colors);
9599
9600            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9601             "    Tentative PNG bit depth: %d",ping_bit_depth);
9602          }
9603
9604       if (ping_bit_depth < (int) mng_info->write_png_depth)
9605          ping_bit_depth = mng_info->write_png_depth;
9606     }
9607
9608   image_depth=ping_bit_depth;
9609
9610   if (logging != MagickFalse)
9611     {
9612       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9613         "    Tentative PNG color type: %s (%.20g)",
9614         PngColorTypeToString(ping_color_type),
9615         (double) ping_color_type);
9616
9617       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9618         "    image_info->type: %.20g",(double) image_info->type);
9619
9620       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9621         "    image_depth: %.20g",(double) image_depth);
9622
9623       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9624
9625         "    image->depth: %.20g",(double) image->depth);
9626
9627       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9628         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
9629     }
9630
9631   if (matte != MagickFalse)
9632     {
9633       if (mng_info->IsPalette)
9634         {
9635           if (mng_info->write_png_colortype == 0)
9636             {
9637               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9638
9639               if (ping_have_color != MagickFalse)
9640                  ping_color_type=PNG_COLOR_TYPE_RGBA;
9641             }
9642
9643           /*
9644            * Determine if there is any transparent color.
9645           */
9646           if (number_transparent + number_semitransparent == 0)
9647             {
9648               /*
9649                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
9650               */
9651
9652               image_matte=MagickFalse;
9653
9654               if (mng_info->write_png_colortype == 0)
9655                 ping_color_type&=0x03;
9656             }
9657
9658           else
9659             {
9660               unsigned int
9661                 mask;
9662
9663               mask=0xffff;
9664
9665               if (ping_bit_depth == 8)
9666                  mask=0x00ff;
9667
9668               if (ping_bit_depth == 4)
9669                  mask=0x000f;
9670
9671               if (ping_bit_depth == 2)
9672                  mask=0x0003;
9673
9674               if (ping_bit_depth == 1)
9675                  mask=0x0001;
9676
9677               ping_trans_color.red=(png_uint_16)
9678                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9679
9680               ping_trans_color.green=(png_uint_16)
9681                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9682
9683               ping_trans_color.blue=(png_uint_16)
9684                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9685
9686               ping_trans_color.gray=(png_uint_16)
9687                 (ScaleQuantumToShort(GetPixelInfoIntensity(
9688                    image->colormap)) & mask);
9689
9690               ping_trans_color.index=(png_byte) 0;
9691
9692               ping_have_tRNS=MagickTrue;
9693             }
9694
9695           if (ping_have_tRNS != MagickFalse)
9696             {
9697               /*
9698                * Determine if there is one and only one transparent color
9699                * and if so if it is fully transparent.
9700                */
9701               if (ping_have_cheap_transparency == MagickFalse)
9702                 ping_have_tRNS=MagickFalse;
9703             }
9704
9705           if (ping_have_tRNS != MagickFalse)
9706             {
9707               if (mng_info->write_png_colortype == 0)
9708                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
9709
9710               if (image_depth == 8)
9711                 {
9712                   ping_trans_color.red&=0xff;
9713                   ping_trans_color.green&=0xff;
9714                   ping_trans_color.blue&=0xff;
9715                   ping_trans_color.gray&=0xff;
9716                 }
9717             }
9718         }
9719       else
9720         {
9721           if (image_depth == 8)
9722             {
9723               ping_trans_color.red&=0xff;
9724               ping_trans_color.green&=0xff;
9725               ping_trans_color.blue&=0xff;
9726               ping_trans_color.gray&=0xff;
9727             }
9728         }
9729     }
9730
9731     matte=image_matte;
9732
9733     if (ping_have_tRNS != MagickFalse)
9734       image_matte=MagickFalse;
9735
9736     if ((mng_info->IsPalette) &&
9737         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9738         ping_have_color == MagickFalse &&
9739         (image_matte == MagickFalse || image_depth >= 8))
9740       {
9741         size_t one=1;
9742
9743         if (image_matte != MagickFalse)
9744           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9745
9746         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9747           {
9748             ping_color_type=PNG_COLOR_TYPE_GRAY;
9749
9750             if (save_image_depth == 16 && image_depth == 8)
9751               {
9752                 if (logging != MagickFalse)
9753                   {
9754                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9755                         "  Scaling ping_trans_color (0)");
9756                   }
9757                     ping_trans_color.gray*=0x0101;
9758               }
9759           }
9760
9761         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9762           image_depth=MAGICKCORE_QUANTUM_DEPTH;
9763
9764         if ((image_colors == 0) ||
9765              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9766           image_colors=(int) (one << image_depth);
9767
9768         if (image_depth > 8)
9769           ping_bit_depth=16;
9770
9771         else
9772           {
9773             ping_bit_depth=8;
9774             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9775               {
9776                 if(!mng_info->write_png_depth)
9777                   {
9778                     ping_bit_depth=1;
9779
9780                     while ((int) (one << ping_bit_depth)
9781                         < (ssize_t) image_colors)
9782                       ping_bit_depth <<= 1;
9783                   }
9784               }
9785
9786             else if (ping_color_type ==
9787                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9788                 mng_info->IsPalette)
9789               {
9790               /* Check if grayscale is reducible */
9791
9792                 int
9793                   depth_4_ok=MagickTrue,
9794                   depth_2_ok=MagickTrue,
9795                   depth_1_ok=MagickTrue;
9796
9797                 for (i=0; i < (ssize_t) image_colors; i++)
9798                 {
9799                    unsigned char
9800                      intensity;
9801
9802                    intensity=ScaleQuantumToChar(image->colormap[i].red);
9803
9804                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9805                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9806                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9807                      depth_2_ok=depth_1_ok=MagickFalse;
9808                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9809                      depth_1_ok=MagickFalse;
9810                 }
9811
9812                 if (depth_1_ok && mng_info->write_png_depth <= 1)
9813                   ping_bit_depth=1;
9814
9815                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9816                   ping_bit_depth=2;
9817
9818                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9819                   ping_bit_depth=4;
9820               }
9821           }
9822
9823           image_depth=ping_bit_depth;
9824       }
9825
9826     else
9827
9828       if (mng_info->IsPalette)
9829       {
9830         number_colors=image_colors;
9831
9832         if (image_depth <= 8)
9833           {
9834             /*
9835               Set image palette.
9836             */
9837             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9838
9839             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
9840               {
9841                 for (i=0; i < (ssize_t) number_colors; i++)
9842                 {
9843                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9844                   palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9845                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9846                 }
9847
9848                 if (logging != MagickFalse)
9849                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9850                     "  Setting up PLTE chunk with %d colors",
9851                     number_colors);
9852
9853                 ping_have_PLTE=MagickTrue;
9854               }
9855
9856             /* color_type is PNG_COLOR_TYPE_PALETTE */
9857             if (mng_info->write_png_depth == 0)
9858               {
9859                 size_t
9860                   one;
9861
9862                 ping_bit_depth=1;
9863                 one=1;
9864
9865                 while ((one << ping_bit_depth) < (size_t) number_colors)
9866                   ping_bit_depth <<= 1;
9867               }
9868
9869             ping_num_trans=0;
9870
9871             if (matte != MagickFalse)
9872               {
9873                 /*
9874                  * Set up trans_colors array.
9875                  */
9876                 assert(number_colors <= 256);
9877
9878                 ping_num_trans=(unsigned short) (number_transparent +
9879                   number_semitransparent);
9880
9881                 if (ping_num_trans == 0)
9882                   ping_have_tRNS=MagickFalse;
9883
9884                 else
9885                   {
9886                     if (logging != MagickFalse)
9887                       {
9888                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9889                           "  Scaling ping_trans_color (1)");
9890                       }
9891                     ping_have_tRNS=MagickTrue;
9892
9893                     for (i=0; i < ping_num_trans; i++)
9894                     {
9895                        ping_trans_alpha[i]= (png_byte)
9896                          ScaleQuantumToChar(image->colormap[i].alpha);
9897                     }
9898                   }
9899               }
9900           }
9901       }
9902
9903     else
9904       {
9905
9906         if (image_depth < 8)
9907           image_depth=8;
9908
9909         if ((save_image_depth == 16) && (image_depth == 8))
9910           {
9911             if (logging != MagickFalse)
9912               {
9913                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9914                   "    Scaling ping_trans_color from (%d,%d,%d)",
9915                   (int) ping_trans_color.red,
9916                   (int) ping_trans_color.green,
9917                   (int) ping_trans_color.blue);
9918               }
9919
9920             ping_trans_color.red*=0x0101;
9921             ping_trans_color.green*=0x0101;
9922             ping_trans_color.blue*=0x0101;
9923             ping_trans_color.gray*=0x0101;
9924
9925             if (logging != MagickFalse)
9926               {
9927                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9928                   "    to (%d,%d,%d)",
9929                   (int) ping_trans_color.red,
9930                   (int) ping_trans_color.green,
9931                   (int) ping_trans_color.blue);
9932               }
9933           }
9934       }
9935
9936     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
9937          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
9938
9939     /*
9940       Adjust background and transparency samples in sub-8-bit grayscale files.
9941     */
9942     if (ping_bit_depth < 8 && ping_color_type ==
9943         PNG_COLOR_TYPE_GRAY)
9944       {
9945          png_uint_16
9946            maxval;
9947
9948          size_t
9949            one=1;
9950
9951          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9952
9953          if (ping_exclude_bKGD == MagickFalse)
9954          {
9955
9956          ping_background.gray=(png_uint_16) ((maxval/65535.)*
9957            (ScaleQuantumToShort(((GetPixelInfoIntensity(
9958            &image->background_color))) +.5)));
9959
9960          if (logging != MagickFalse)
9961            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9962              "  Setting up bKGD chunk (2)");
9963          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9964              "      background_color index is %d",
9965              (int) ping_background.index);
9966
9967          ping_have_bKGD = MagickTrue;
9968          }
9969
9970          if (logging != MagickFalse)
9971            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9972              "  Scaling ping_trans_color.gray from %d",
9973              (int)ping_trans_color.gray);
9974
9975          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9976            ping_trans_color.gray)+.5);
9977
9978          if (logging != MagickFalse)
9979            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9980              "      to %d", (int)ping_trans_color.gray);
9981       }
9982
9983   if (ping_exclude_bKGD == MagickFalse)
9984   {
9985     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9986       {
9987         /*
9988            Identify which colormap entry is the background color.
9989         */
9990
9991         number_colors=image_colors;
9992
9993         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9994           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9995             break;
9996
9997         ping_background.index=(png_byte) i;
9998
9999         if (logging != MagickFalse)
10000           {
10001             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10002               "  Setting up bKGD chunk with index=%d",(int) i);
10003           }
10004
10005         if (i < (ssize_t) number_colors)
10006           {
10007             ping_have_bKGD = MagickTrue;
10008
10009             if (logging != MagickFalse)
10010               {
10011                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10012                   "     background   =(%d,%d,%d)",
10013                         (int) ping_background.red,
10014                         (int) ping_background.green,
10015                         (int) ping_background.blue);
10016               }
10017           }
10018
10019         else  /* Can't happen */
10020           {
10021             if (logging != MagickFalse)
10022               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10023                   "      No room in PLTE to add bKGD color");
10024             ping_have_bKGD = MagickFalse;
10025           }
10026       }
10027   }
10028
10029   if (logging != MagickFalse)
10030     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10031       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10032       ping_color_type);
10033   /*
10034     Initialize compression level and filtering.
10035   */
10036   if (logging != MagickFalse)
10037     {
10038       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10039         "  Setting up deflate compression");
10040
10041       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10042         "    Compression buffer size: 32768");
10043     }
10044
10045   png_set_compression_buffer_size(ping,32768L);
10046
10047   if (logging != MagickFalse)
10048     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10049       "    Compression mem level: 9");
10050
10051   png_set_compression_mem_level(ping, 9);
10052
10053   /* Untangle the "-quality" setting:
10054
10055      Undefined is 0; the default is used.
10056      Default is 75
10057
10058      10's digit:
10059
10060         0: Use Z_HUFFMAN_ONLY strategy with the
10061            zlib default compression level
10062
10063         1-9: the zlib compression level
10064
10065      1's digit:
10066
10067         0-4: the PNG filter method
10068
10069         5:   libpng adaptive filtering if compression level > 5
10070              libpng filter type "none" if compression level <= 5
10071                 or if image is grayscale or palette
10072
10073         6:   libpng adaptive filtering
10074
10075         7:   "LOCO" filtering (intrapixel differing) if writing
10076              a MNG, othewise "none".  Did not work in IM-6.7.0-9
10077              and earlier because of a missing "else".
10078
10079         8:   Z_RLE strategy, all filters
10080              Unused prior to IM-6.7.0-10, was same as 6
10081
10082         9:   Z_RLE strategy, no PNG filters
10083              Unused prior to IM-6.7.0-10, was same as 6
10084
10085     Note that using the -quality option, not all combinations of
10086     PNG filter type, zlib compression level, and zlib compression
10087     strategy are possible.  This will be addressed soon in a
10088     release that accomodates "-define png:compression-strategy", etc.
10089
10090    */
10091
10092   quality=image->quality == UndefinedCompressionQuality ? 75UL :
10093      image->quality;
10094
10095   if (quality <= 9)
10096     {
10097       if (mng_info->write_png_compression_strategy == 0)
10098         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10099     }
10100
10101   else if (mng_info->write_png_compression_level == 0)
10102     {
10103       int
10104         level;
10105
10106       level=(int) MagickMin((ssize_t) quality/10,9);
10107
10108       mng_info->write_png_compression_level = level+1;
10109     }
10110
10111   if (mng_info->write_png_compression_strategy == 0)
10112     {
10113         if ((quality %10) == 8 || (quality %10) == 9)
10114 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10115           mng_info->write_png_compression_strategy=Z_RLE+1;
10116 #else
10117           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10118 #endif
10119     }
10120
10121   if (mng_info->write_png_compression_filter == 0)
10122         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10123
10124   if (logging != MagickFalse)
10125     {
10126      if (mng_info->write_png_compression_level)
10127         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10128           "    Compression level:    %d",
10129             (int) mng_info->write_png_compression_level-1);
10130
10131      if (mng_info->write_png_compression_strategy)
10132         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10133           "    Compression strategy: %d",
10134             (int) mng_info->write_png_compression_strategy-1);
10135
10136         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10137           "  Setting up filtering");
10138
10139         if (mng_info->write_png_compression_filter == 6)
10140           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10141             "    Base filter method: ADAPTIVE");
10142         else if (mng_info->write_png_compression_filter == 0 ||
10143                  mng_info->write_png_compression_filter == 1)
10144           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10145             "    Base filter method: NONE");
10146         else
10147           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10148             "    Base filter method: %d",
10149             (int) mng_info->write_png_compression_filter-1);
10150     }
10151
10152   if (mng_info->write_png_compression_level != 0)
10153     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10154
10155   if (mng_info->write_png_compression_filter == 6)
10156     {
10157       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10158          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10159          (quality < 50))
10160         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10161       else
10162         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10163      }
10164   else if (mng_info->write_png_compression_filter == 7 ||
10165       mng_info->write_png_compression_filter == 10)
10166     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10167
10168   else if (mng_info->write_png_compression_filter == 8)
10169     {
10170 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10171       if (mng_info->write_mng)
10172       {
10173          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10174              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10175         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10176       }
10177 #endif
10178       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10179     }
10180
10181   else if (mng_info->write_png_compression_filter == 9)
10182     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10183
10184   else if (mng_info->write_png_compression_filter != 0)
10185     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10186        mng_info->write_png_compression_filter-1);
10187
10188   if (mng_info->write_png_compression_strategy != 0)
10189     png_set_compression_strategy(ping,
10190        mng_info->write_png_compression_strategy-1);
10191
10192   ping_interlace_method=image_info->interlace != NoInterlace;
10193
10194   if (mng_info->write_mng)
10195     png_set_sig_bytes(ping,8);
10196
10197   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10198
10199   if (mng_info->write_png_colortype != 0)
10200     {
10201      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10202        if (ping_have_color != MagickFalse)
10203          {
10204            ping_color_type = PNG_COLOR_TYPE_RGB;
10205
10206            if (ping_bit_depth < 8)
10207              ping_bit_depth=8;
10208          }
10209
10210      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10211        if (ping_have_color != MagickFalse)
10212          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10213     }
10214
10215   if (ping_need_colortype_warning != MagickFalse ||
10216      ((mng_info->write_png_depth &&
10217      (int) mng_info->write_png_depth != ping_bit_depth) ||
10218      (mng_info->write_png_colortype &&
10219      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10220       mng_info->write_png_colortype != 7 &&
10221       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10222     {
10223       if (logging != MagickFalse)
10224         {
10225           if (ping_need_colortype_warning != MagickFalse)
10226             {
10227               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10228                  "  Image has transparency but tRNS chunk was excluded");
10229             }
10230
10231           if (mng_info->write_png_depth)
10232             {
10233               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10234                   "  Defined png:bit-depth=%u, Computed depth=%u",
10235                   mng_info->write_png_depth,
10236                   ping_bit_depth);
10237             }
10238
10239           if (mng_info->write_png_colortype)
10240             {
10241               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10242                   "  Defined png:color-type=%u, Computed color type=%u",
10243                   mng_info->write_png_colortype-1,
10244                   ping_color_type);
10245             }
10246         }
10247
10248       png_warning(ping,
10249         "Cannot write image with defined png:bit-depth or png:color-type.");
10250     }
10251
10252   if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10253     {
10254       /* Add an opaque matte channel */
10255       image->alpha_trait = BlendPixelTrait;
10256       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10257
10258       if (logging != MagickFalse)
10259         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10260           "  Added an opaque matte channel");
10261     }
10262
10263   if (number_transparent != 0 || number_semitransparent != 0)
10264     {
10265       if (ping_color_type < 4)
10266         {
10267            ping_have_tRNS=MagickTrue;
10268            if (logging != MagickFalse)
10269              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10270                "  Setting ping_have_tRNS=MagickTrue.");
10271         }
10272     }
10273
10274   if (logging != MagickFalse)
10275     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10276       "  Writing PNG header chunks");
10277
10278   png_set_IHDR(ping,ping_info,ping_width,ping_height,
10279                ping_bit_depth,ping_color_type,
10280                ping_interlace_method,ping_compression_method,
10281                ping_filter_method);
10282
10283   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10284     {
10285       png_set_PLTE(ping,ping_info,palette,number_colors);
10286
10287       if (logging != MagickFalse)
10288         {
10289           for (i=0; i< (ssize_t) number_colors; i++)
10290           {
10291             if (i < ping_num_trans)
10292               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10293                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10294                       (int) i,
10295                       (int) palette[i].red,
10296                       (int) palette[i].green,
10297                       (int) palette[i].blue,
10298                       (int) i,
10299                       (int) ping_trans_alpha[i]);
10300              else
10301               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10302                 "     PLTE[%d] = (%d,%d,%d)",
10303                       (int) i,
10304                       (int) palette[i].red,
10305                       (int) palette[i].green,
10306                       (int) palette[i].blue);
10307            }
10308          }
10309     }
10310
10311   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10312   if (ping_exclude_sRGB != MagickFalse ||
10313      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10314   {
10315     if ((ping_exclude_tEXt == MagickFalse ||
10316        ping_exclude_zTXt == MagickFalse) &&
10317        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10318     {
10319       ResetImageProfileIterator(image);
10320       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10321       {
10322         profile=GetImageProfile(image,name);
10323
10324         if (profile != (StringInfo *) NULL)
10325           {
10326 #ifdef PNG_WRITE_iCCP_SUPPORTED
10327             if ((LocaleCompare(name,"ICC") == 0) ||
10328                 (LocaleCompare(name,"ICM") == 0))
10329              {
10330
10331                if (ping_exclude_iCCP == MagickFalse)
10332                  {
10333                        png_set_iCCP(ping,ping_info,(png_charp) name,0,
10334 #if (PNG_LIBPNG_VER < 10500)
10335                          (png_charp) GetStringInfoDatum(profile),
10336 #else
10337                          (png_const_bytep) GetStringInfoDatum(profile),
10338 #endif
10339                          (png_uint_32) GetStringInfoLength(profile));
10340                  }
10341              }
10342
10343             else
10344 #endif
10345               if (ping_exclude_zCCP == MagickFalse)
10346                 {
10347                   Magick_png_write_raw_profile(image_info,ping,ping_info,
10348                     (unsigned char *) name,(unsigned char *) name,
10349                     GetStringInfoDatum(profile),
10350                     (png_uint_32) GetStringInfoLength(profile));
10351                 }
10352           }
10353
10354           if (logging != MagickFalse)
10355             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10356               "  Setting up text chunk with %s profile",name);
10357
10358         name=GetNextImageProfile(image);
10359       }
10360     }
10361   }
10362
10363 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10364   if ((mng_info->have_write_global_srgb == 0) &&
10365       (png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10366     {
10367       if (ping_exclude_sRGB == MagickFalse)
10368         {
10369           /*
10370             Note image rendering intent.
10371           */
10372           if (logging != MagickFalse)
10373             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10374                 "  Setting up sRGB chunk");
10375
10376           (void) png_set_sRGB(ping,ping_info,(
10377             Magick_RenderingIntent_to_PNG_RenderingIntent(
10378               image->rendering_intent)));
10379         }
10380     }
10381
10382   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10383 #endif
10384     {
10385       if (ping_exclude_gAMA == MagickFalse &&
10386           (ping_exclude_sRGB == MagickFalse ||
10387           (image->gamma < .45 || image->gamma > .46)))
10388       {
10389       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10390         {
10391           /*
10392             Note image gamma.
10393             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10394           */
10395           if (logging != MagickFalse)
10396             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10397               "  Setting up gAMA chunk");
10398
10399           png_set_gAMA(ping,ping_info,image->gamma);
10400         }
10401       }
10402
10403       if (ping_exclude_cHRM == MagickFalse)
10404         {
10405           if ((mng_info->have_write_global_chrm == 0) &&
10406               (image->chromaticity.red_primary.x != 0.0))
10407             {
10408               /*
10409                 Note image chromaticity.
10410                 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10411               */
10412                PrimaryInfo
10413                  bp,
10414                  gp,
10415                  rp,
10416                  wp;
10417
10418                wp=image->chromaticity.white_point;
10419                rp=image->chromaticity.red_primary;
10420                gp=image->chromaticity.green_primary;
10421                bp=image->chromaticity.blue_primary;
10422
10423                if (logging != MagickFalse)
10424                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10425                    "  Setting up cHRM chunk");
10426
10427                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10428                    bp.x,bp.y);
10429            }
10430         }
10431     }
10432
10433   if (ping_exclude_bKGD == MagickFalse)
10434     {
10435       if (ping_have_bKGD != MagickFalse)
10436         {
10437           png_set_bKGD(ping,ping_info,&ping_background);
10438           if (logging)
10439             {
10440               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10441                    "    Setting up bKGD chunk");
10442               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10443                    "      background color = (%d,%d,%d)",
10444                         (int) ping_background.red,
10445                         (int) ping_background.green,
10446                         (int) ping_background.blue);
10447               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10448                    "      index = %d, gray=%d",
10449                         (int) ping_background.index,
10450                         (int) ping_background.gray);
10451             }
10452          }
10453     }
10454
10455   if (ping_exclude_pHYs == MagickFalse)
10456     {
10457       if (ping_have_pHYs != MagickFalse)
10458         {
10459           png_set_pHYs(ping,ping_info,
10460              ping_pHYs_x_resolution,
10461              ping_pHYs_y_resolution,
10462              ping_pHYs_unit_type);
10463
10464           if (logging)
10465             {
10466               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10467                    "    Setting up pHYs chunk");
10468               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10469                    "      x_resolution=%lu",
10470                    (unsigned long) ping_pHYs_x_resolution);
10471               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10472                    "      y_resolution=%lu",
10473                    (unsigned long) ping_pHYs_y_resolution);
10474               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10475                    "      unit_type=%lu",
10476                    (unsigned long) ping_pHYs_unit_type);
10477             }
10478         }
10479     }
10480
10481 #if defined(PNG_oFFs_SUPPORTED)
10482   if (ping_exclude_oFFs == MagickFalse)
10483     {
10484       if (image->page.x || image->page.y)
10485         {
10486            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10487               (png_int_32) image->page.y, 0);
10488
10489            if (logging != MagickFalse)
10490              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10491                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
10492                  (int) image->page.x, (int) image->page.y);
10493         }
10494     }
10495 #endif
10496
10497   if (mng_info->need_blob != MagickFalse)
10498   {
10499     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10500        MagickFalse)
10501        png_error(ping,"WriteBlob Failed");
10502
10503      ping_have_blob=MagickTrue;
10504   }
10505
10506   png_write_info_before_PLTE(ping, ping_info);
10507
10508   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10509     {
10510       if (logging != MagickFalse)
10511         {
10512           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10513               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10514         }
10515
10516       if (ping_color_type == 3)
10517          (void) png_set_tRNS(ping, ping_info,
10518                 ping_trans_alpha,
10519                 ping_num_trans,
10520                 NULL);
10521
10522       else
10523         {
10524            (void) png_set_tRNS(ping, ping_info,
10525                   NULL,
10526                   0,
10527                   &ping_trans_color);
10528
10529            if (logging != MagickFalse)
10530              {
10531                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10532                  "     tRNS color   =(%d,%d,%d)",
10533                        (int) ping_trans_color.red,
10534                        (int) ping_trans_color.green,
10535                        (int) ping_trans_color.blue);
10536              }
10537          }
10538     }
10539
10540   /* write any png-chunk-b profiles */
10541   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10542
10543   png_write_info(ping,ping_info);
10544
10545   /* write any PNG-chunk-m profiles */
10546   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10547
10548   if (ping_exclude_vpAg == MagickFalse)
10549     {
10550       if ((image->page.width != 0 && image->page.width != image->columns) ||
10551           (image->page.height != 0 && image->page.height != image->rows))
10552         {
10553           unsigned char
10554             chunk[14];
10555
10556           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
10557           PNGType(chunk,mng_vpAg);
10558           LogPNGChunk(logging,mng_vpAg,9L);
10559           PNGLong(chunk+4,(png_uint_32) image->page.width);
10560           PNGLong(chunk+8,(png_uint_32) image->page.height);
10561           chunk[12]=0;   /* unit = pixels */
10562           (void) WriteBlob(image,13,chunk);
10563           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10564         }
10565     }
10566
10567 #if (PNG_LIBPNG_VER == 10206)
10568     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10569 #define PNG_HAVE_IDAT               0x04
10570     ping->mode |= PNG_HAVE_IDAT;
10571 #undef PNG_HAVE_IDAT
10572 #endif
10573
10574   png_set_packing(ping);
10575   /*
10576     Allocate memory.
10577   */
10578   rowbytes=image->columns;
10579   if (image_depth > 8)
10580     rowbytes*=2;
10581   switch (ping_color_type)
10582     {
10583       case PNG_COLOR_TYPE_RGB:
10584         rowbytes*=3;
10585         break;
10586
10587       case PNG_COLOR_TYPE_GRAY_ALPHA:
10588         rowbytes*=2;
10589         break;
10590
10591       case PNG_COLOR_TYPE_RGBA:
10592         rowbytes*=4;
10593         break;
10594
10595       default:
10596         break;
10597     }
10598
10599   if (logging != MagickFalse)
10600     {
10601       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10602         "  Writing PNG image data");
10603
10604       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10605         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10606     }
10607   ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10608     sizeof(*ping_pixels));
10609
10610   if (ping_pixels == (unsigned char *) NULL)
10611     png_error(ping,"Allocation of memory for pixels failed");
10612
10613   /*
10614     Initialize image scanlines.
10615   */
10616   quantum_info=AcquireQuantumInfo(image_info,image);
10617   if (quantum_info == (QuantumInfo *) NULL)
10618     png_error(ping,"Memory allocation for quantum_info failed");
10619   quantum_info->format=UndefinedQuantumFormat;
10620   quantum_info->depth=image_depth;
10621   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10622   num_passes=png_set_interlace_handling(ping);
10623
10624   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10625        !mng_info->write_png48 && !mng_info->write_png64 &&
10626        !mng_info->write_png32) &&
10627        (mng_info->IsPalette ||
10628        (image_info->type == BilevelType)) &&
10629        image_matte == MagickFalse &&
10630        ping_have_non_bw == MagickFalse)
10631     {
10632       /* Palette, Bilevel, or Opaque Monochrome */
10633       register const Quantum
10634         *p;
10635
10636       quantum_info->depth=8;
10637       for (pass=0; pass < num_passes; pass++)
10638       {
10639         /*
10640           Convert PseudoClass image to a PNG monochrome image.
10641         */
10642         for (y=0; y < (ssize_t) image->rows; y++)
10643         {
10644           if (logging != MagickFalse && y == 0)
10645              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10646                  "    Writing row of pixels (0)");
10647
10648           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10649
10650           if (p == (const Quantum *) NULL)
10651             break;
10652
10653           if (mng_info->IsPalette)
10654             {
10655               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10656                 quantum_info,GrayQuantum,ping_pixels,exception);
10657               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10658                   mng_info->write_png_depth &&
10659                   mng_info->write_png_depth != old_bit_depth)
10660                 {
10661                   /* Undo pixel scaling */
10662                   for (i=0; i < (ssize_t) image->columns; i++)
10663                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10664                      >> (8-old_bit_depth));
10665                 }
10666             }
10667
10668           else
10669             {
10670               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10671                 quantum_info,RedQuantum,ping_pixels,exception);
10672             }
10673
10674           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10675             for (i=0; i < (ssize_t) image->columns; i++)
10676                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10677                       255 : 0);
10678
10679           if (logging != MagickFalse && y == 0)
10680             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10681                 "    Writing row of pixels (1)");
10682
10683           png_write_row(ping,ping_pixels);
10684         }
10685         if (image->previous == (Image *) NULL)
10686           {
10687             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10688             if (status == MagickFalse)
10689               break;
10690           }
10691       }
10692     }
10693
10694   else   /* Not Palette, Bilevel, or Opaque Monochrome */
10695     {
10696       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10697           !mng_info->write_png48 && !mng_info->write_png64 &&
10698           !mng_info->write_png32) && (image_matte != MagickFalse ||
10699           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10700           (mng_info->IsPalette) && ping_have_color == MagickFalse)
10701         {
10702           register const Quantum
10703             *p;
10704
10705           for (pass=0; pass < num_passes; pass++)
10706           {
10707
10708           for (y=0; y < (ssize_t) image->rows; y++)
10709           {
10710             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10711
10712             if (p == (const Quantum *) NULL)
10713               break;
10714
10715             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10716               {
10717                 if (mng_info->IsPalette)
10718                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10719                     quantum_info,GrayQuantum,ping_pixels,exception);
10720
10721                 else
10722                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10723                     quantum_info,RedQuantum,ping_pixels,exception);
10724
10725                 if (logging != MagickFalse && y == 0)
10726                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10727                        "    Writing GRAY PNG pixels (2)");
10728               }
10729
10730             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10731               {
10732                 if (logging != MagickFalse && y == 0)
10733                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10734                          "    Writing GRAY_ALPHA PNG pixels (2)");
10735
10736                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10737                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10738               }
10739
10740             if (logging != MagickFalse && y == 0)
10741               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10742                   "    Writing row of pixels (2)");
10743
10744             png_write_row(ping,ping_pixels);
10745           }
10746
10747           if (image->previous == (Image *) NULL)
10748             {
10749               status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10750               if (status == MagickFalse)
10751                 break;
10752             }
10753           }
10754         }
10755
10756       else
10757         {
10758           register const Quantum
10759             *p;
10760
10761           for (pass=0; pass < num_passes; pass++)
10762           {
10763             if ((image_depth > 8) ||
10764                 mng_info->write_png24 ||
10765                 mng_info->write_png32 ||
10766                 mng_info->write_png48 ||
10767                 mng_info->write_png64 ||
10768                 (!mng_info->write_png8 && !mng_info->IsPalette))
10769             {
10770               for (y=0; y < (ssize_t) image->rows; y++)
10771               {
10772                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10773
10774                 if (p == (const Quantum *) NULL)
10775                   break;
10776
10777                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10778                   {
10779                     if (image->storage_class == DirectClass)
10780                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
10781                         quantum_info,RedQuantum,ping_pixels,exception);
10782
10783                     else
10784                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
10785                         quantum_info,GrayQuantum,ping_pixels,exception);
10786                   }
10787
10788                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10789                   {
10790                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
10791                       quantum_info,GrayAlphaQuantum,ping_pixels,
10792                       exception);
10793
10794                     if (logging != MagickFalse && y == 0)
10795                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10796                            "    Writing GRAY_ALPHA PNG pixels (3)");
10797                   }
10798
10799                 else if (image_matte != MagickFalse)
10800                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10801                     quantum_info,RGBAQuantum,ping_pixels,exception);
10802
10803                 else
10804                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10805                     quantum_info,RGBQuantum,ping_pixels,exception);
10806
10807                 if (logging != MagickFalse && y == 0)
10808                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10809                       "    Writing row of pixels (3)");
10810
10811                 png_write_row(ping,ping_pixels);
10812               }
10813             }
10814
10815           else
10816             /* not ((image_depth > 8) ||
10817                 mng_info->write_png24 || mng_info->write_png32 ||
10818                 mng_info->write_png48 || mng_info->write_png64 ||
10819                 (!mng_info->write_png8 && !mng_info->IsPalette))
10820              */
10821             {
10822               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10823                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10824                 {
10825                   if (logging != MagickFalse)
10826                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10827                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10828
10829                   quantum_info->depth=8;
10830                   image_depth=8;
10831                 }
10832
10833               for (y=0; y < (ssize_t) image->rows; y++)
10834               {
10835                 if (logging != MagickFalse && y == 0)
10836                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10837                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10838
10839                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10840
10841                 if (p == (const Quantum *) NULL)
10842                   break;
10843
10844                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10845                   {
10846                     quantum_info->depth=image->depth;
10847
10848                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
10849                        quantum_info,GrayQuantum,ping_pixels,exception);
10850                   }
10851
10852                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10853                   {
10854                     if (logging != MagickFalse && y == 0)
10855                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10856                            "  Writing GRAY_ALPHA PNG pixels (4)");
10857
10858                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
10859                          quantum_info,GrayAlphaQuantum,ping_pixels,
10860                          exception);
10861                   }
10862
10863                 else
10864                   {
10865                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
10866                       quantum_info,IndexQuantum,ping_pixels,exception);
10867
10868                     if (logging != MagickFalse && y <= 2)
10869                     {
10870                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10871                           "  Writing row of non-gray pixels (4)");
10872
10873                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10874                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
10875                           (int)ping_pixels[0],(int)ping_pixels[1]);
10876                     }
10877                   }
10878                 png_write_row(ping,ping_pixels);
10879               }
10880             }
10881
10882             if (image->previous == (Image *) NULL)
10883               {
10884                 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10885                 if (status == MagickFalse)
10886                   break;
10887               }
10888           }
10889         }
10890     }
10891
10892   if (quantum_info != (QuantumInfo *) NULL)
10893     quantum_info=DestroyQuantumInfo(quantum_info);
10894
10895   if (logging != MagickFalse)
10896     {
10897       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10898         "  Wrote PNG image data");
10899
10900       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10901         "    Width: %.20g",(double) ping_width);
10902
10903       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10904         "    Height: %.20g",(double) ping_height);
10905
10906       if (mng_info->write_png_depth)
10907         {
10908           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10909             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
10910         }
10911
10912       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10913         "    PNG bit-depth written: %d",ping_bit_depth);
10914
10915       if (mng_info->write_png_colortype)
10916         {
10917           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10918             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
10919         }
10920
10921       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10922         "    PNG color-type written: %d",ping_color_type);
10923
10924       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10925         "    PNG Interlace method: %d",ping_interlace_method);
10926     }
10927   /*
10928     Generate text chunks after IDAT.
10929   */
10930   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10931   {
10932     ResetImagePropertyIterator(image);
10933     property=GetNextImageProperty(image);
10934     while (property != (const char *) NULL)
10935     {
10936       png_textp
10937         text;
10938
10939       value=GetImageProperty(image,property,exception);
10940
10941       /* Don't write any "png:" properties; those are just for "identify" */
10942       if (LocaleNCompare(property,"png:",4) != 0 &&
10943
10944           /* Suppress density and units if we wrote a pHYs chunk */
10945           (ping_exclude_pHYs != MagickFalse      ||
10946           LocaleCompare(property,"density") != 0 ||
10947           LocaleCompare(property,"units") != 0) &&
10948
10949           /* Suppress the IM-generated Date:create and Date:modify */
10950           (ping_exclude_date == MagickFalse      ||
10951           LocaleNCompare(property, "Date:",5) != 0))
10952         {
10953         if (value != (const char *) NULL)
10954           {
10955
10956 #if PNG_LIBPNG_VER >= 14000
10957             text=(png_textp) png_malloc(ping,
10958                  (png_alloc_size_t) sizeof(png_text));
10959 #else
10960             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
10961 #endif
10962             text[0].key=(char *) property;
10963             text[0].text=(char *) value;
10964             text[0].text_length=strlen(value);
10965
10966             if (ping_exclude_tEXt != MagickFalse)
10967                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10968
10969             else if (ping_exclude_zTXt != MagickFalse)
10970                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10971
10972             else
10973             {
10974                text[0].compression=image_info->compression == NoCompression ||
10975                  (image_info->compression == UndefinedCompression &&
10976                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10977                  PNG_TEXT_COMPRESSION_zTXt ;
10978             }
10979
10980             if (logging != MagickFalse)
10981               {
10982                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10983                   "  Setting up text chunk");
10984
10985                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10986                   "    keyword: '%s'",text[0].key);
10987               }
10988
10989             png_set_text(ping,ping_info,text,1);
10990             png_free(ping,text);
10991           }
10992         }
10993       property=GetNextImageProperty(image);
10994     }
10995   }
10996
10997   /* write any PNG-chunk-e profiles */
10998   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10999
11000   if (logging != MagickFalse)
11001     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11002       "  Writing PNG end info");
11003
11004   png_write_end(ping,ping_info);
11005
11006   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11007     {
11008       if (mng_info->page.x || mng_info->page.y ||
11009           (ping_width != mng_info->page.width) ||
11010           (ping_height != mng_info->page.height))
11011         {
11012           unsigned char
11013             chunk[32];
11014
11015           /*
11016             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11017           */
11018           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11019           PNGType(chunk,mng_FRAM);
11020           LogPNGChunk(logging,mng_FRAM,27L);
11021           chunk[4]=4;
11022           chunk[5]=0;  /* frame name separator (no name) */
11023           chunk[6]=1;  /* flag for changing delay, for next frame only */
11024           chunk[7]=0;  /* flag for changing frame timeout */
11025           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11026           chunk[9]=0;  /* flag for changing frame sync_id */
11027           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11028           chunk[14]=0; /* clipping boundaries delta type */
11029           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11030           PNGLong(chunk+19,
11031              (png_uint_32) (mng_info->page.x + ping_width));
11032           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11033           PNGLong(chunk+27,
11034              (png_uint_32) (mng_info->page.y + ping_height));
11035           (void) WriteBlob(image,31,chunk);
11036           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11037           mng_info->old_framing_mode=4;
11038           mng_info->framing_mode=1;
11039         }
11040
11041       else
11042         mng_info->framing_mode=3;
11043     }
11044   if (mng_info->write_mng && !mng_info->need_fram &&
11045       ((int) image->dispose == 3))
11046      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11047
11048   /*
11049     Free PNG resources.
11050   */
11051
11052   png_destroy_write_struct(&ping,&ping_info);
11053
11054   ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
11055
11056   if (ping_have_blob != MagickFalse)
11057      (void) CloseBlob(image);
11058
11059   image_info=DestroyImageInfo(image_info);
11060   image=DestroyImage(image);
11061
11062   /* Store bit depth actually written */
11063   s[0]=(char) ping_bit_depth;
11064   s[1]='\0';
11065
11066   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11067
11068   if (logging != MagickFalse)
11069     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11070       "  exit WriteOnePNGImage()");
11071
11072 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
11073   UnlockSemaphoreInfo(ping_semaphore);
11074 #endif
11075
11076    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11077     *    Throwing an Exception when an error occurs.
11078     */
11079
11080   return(MagickTrue);
11081 /*  End write one PNG image */
11082
11083 }
11084
11085 /*
11086 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11087 %                                                                             %
11088 %                                                                             %
11089 %                                                                             %
11090 %   W r i t e P N G I m a g e                                                 %
11091 %                                                                             %
11092 %                                                                             %
11093 %                                                                             %
11094 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11095 %
11096 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11097 %  Multiple-image Network Graphics (MNG) image file.
11098 %
11099 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11100 %
11101 %  The format of the WritePNGImage method is:
11102 %
11103 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11104 %        Image *image,ExceptionInfo *exception)
11105 %
11106 %  A description of each parameter follows:
11107 %
11108 %    o image_info: the image info.
11109 %
11110 %    o image:  The image.
11111 %
11112 %    o exception: return any errors or warnings in this structure.
11113 %
11114 %  Returns MagickTrue on success, MagickFalse on failure.
11115 %
11116 %  Communicating with the PNG encoder:
11117 %
11118 %  While the datastream written is always in PNG format and normally would
11119 %  be given the "png" file extension, this method also writes the following
11120 %  pseudo-formats which are subsets of png:
11121 %
11122 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11123 %               a depth greater than 8, the depth is reduced. If transparency
11124 %               is present, the tRNS chunk must only have values 0 and 255
11125 %               (i.e., transparency is binary: fully opaque or fully
11126 %               transparent).  If other values are present they will be
11127 %               50%-thresholded to binary transparency.  If more than 256
11128 %               colors are present, they will be quantized to the 4-4-4-1,
11129 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11130 %               of any resulting fully-transparent pixels is changed to
11131 %               the image's background color.
11132 %
11133 %               If you want better quantization or dithering of the colors
11134 %               or alpha than that, you need to do it before calling the
11135 %               PNG encoder. The pixels contain 8-bit indices even if
11136 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11137 %               images will be written as indexed PNG files even though the
11138 %               PNG grayscale type might be slightly more efficient.  Please
11139 %               note that writing to the PNG8 format may result in loss
11140 %               of color and alpha data.
11141 %
11142 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11143 %               chunk can be present to convey binary transparency by naming
11144 %               one of the colors as transparent.  The only loss incurred
11145 %               is reduction of sample depth to 8.  If the image has more
11146 %               than one transparent color, has semitransparent pixels, or
11147 %               has an opaque pixel with the same RGB components as the
11148 %               transparent color, an image is not written.
11149 %
11150 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
11151 %               transparency is permitted, i.e., the alpha sample for
11152 %               each pixel can have any value from 0 to 255. The alpha
11153 %               channel is present even if the image is fully opaque.
11154 %               The only loss in data is the reduction of the sample depth
11155 %               to 8.
11156 %
11157 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
11158 %               chunk can be present to convey binary transparency by naming
11159 %               one of the colors as transparent.  If the image has more
11160 %               than one transparent color, has semitransparent pixels, or
11161 %               has an opaque pixel with the same RGB components as the
11162 %               transparent color, an image is not written.
11163 %
11164 %    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
11165 %               transparency is permitted, i.e., the alpha sample for
11166 %               each pixel can have any value from 0 to 65535. The alpha
11167 %               channel is present even if the image is fully opaque.
11168 %
11169 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
11170 %               image, if the input was a PNG, is written.  If these values
11171 %               cannot be found, then "PNG00" falls back to the regular "PNG"
11172 %               format.
11173 %
11174 %    o -define: For more precise control of the PNG output, you can use the
11175 %               Image options "png:bit-depth" and "png:color-type".  These
11176 %               can be set from the commandline with "-define" and also
11177 %               from the application programming interfaces.  The options
11178 %               are case-independent and are converted to lowercase before
11179 %               being passed to this encoder.
11180 %
11181 %               png:color-type can be 0, 2, 3, 4, or 6.
11182 %
11183 %               When png:color-type is 0 (Grayscale), png:bit-depth can
11184 %               be 1, 2, 4, 8, or 16.
11185 %
11186 %               When png:color-type is 2 (RGB), png:bit-depth can
11187 %               be 8 or 16.
11188 %
11189 %               When png:color-type is 3 (Indexed), png:bit-depth can
11190 %               be 1, 2, 4, or 8.  This refers to the number of bits
11191 %               used to store the index.  The color samples always have
11192 %               bit-depth 8 in indexed PNG files.
11193 %
11194 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11195 %               png:bit-depth can be 8 or 16.
11196 %
11197 %               If the image cannot be written without loss with the
11198 %               requested bit-depth and color-type, a PNG file will not
11199 %               be written, a warning will be issued, and the encoder will
11200 %               return MagickFalse.
11201 %
11202 %  Since image encoders should not be responsible for the "heavy lifting",
11203 %  the user should make sure that ImageMagick has already reduced the
11204 %  image depth and number of colors and limit transparency to binary
11205 %  transparency prior to attempting to write the image with depth, color,
11206 %  or transparency limitations.
11207 %
11208 %  Note that another definition, "png:bit-depth-written" exists, but it
11209 %  is not intended for external use.  It is only used internally by the
11210 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11211 %
11212 %  It is possible to request that the PNG encoder write previously-formatted
11213 %  ancillary chunks in the output PNG file, using the "-profile" commandline
11214 %  option as shown below or by setting the profile via a programming
11215 %  interface:
11216 %
11217 %     -profile PNG-chunk-x:<file>
11218 %
11219 %  where x is a location flag and <file> is a file containing the chunk
11220 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11221 %  This encoder will compute the chunk length and CRC, so those must not
11222 %  be included in the file.
11223 %
11224 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11225 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
11226 %  of the same type, then add a short unique string after the "x" to prevent
11227 %  subsequent profiles from overwriting the preceding ones, e.g.,
11228 %
11229 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11230 %
11231 %  As of version 6.6.6 the following optimizations are always done:
11232 %
11233 %   o  32-bit depth is reduced to 16.
11234 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
11235 %      high byte and low byte are identical.
11236 %   o  Palette is sorted to remove unused entries and to put a
11237 %      transparent color first, if BUILD_PNG_PALETTE is defined.
11238 %   o  Opaque matte channel is removed when writing an indexed PNG.
11239 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
11240 %      this can be done without loss and a larger bit depth N was not
11241 %      requested via the "-define png:bit-depth=N" option.
11242 %   o  If matte channel is present but only one transparent color is
11243 %      present, RGB+tRNS is written instead of RGBA
11244 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
11245 %      was requested when converting an opaque image).
11246 %
11247 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11248 */
11249 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11250   Image *image,ExceptionInfo *exception)
11251 {
11252   MagickBooleanType
11253     excluding,
11254     logging,
11255     have_mng_structure,
11256     status;
11257
11258   MngInfo
11259     *mng_info;
11260
11261   const char
11262     *value;
11263
11264   int
11265     i,
11266     source;
11267
11268   /*
11269     Open image file.
11270   */
11271   assert(image_info != (const ImageInfo *) NULL);
11272   assert(image_info->signature == MagickSignature);
11273   assert(image != (Image *) NULL);
11274   assert(image->signature == MagickSignature);
11275   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11276   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11277   /*
11278     Allocate a MngInfo structure.
11279   */
11280   have_mng_structure=MagickFalse;
11281   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11282
11283   if (mng_info == (MngInfo *) NULL)
11284     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11285
11286   /*
11287     Initialize members of the MngInfo structure.
11288   */
11289   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11290   mng_info->image=image;
11291   mng_info->equal_backgrounds=MagickTrue;
11292   have_mng_structure=MagickTrue;
11293
11294   /* See if user has requested a specific PNG subformat */
11295
11296   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11297   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11298   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11299   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11300   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11301
11302   value=GetImageOption(image_info,"png:format");
11303
11304   if (value != (char *) NULL)
11305     {
11306       mng_info->write_png8 = MagickFalse;
11307       mng_info->write_png24 = MagickFalse;
11308       mng_info->write_png32 = MagickFalse;
11309       mng_info->write_png48 = MagickFalse;
11310       mng_info->write_png64 = MagickFalse;
11311
11312       if (LocaleCompare(value,"png8") == 0)
11313         mng_info->write_png8 = MagickTrue;
11314
11315       else if (LocaleCompare(value,"png24") == 0)
11316         mng_info->write_png24 = MagickTrue;
11317
11318       else if (LocaleCompare(value,"png32") == 0)
11319         mng_info->write_png32 = MagickTrue;
11320
11321       else if (LocaleCompare(value,"png48") == 0)
11322         mng_info->write_png48 = MagickTrue;
11323
11324       else if (LocaleCompare(value,"png64") == 0)
11325         mng_info->write_png64 = MagickTrue;
11326     }
11327
11328   if (LocaleCompare(value,"png00") == 0)
11329     {
11330       /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig
11331          Note that whitespace at the end of the property names must match
11332          that in the corresponding SetImageProperty() calls.
11333        */
11334       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11335          "  Format=%s",value);
11336
11337       value=GetImageProperty(image,"png:IHDR.bit-depth-orig  ",exception);
11338
11339       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11340          "  png00 inherited bit depth=%s",value);
11341
11342       if (value != (char *) NULL)
11343         {
11344           if (LocaleCompare(value,"1") == 0)
11345             mng_info->write_png_depth = 1;
11346
11347           else if (LocaleCompare(value,"1") == 0)
11348             mng_info->write_png_depth = 2;
11349
11350           else if (LocaleCompare(value,"2") == 0)
11351             mng_info->write_png_depth = 4;
11352
11353           else if (LocaleCompare(value,"8") == 0)
11354             mng_info->write_png_depth = 8;
11355
11356           else if (LocaleCompare(value,"16") == 0)
11357             mng_info->write_png_depth = 16;
11358         }
11359
11360       value=GetImageProperty(image,"png:IHDR.color-type-orig ",exception);
11361
11362       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11363          "  png00 inherited color type=%s",value);
11364
11365       if (value != (char *) NULL)
11366         {
11367           if (LocaleCompare(value,"0") == 0)
11368             mng_info->write_png_colortype = 1;
11369
11370           else if (LocaleCompare(value,"2") == 0)
11371             mng_info->write_png_colortype = 3;
11372
11373           else if (LocaleCompare(value,"3") == 0)
11374             mng_info->write_png_colortype = 4;
11375
11376           else if (LocaleCompare(value,"4") == 0)
11377             mng_info->write_png_colortype = 5;
11378
11379           else if (LocaleCompare(value,"6") == 0)
11380             mng_info->write_png_colortype = 7;
11381         }
11382     }
11383
11384   if (mng_info->write_png8)
11385     {
11386       mng_info->write_png_colortype = /* 3 */ 4;
11387       mng_info->write_png_depth = 8;
11388       image->depth = 8;
11389     }
11390
11391   if (mng_info->write_png24)
11392     {
11393       mng_info->write_png_colortype = /* 2 */ 3;
11394       mng_info->write_png_depth = 8;
11395       image->depth = 8;
11396
11397       if (image->alpha_trait == BlendPixelTrait)
11398         (void) SetImageType(image,TrueColorMatteType,exception);
11399
11400       else
11401         (void) SetImageType(image,TrueColorType,exception);
11402
11403       (void) SyncImage(image,exception);
11404     }
11405
11406   if (mng_info->write_png32)
11407     {
11408       mng_info->write_png_colortype = /* 6 */  7;
11409       mng_info->write_png_depth = 8;
11410       image->depth = 8;
11411
11412       if (image->alpha_trait == BlendPixelTrait)
11413         (void) SetImageType(image,TrueColorMatteType,exception);
11414
11415       else
11416         (void) SetImageType(image,TrueColorType,exception);
11417
11418       (void) SyncImage(image,exception);
11419     }
11420
11421   if (mng_info->write_png48)
11422     {
11423       mng_info->write_png_colortype = /* 2 */ 3;
11424       mng_info->write_png_depth = 16;
11425       image->depth = 16;
11426
11427       if (image->alpha_trait == BlendPixelTrait)
11428         (void) SetImageType(image,TrueColorMatteType,exception);
11429
11430       else
11431         (void) SetImageType(image,TrueColorType,exception);
11432
11433       (void) SyncImage(image,exception);
11434     }
11435
11436   if (mng_info->write_png64)
11437     {
11438       mng_info->write_png_colortype = /* 6 */  7;
11439       mng_info->write_png_depth = 16;
11440       image->depth = 16;
11441
11442       if (image->alpha_trait == BlendPixelTrait)
11443         (void) SetImageType(image,TrueColorMatteType,exception);
11444
11445       else
11446         (void) SetImageType(image,TrueColorType,exception);
11447
11448       (void) SyncImage(image,exception);
11449     }
11450
11451   value=GetImageOption(image_info,"png:bit-depth");
11452
11453   if (value != (char *) NULL)
11454     {
11455       if (LocaleCompare(value,"1") == 0)
11456         mng_info->write_png_depth = 1;
11457
11458       else if (LocaleCompare(value,"2") == 0)
11459         mng_info->write_png_depth = 2;
11460
11461       else if (LocaleCompare(value,"4") == 0)
11462         mng_info->write_png_depth = 4;
11463
11464       else if (LocaleCompare(value,"8") == 0)
11465         mng_info->write_png_depth = 8;
11466
11467       else if (LocaleCompare(value,"16") == 0)
11468         mng_info->write_png_depth = 16;
11469
11470       else
11471         (void) ThrowMagickException(exception,
11472              GetMagickModule(),CoderWarning,
11473              "ignoring invalid defined png:bit-depth",
11474              "=%s",value);
11475
11476       if (logging != MagickFalse)
11477         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11478           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11479     }
11480
11481   value=GetImageOption(image_info,"png:color-type");
11482
11483   if (value != (char *) NULL)
11484     {
11485       /* We must store colortype+1 because 0 is a valid colortype */
11486       if (LocaleCompare(value,"0") == 0)
11487         mng_info->write_png_colortype = 1;
11488
11489       else if (LocaleCompare(value,"1") == 0)
11490         mng_info->write_png_colortype = 2;
11491
11492       else if (LocaleCompare(value,"2") == 0)
11493         mng_info->write_png_colortype = 3;
11494
11495       else if (LocaleCompare(value,"3") == 0)
11496         mng_info->write_png_colortype = 4;
11497
11498       else if (LocaleCompare(value,"4") == 0)
11499         mng_info->write_png_colortype = 5;
11500
11501       else if (LocaleCompare(value,"6") == 0)
11502         mng_info->write_png_colortype = 7;
11503
11504       else
11505         (void) ThrowMagickException(exception,
11506              GetMagickModule(),CoderWarning,
11507              "ignoring invalid defined png:color-type",
11508              "=%s",value);
11509
11510       if (logging != MagickFalse)
11511         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11512           "  png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11513     }
11514
11515   /* Check for chunks to be excluded:
11516    *
11517    * The default is to not exclude any known chunks except for any
11518    * listed in the "unused_chunks" array, above.
11519    *
11520    * Chunks can be listed for exclusion via a "png:exclude-chunk"
11521    * define (in the image properties or in the image artifacts)
11522    * or via a mng_info member.  For convenience, in addition
11523    * to or instead of a comma-separated list of chunks, the
11524    * "exclude-chunk" string can be simply "all" or "none".
11525    *
11526    * The exclude-chunk define takes priority over the mng_info.
11527    *
11528    * A "png:include-chunk" define takes  priority over both the
11529    * mng_info and the "png:exclude-chunk" define.  Like the
11530    * "exclude-chunk" string, it can define "all" or "none" as
11531    * well as a comma-separated list.  Chunks that are unknown to
11532    * ImageMagick are always excluded, regardless of their "copy-safe"
11533    * status according to the PNG specification, and even if they
11534    * appear in the "include-chunk" list. Such defines appearing among
11535    * the image options take priority over those found among the image
11536    * artifacts.
11537    *
11538    * Finally, all chunks listed in the "unused_chunks" array are
11539    * automatically excluded, regardless of the other instructions
11540    * or lack thereof.
11541    *
11542    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11543    * will not be written and the gAMA chunk will only be written if it
11544    * is not between .45 and .46, or approximately (1.0/2.2).
11545    *
11546    * If you exclude tRNS and the image has transparency, the colortype
11547    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11548    *
11549    * The -strip option causes StripImage() to set the png:include-chunk
11550    * artifact to "none,trns,gama".
11551    */
11552
11553   mng_info->ping_exclude_bKGD=MagickFalse;
11554   mng_info->ping_exclude_cHRM=MagickFalse;
11555   mng_info->ping_exclude_date=MagickFalse;
11556   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11557   mng_info->ping_exclude_gAMA=MagickFalse;
11558   mng_info->ping_exclude_iCCP=MagickFalse;
11559   /* mng_info->ping_exclude_iTXt=MagickFalse; */
11560   mng_info->ping_exclude_oFFs=MagickFalse;
11561   mng_info->ping_exclude_pHYs=MagickFalse;
11562   mng_info->ping_exclude_sRGB=MagickFalse;
11563   mng_info->ping_exclude_tEXt=MagickFalse;
11564   mng_info->ping_exclude_tRNS=MagickFalse;
11565   mng_info->ping_exclude_vpAg=MagickFalse;
11566   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11567   mng_info->ping_exclude_zTXt=MagickFalse;
11568
11569   mng_info->ping_preserve_colormap=MagickFalse;
11570
11571   value=GetImageArtifact(image,"png:preserve-colormap");
11572   if (value == NULL)
11573      value=GetImageOption(image_info,"png:preserve-colormap");
11574   if (value != NULL)
11575      mng_info->ping_preserve_colormap=MagickTrue;
11576
11577   /* Thes compression-level, compression-strategy, and compression-filter
11578    * defines take precedence over values from the -quality option.
11579    */
11580   value=GetImageArtifact(image,"png:compression-level");
11581   if (value == NULL)
11582      value=GetImageOption(image_info,"png:compression-level");
11583   if (value != NULL)
11584   {
11585       /* We have to add 1 to everything because 0 is a valid input,
11586        * and we want to use 0 (the default) to mean undefined.
11587        */
11588       if (LocaleCompare(value,"0") == 0)
11589         mng_info->write_png_compression_level = 1;
11590
11591       else if (LocaleCompare(value,"1") == 0)
11592         mng_info->write_png_compression_level = 2;
11593
11594       else if (LocaleCompare(value,"2") == 0)
11595         mng_info->write_png_compression_level = 3;
11596
11597       else if (LocaleCompare(value,"3") == 0)
11598         mng_info->write_png_compression_level = 4;
11599
11600       else if (LocaleCompare(value,"4") == 0)
11601         mng_info->write_png_compression_level = 5;
11602
11603       else if (LocaleCompare(value,"5") == 0)
11604         mng_info->write_png_compression_level = 6;
11605
11606       else if (LocaleCompare(value,"6") == 0)
11607         mng_info->write_png_compression_level = 7;
11608
11609       else if (LocaleCompare(value,"7") == 0)
11610         mng_info->write_png_compression_level = 8;
11611
11612       else if (LocaleCompare(value,"8") == 0)
11613         mng_info->write_png_compression_level = 9;
11614
11615       else if (LocaleCompare(value,"9") == 0)
11616         mng_info->write_png_compression_level = 10;
11617
11618       else
11619         (void) ThrowMagickException(exception,
11620              GetMagickModule(),CoderWarning,
11621              "ignoring invalid defined png:compression-level",
11622              "=%s",value);
11623     }
11624
11625   value=GetImageArtifact(image,"png:compression-strategy");
11626   if (value == NULL)
11627      value=GetImageOption(image_info,"png:compression-strategy");
11628   if (value != NULL)
11629   {
11630
11631       if (LocaleCompare(value,"0") == 0)
11632         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11633
11634       else if (LocaleCompare(value,"1") == 0)
11635         mng_info->write_png_compression_strategy = Z_FILTERED+1;
11636
11637       else if (LocaleCompare(value,"2") == 0)
11638         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11639
11640       else if (LocaleCompare(value,"3") == 0)
11641 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
11642         mng_info->write_png_compression_strategy = Z_RLE+1;
11643 #else
11644         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11645 #endif
11646
11647       else if (LocaleCompare(value,"4") == 0)
11648 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
11649         mng_info->write_png_compression_strategy = Z_FIXED+1;
11650 #else
11651         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11652 #endif
11653
11654       else
11655         (void) ThrowMagickException(exception,
11656              GetMagickModule(),CoderWarning,
11657              "ignoring invalid defined png:compression-strategy",
11658              "=%s",value);
11659     }
11660
11661   value=GetImageArtifact(image,"png:compression-filter");
11662   if (value == NULL)
11663      value=GetImageOption(image_info,"png:compression-filter");
11664   if (value != NULL)
11665   {
11666
11667       /* To do: combinations of filters allowed by libpng
11668        * masks 0x08 through 0xf8
11669        *
11670        * Implement this as a comma-separated list of 0,1,2,3,4,5
11671        * where 5 is a special case meaning PNG_ALL_FILTERS.
11672        */
11673
11674       if (LocaleCompare(value,"0") == 0)
11675         mng_info->write_png_compression_filter = 1;
11676
11677       else if (LocaleCompare(value,"1") == 0)
11678         mng_info->write_png_compression_filter = 2;
11679
11680       else if (LocaleCompare(value,"2") == 0)
11681         mng_info->write_png_compression_filter = 3;
11682
11683       else if (LocaleCompare(value,"3") == 0)
11684         mng_info->write_png_compression_filter = 4;
11685
11686       else if (LocaleCompare(value,"4") == 0)
11687         mng_info->write_png_compression_filter = 5;
11688
11689       else if (LocaleCompare(value,"5") == 0)
11690         mng_info->write_png_compression_filter = 6;
11691
11692       else
11693         (void) ThrowMagickException(exception,
11694              GetMagickModule(),CoderWarning,
11695              "ignoring invalid defined png:compression-filter",
11696              "=%s",value);
11697     }
11698
11699   excluding=MagickFalse;
11700
11701   for (source=0; source<1; source++)
11702   {
11703     if (source==0)
11704       {
11705        value=GetImageArtifact(image,"png:exclude-chunk");
11706
11707        if (value == NULL)
11708          value=GetImageArtifact(image,"png:exclude-chunks");
11709       }
11710     else
11711       {
11712        value=GetImageOption(image_info,"png:exclude-chunk");
11713
11714        if (value == NULL)
11715          value=GetImageOption(image_info,"png:exclude-chunks");
11716       }
11717
11718     if (value != NULL)
11719     {
11720
11721     size_t
11722       last;
11723
11724     excluding=MagickTrue;
11725
11726     if (logging != MagickFalse)
11727       {
11728         if (source == 0)
11729            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11730               "  png:exclude-chunk=%s found in image artifacts.\n", value);
11731         else
11732            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11733               "  png:exclude-chunk=%s found in image properties.\n", value);
11734       }
11735
11736     last=strlen(value);
11737
11738     for (i=0; i<(int) last; i+=5)
11739     {
11740
11741       if (LocaleNCompare(value+i,"all",3) == 0)
11742       {
11743         mng_info->ping_exclude_bKGD=MagickTrue;
11744         mng_info->ping_exclude_cHRM=MagickTrue;
11745         mng_info->ping_exclude_date=MagickTrue;
11746         mng_info->ping_exclude_EXIF=MagickTrue;
11747         mng_info->ping_exclude_gAMA=MagickTrue;
11748         mng_info->ping_exclude_iCCP=MagickTrue;
11749         /* mng_info->ping_exclude_iTXt=MagickTrue; */
11750         mng_info->ping_exclude_oFFs=MagickTrue;
11751         mng_info->ping_exclude_pHYs=MagickTrue;
11752         mng_info->ping_exclude_sRGB=MagickTrue;
11753         mng_info->ping_exclude_tEXt=MagickTrue;
11754         mng_info->ping_exclude_tRNS=MagickTrue;
11755         mng_info->ping_exclude_vpAg=MagickTrue;
11756         mng_info->ping_exclude_zCCP=MagickTrue;
11757         mng_info->ping_exclude_zTXt=MagickTrue;
11758         i--;
11759       }
11760
11761       if (LocaleNCompare(value+i,"none",4) == 0)
11762       {
11763         mng_info->ping_exclude_bKGD=MagickFalse;
11764         mng_info->ping_exclude_cHRM=MagickFalse;
11765         mng_info->ping_exclude_date=MagickFalse;
11766         mng_info->ping_exclude_EXIF=MagickFalse;
11767         mng_info->ping_exclude_gAMA=MagickFalse;
11768         mng_info->ping_exclude_iCCP=MagickFalse;
11769         /* mng_info->ping_exclude_iTXt=MagickFalse; */
11770         mng_info->ping_exclude_oFFs=MagickFalse;
11771         mng_info->ping_exclude_pHYs=MagickFalse;
11772         mng_info->ping_exclude_sRGB=MagickFalse;
11773         mng_info->ping_exclude_tEXt=MagickFalse;
11774         mng_info->ping_exclude_tRNS=MagickFalse;
11775         mng_info->ping_exclude_vpAg=MagickFalse;
11776         mng_info->ping_exclude_zCCP=MagickFalse;
11777         mng_info->ping_exclude_zTXt=MagickFalse;
11778       }
11779
11780       if (LocaleNCompare(value+i,"bkgd",4) == 0)
11781         mng_info->ping_exclude_bKGD=MagickTrue;
11782
11783       if (LocaleNCompare(value+i,"chrm",4) == 0)
11784         mng_info->ping_exclude_cHRM=MagickTrue;
11785
11786       if (LocaleNCompare(value+i,"date",4) == 0)
11787         mng_info->ping_exclude_date=MagickTrue;
11788
11789       if (LocaleNCompare(value+i,"exif",4) == 0)
11790         mng_info->ping_exclude_EXIF=MagickTrue;
11791
11792       if (LocaleNCompare(value+i,"gama",4) == 0)
11793         mng_info->ping_exclude_gAMA=MagickTrue;
11794
11795       if (LocaleNCompare(value+i,"iccp",4) == 0)
11796         mng_info->ping_exclude_iCCP=MagickTrue;
11797
11798     /*
11799       if (LocaleNCompare(value+i,"itxt",4) == 0)
11800         mng_info->ping_exclude_iTXt=MagickTrue;
11801      */
11802
11803       if (LocaleNCompare(value+i,"gama",4) == 0)
11804         mng_info->ping_exclude_gAMA=MagickTrue;
11805
11806       if (LocaleNCompare(value+i,"offs",4) == 0)
11807         mng_info->ping_exclude_oFFs=MagickTrue;
11808
11809       if (LocaleNCompare(value+i,"phys",4) == 0)
11810         mng_info->ping_exclude_pHYs=MagickTrue;
11811
11812       if (LocaleNCompare(value+i,"srgb",4) == 0)
11813         mng_info->ping_exclude_sRGB=MagickTrue;
11814
11815       if (LocaleNCompare(value+i,"text",4) == 0)
11816         mng_info->ping_exclude_tEXt=MagickTrue;
11817
11818       if (LocaleNCompare(value+i,"trns",4) == 0)
11819         mng_info->ping_exclude_tRNS=MagickTrue;
11820
11821       if (LocaleNCompare(value+i,"vpag",4) == 0)
11822         mng_info->ping_exclude_vpAg=MagickTrue;
11823
11824       if (LocaleNCompare(value+i,"zccp",4) == 0)
11825         mng_info->ping_exclude_zCCP=MagickTrue;
11826
11827       if (LocaleNCompare(value+i,"ztxt",4) == 0)
11828         mng_info->ping_exclude_zTXt=MagickTrue;
11829
11830       }
11831     }
11832   }
11833
11834   for (source=0; source<1; source++)
11835   {
11836     if (source==0)
11837       {
11838        value=GetImageArtifact(image,"png:include-chunk");
11839
11840        if (value == NULL)
11841          value=GetImageArtifact(image,"png:include-chunks");
11842       }
11843     else
11844       {
11845        value=GetImageOption(image_info,"png:include-chunk");
11846
11847        if (value == NULL)
11848          value=GetImageOption(image_info,"png:include-chunks");
11849       }
11850
11851     if (value != NULL)
11852     {
11853     size_t
11854       last;
11855
11856     excluding=MagickTrue;
11857
11858     if (logging != MagickFalse)
11859       {
11860         if (source == 0)
11861            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11862               "  png:include-chunk=%s found in image artifacts.\n", value);
11863         else
11864            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11865               "  png:include-chunk=%s found in image properties.\n", value);
11866       }
11867
11868     last=strlen(value);
11869
11870     for (i=0; i<(int) last; i+=5)
11871       {
11872       if (LocaleNCompare(value+i,"all",3) == 0)
11873         {
11874           mng_info->ping_exclude_bKGD=MagickFalse;
11875           mng_info->ping_exclude_cHRM=MagickFalse;
11876           mng_info->ping_exclude_date=MagickFalse;
11877           mng_info->ping_exclude_EXIF=MagickFalse;
11878           mng_info->ping_exclude_gAMA=MagickFalse;
11879           mng_info->ping_exclude_iCCP=MagickFalse;
11880           /* mng_info->ping_exclude_iTXt=MagickFalse; */
11881           mng_info->ping_exclude_oFFs=MagickFalse;
11882           mng_info->ping_exclude_pHYs=MagickFalse;
11883           mng_info->ping_exclude_sRGB=MagickFalse;
11884           mng_info->ping_exclude_tEXt=MagickFalse;
11885           mng_info->ping_exclude_tRNS=MagickFalse;
11886           mng_info->ping_exclude_vpAg=MagickFalse;
11887           mng_info->ping_exclude_zCCP=MagickFalse;
11888           mng_info->ping_exclude_zTXt=MagickFalse;
11889           i--;
11890         }
11891
11892       if (LocaleNCompare(value+i,"none",4) == 0)
11893         {
11894           mng_info->ping_exclude_bKGD=MagickTrue;
11895           mng_info->ping_exclude_cHRM=MagickTrue;
11896           mng_info->ping_exclude_date=MagickTrue;
11897           mng_info->ping_exclude_EXIF=MagickTrue;
11898           mng_info->ping_exclude_gAMA=MagickTrue;
11899           mng_info->ping_exclude_iCCP=MagickTrue;
11900           /* mng_info->ping_exclude_iTXt=MagickTrue; */
11901           mng_info->ping_exclude_oFFs=MagickTrue;
11902           mng_info->ping_exclude_pHYs=MagickTrue;
11903           mng_info->ping_exclude_sRGB=MagickTrue;
11904           mng_info->ping_exclude_tEXt=MagickTrue;
11905           mng_info->ping_exclude_tRNS=MagickTrue;
11906           mng_info->ping_exclude_vpAg=MagickTrue;
11907           mng_info->ping_exclude_zCCP=MagickTrue;
11908           mng_info->ping_exclude_zTXt=MagickTrue;
11909         }
11910
11911       if (LocaleNCompare(value+i,"bkgd",4) == 0)
11912         mng_info->ping_exclude_bKGD=MagickFalse;
11913
11914       if (LocaleNCompare(value+i,"chrm",4) == 0)
11915         mng_info->ping_exclude_cHRM=MagickFalse;
11916
11917       if (LocaleNCompare(value+i,"date",4) == 0)
11918         mng_info->ping_exclude_date=MagickFalse;
11919
11920       if (LocaleNCompare(value+i,"exif",4) == 0)
11921         mng_info->ping_exclude_EXIF=MagickFalse;
11922
11923       if (LocaleNCompare(value+i,"gama",4) == 0)
11924         mng_info->ping_exclude_gAMA=MagickFalse;
11925
11926       if (LocaleNCompare(value+i,"iccp",4) == 0)
11927         mng_info->ping_exclude_iCCP=MagickFalse;
11928
11929     /*
11930       if (LocaleNCompare(value+i,"itxt",4) == 0)
11931         mng_info->ping_exclude_iTXt=MagickFalse;
11932      */
11933
11934       if (LocaleNCompare(value+i,"gama",4) == 0)
11935         mng_info->ping_exclude_gAMA=MagickFalse;
11936
11937       if (LocaleNCompare(value+i,"offs",4) == 0)
11938         mng_info->ping_exclude_oFFs=MagickFalse;
11939
11940       if (LocaleNCompare(value+i,"phys",4) == 0)
11941         mng_info->ping_exclude_pHYs=MagickFalse;
11942
11943       if (LocaleNCompare(value+i,"srgb",4) == 0)
11944         mng_info->ping_exclude_sRGB=MagickFalse;
11945
11946       if (LocaleNCompare(value+i,"text",4) == 0)
11947         mng_info->ping_exclude_tEXt=MagickFalse;
11948
11949       if (LocaleNCompare(value+i,"trns",4) == 0)
11950         mng_info->ping_exclude_tRNS=MagickFalse;
11951
11952       if (LocaleNCompare(value+i,"vpag",4) == 0)
11953         mng_info->ping_exclude_vpAg=MagickFalse;
11954
11955       if (LocaleNCompare(value+i,"zccp",4) == 0)
11956         mng_info->ping_exclude_zCCP=MagickFalse;
11957
11958       if (LocaleNCompare(value+i,"ztxt",4) == 0)
11959         mng_info->ping_exclude_zTXt=MagickFalse;
11960
11961       }
11962     }
11963   }
11964
11965   if (excluding != MagickFalse && logging != MagickFalse)
11966   {
11967     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11968       "  Chunks to be excluded from the output png:");
11969     if (mng_info->ping_exclude_bKGD != MagickFalse)
11970       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11971           "    bKGD");
11972     if (mng_info->ping_exclude_cHRM != MagickFalse)
11973       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11974           "    cHRM");
11975     if (mng_info->ping_exclude_date != MagickFalse)
11976       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11977           "    date");
11978     if (mng_info->ping_exclude_EXIF != MagickFalse)
11979       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11980           "    EXIF");
11981     if (mng_info->ping_exclude_gAMA != MagickFalse)
11982       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11983           "    gAMA");
11984     if (mng_info->ping_exclude_iCCP != MagickFalse)
11985       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11986           "    iCCP");
11987 /*
11988     if (mng_info->ping_exclude_iTXt != MagickFalse)
11989       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11990           "    iTXt");
11991 */
11992     if (mng_info->ping_exclude_oFFs != MagickFalse)
11993       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11994           "    oFFs");
11995     if (mng_info->ping_exclude_pHYs != MagickFalse)
11996       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11997           "    pHYs");
11998     if (mng_info->ping_exclude_sRGB != MagickFalse)
11999       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12000           "    sRGB");
12001     if (mng_info->ping_exclude_tEXt != MagickFalse)
12002       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12003           "    tEXt");
12004     if (mng_info->ping_exclude_tRNS != MagickFalse)
12005       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12006           "    tRNS");
12007     if (mng_info->ping_exclude_vpAg != MagickFalse)
12008       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12009           "    vpAg");
12010     if (mng_info->ping_exclude_zCCP != MagickFalse)
12011       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12012           "    zCCP");
12013     if (mng_info->ping_exclude_zTXt != MagickFalse)
12014       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12015           "    zTXt");
12016   }
12017
12018   mng_info->need_blob = MagickTrue;
12019
12020   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12021
12022   MngInfoFreeStruct(mng_info,&have_mng_structure);
12023
12024   if (logging != MagickFalse)
12025     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12026
12027   return(status);
12028 }
12029
12030 #if defined(JNG_SUPPORTED)
12031
12032 /* Write one JNG image */
12033 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12034    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12035 {
12036   Image
12037     *jpeg_image;
12038
12039   ImageInfo
12040     *jpeg_image_info;
12041
12042   MagickBooleanType
12043     logging,
12044     status;
12045
12046   size_t
12047     length;
12048
12049   unsigned char
12050     *blob,
12051     chunk[80],
12052     *p;
12053
12054   unsigned int
12055     jng_alpha_compression_method,
12056     jng_alpha_sample_depth,
12057     jng_color_type,
12058     transparent;
12059
12060   size_t
12061     jng_alpha_quality,
12062     jng_quality;
12063
12064   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12065     "  Enter WriteOneJNGImage()");
12066
12067   blob=(unsigned char *) NULL;
12068   jpeg_image=(Image *) NULL;
12069   jpeg_image_info=(ImageInfo *) NULL;
12070
12071   status=MagickTrue;
12072   transparent=image_info->type==GrayscaleMatteType ||
12073      image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
12074
12075   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12076
12077   jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12078
12079   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12080       image_info->quality;
12081
12082   if (jng_alpha_quality >= 1000)
12083     jng_alpha_quality /= 1000;
12084
12085   if (transparent)
12086     {
12087       jng_color_type=14;
12088
12089       /* Create JPEG blob, image, and image_info */
12090       if (logging != MagickFalse)
12091         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12092           "  Creating jpeg_image_info for alpha.");
12093
12094       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12095
12096       if (jpeg_image_info == (ImageInfo *) NULL)
12097         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12098
12099       if (logging != MagickFalse)
12100         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12101           "  Creating jpeg_image.");
12102
12103       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12104       if (jpeg_image == (Image *) NULL)
12105         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12106       (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12107       jpeg_image->alpha_trait=UndefinedPixelTrait;
12108       jpeg_image->quality=jng_alpha_quality;
12109       jpeg_image_info->type=GrayscaleType;
12110       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12111       (void) AcquireUniqueFilename(jpeg_image->filename);
12112       (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
12113         "%s",jpeg_image->filename);
12114     }
12115   else
12116     {
12117       jng_alpha_compression_method=0;
12118       jng_color_type=10;
12119       jng_alpha_sample_depth=0;
12120     }
12121
12122   /* To do: check bit depth of PNG alpha channel */
12123
12124   /* Check if image is grayscale. */
12125   if (image_info->type != TrueColorMatteType && image_info->type !=
12126     TrueColorType && IsImageGray(image,exception))
12127     jng_color_type-=2;
12128
12129   if (logging != MagickFalse)
12130     {
12131         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12132           "    JNG Quality           = %d",(int) jng_quality);
12133         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12134           "    JNG Color Type        = %d",jng_color_type);
12135         if (transparent)
12136           {
12137             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12138               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12139             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12140               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12141             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12142               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12143           }
12144     }
12145
12146   if (transparent)
12147     {
12148       if (jng_alpha_compression_method==0)
12149         {
12150           const char
12151             *value;
12152
12153           /* Encode alpha as a grayscale PNG blob */
12154           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12155             exception);
12156           if (logging != MagickFalse)
12157             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12158               "  Creating PNG blob.");
12159           length=0;
12160
12161           (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12162           (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12163           jpeg_image_info->interlace=NoInterlace;
12164
12165           /* Exclude all ancillary chunks */
12166           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12167
12168           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12169             exception);
12170
12171           /* Retrieve sample depth used */
12172           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12173           if (value != (char *) NULL)
12174             jng_alpha_sample_depth= (unsigned int) value[0];
12175         }
12176       else
12177         {
12178           /* Encode alpha as a grayscale JPEG blob */
12179
12180           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12181             exception);
12182
12183           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12184           (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12185           jpeg_image_info->interlace=NoInterlace;
12186           if (logging != MagickFalse)
12187             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12188               "  Creating blob.");
12189           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12190            exception);
12191           jng_alpha_sample_depth=8;
12192
12193           if (logging != MagickFalse)
12194             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12195               "  Successfully read jpeg_image into a blob, length=%.20g.",
12196               (double) length);
12197
12198         }
12199       /* Destroy JPEG image and image_info */
12200       jpeg_image=DestroyImage(jpeg_image);
12201       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12202       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12203     }
12204
12205   /* Write JHDR chunk */
12206   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12207   PNGType(chunk,mng_JHDR);
12208   LogPNGChunk(logging,mng_JHDR,16L);
12209   PNGLong(chunk+4,(png_uint_32) image->columns);
12210   PNGLong(chunk+8,(png_uint_32) image->rows);
12211   chunk[12]=jng_color_type;
12212   chunk[13]=8;  /* sample depth */
12213   chunk[14]=8; /*jng_image_compression_method */
12214   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12215   chunk[16]=jng_alpha_sample_depth;
12216   chunk[17]=jng_alpha_compression_method;
12217   chunk[18]=0; /*jng_alpha_filter_method */
12218   chunk[19]=0; /*jng_alpha_interlace_method */
12219   (void) WriteBlob(image,20,chunk);
12220   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12221   if (logging != MagickFalse)
12222     {
12223       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12224         "    JNG width:%15lu",(unsigned long) image->columns);
12225
12226       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12227         "    JNG height:%14lu",(unsigned long) image->rows);
12228
12229       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12230         "    JNG color type:%10d",jng_color_type);
12231
12232       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12233         "    JNG sample depth:%8d",8);
12234
12235       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12236         "    JNG compression:%9d",8);
12237
12238       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12239         "    JNG interlace:%11d",0);
12240
12241       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12242         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
12243
12244       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12245         "    JNG alpha compression:%3d",jng_alpha_compression_method);
12246
12247       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12248         "    JNG alpha filter:%8d",0);
12249
12250       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12251         "    JNG alpha interlace:%5d",0);
12252     }
12253
12254   /* Write any JNG-chunk-b profiles */
12255   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12256
12257   /*
12258      Write leading ancillary chunks
12259   */
12260
12261   if (transparent)
12262   {
12263     /*
12264       Write JNG bKGD chunk
12265     */
12266
12267     unsigned char
12268       blue,
12269       green,
12270       red;
12271
12272     ssize_t
12273       num_bytes;
12274
12275     if (jng_color_type == 8 || jng_color_type == 12)
12276       num_bytes=6L;
12277     else
12278       num_bytes=10L;
12279     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12280     PNGType(chunk,mng_bKGD);
12281     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12282     red=ScaleQuantumToChar(image->background_color.red);
12283     green=ScaleQuantumToChar(image->background_color.green);
12284     blue=ScaleQuantumToChar(image->background_color.blue);
12285     *(chunk+4)=0;
12286     *(chunk+5)=red;
12287     *(chunk+6)=0;
12288     *(chunk+7)=green;
12289     *(chunk+8)=0;
12290     *(chunk+9)=blue;
12291     (void) WriteBlob(image,(size_t) num_bytes,chunk);
12292     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12293   }
12294
12295   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12296     {
12297       /*
12298         Write JNG sRGB chunk
12299       */
12300       (void) WriteBlobMSBULong(image,1L);
12301       PNGType(chunk,mng_sRGB);
12302       LogPNGChunk(logging,mng_sRGB,1L);
12303
12304       if (image->rendering_intent != UndefinedIntent)
12305         chunk[4]=(unsigned char)
12306           Magick_RenderingIntent_to_PNG_RenderingIntent(
12307           (image->rendering_intent));
12308
12309       else
12310         chunk[4]=(unsigned char)
12311           Magick_RenderingIntent_to_PNG_RenderingIntent(
12312           (PerceptualIntent));
12313
12314       (void) WriteBlob(image,5,chunk);
12315       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12316     }
12317   else
12318     {
12319       if (image->gamma != 0.0)
12320         {
12321           /*
12322              Write JNG gAMA chunk
12323           */
12324           (void) WriteBlobMSBULong(image,4L);
12325           PNGType(chunk,mng_gAMA);
12326           LogPNGChunk(logging,mng_gAMA,4L);
12327           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12328           (void) WriteBlob(image,8,chunk);
12329           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12330         }
12331
12332       if ((mng_info->equal_chrms == MagickFalse) &&
12333           (image->chromaticity.red_primary.x != 0.0))
12334         {
12335           PrimaryInfo
12336             primary;
12337
12338           /*
12339              Write JNG cHRM chunk
12340           */
12341           (void) WriteBlobMSBULong(image,32L);
12342           PNGType(chunk,mng_cHRM);
12343           LogPNGChunk(logging,mng_cHRM,32L);
12344           primary=image->chromaticity.white_point;
12345           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12346           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12347           primary=image->chromaticity.red_primary;
12348           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12349           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12350           primary=image->chromaticity.green_primary;
12351           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12352           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12353           primary=image->chromaticity.blue_primary;
12354           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12355           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12356           (void) WriteBlob(image,36,chunk);
12357           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12358         }
12359     }
12360
12361   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12362     {
12363       /*
12364          Write JNG pHYs chunk
12365       */
12366       (void) WriteBlobMSBULong(image,9L);
12367       PNGType(chunk,mng_pHYs);
12368       LogPNGChunk(logging,mng_pHYs,9L);
12369       if (image->units == PixelsPerInchResolution)
12370         {
12371           PNGLong(chunk+4,(png_uint_32)
12372             (image->resolution.x*100.0/2.54+0.5));
12373
12374           PNGLong(chunk+8,(png_uint_32)
12375             (image->resolution.y*100.0/2.54+0.5));
12376
12377           chunk[12]=1;
12378         }
12379
12380       else
12381         {
12382           if (image->units == PixelsPerCentimeterResolution)
12383             {
12384               PNGLong(chunk+4,(png_uint_32)
12385                 (image->resolution.x*100.0+0.5));
12386
12387               PNGLong(chunk+8,(png_uint_32)
12388                 (image->resolution.y*100.0+0.5));
12389
12390               chunk[12]=1;
12391             }
12392
12393           else
12394             {
12395               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12396               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12397               chunk[12]=0;
12398             }
12399         }
12400       (void) WriteBlob(image,13,chunk);
12401       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12402     }
12403
12404   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12405     {
12406       /*
12407          Write JNG oFFs chunk
12408       */
12409       (void) WriteBlobMSBULong(image,9L);
12410       PNGType(chunk,mng_oFFs);
12411       LogPNGChunk(logging,mng_oFFs,9L);
12412       PNGsLong(chunk+4,(ssize_t) (image->page.x));
12413       PNGsLong(chunk+8,(ssize_t) (image->page.y));
12414       chunk[12]=0;
12415       (void) WriteBlob(image,13,chunk);
12416       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12417     }
12418   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12419     {
12420        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
12421        PNGType(chunk,mng_vpAg);
12422        LogPNGChunk(logging,mng_vpAg,9L);
12423        PNGLong(chunk+4,(png_uint_32) image->page.width);
12424        PNGLong(chunk+8,(png_uint_32) image->page.height);
12425        chunk[12]=0;   /* unit = pixels */
12426        (void) WriteBlob(image,13,chunk);
12427        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12428     }
12429
12430
12431   if (transparent)
12432     {
12433       if (jng_alpha_compression_method==0)
12434         {
12435           register ssize_t
12436             i;
12437
12438           ssize_t
12439             len;
12440
12441           /* Write IDAT chunk header */
12442           if (logging != MagickFalse)
12443             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12444               "  Write IDAT chunks from blob, length=%.20g.",(double)
12445               length);
12446
12447           /* Copy IDAT chunks */
12448           len=0;
12449           p=blob+8;
12450           for (i=8; i<(ssize_t) length; i+=len+12)
12451           {
12452             len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12453             p+=4;
12454
12455             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12456               {
12457                 /* Found an IDAT chunk. */
12458                 (void) WriteBlobMSBULong(image,(size_t) len);
12459                 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12460                 (void) WriteBlob(image,(size_t) len+4,p);
12461                 (void) WriteBlobMSBULong(image,
12462                     crc32(0,p,(uInt) len+4));
12463               }
12464
12465             else
12466               {
12467                 if (logging != MagickFalse)
12468                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12469                     "    Skipping %c%c%c%c chunk, length=%.20g.",
12470                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
12471               }
12472             p+=(8+len);
12473           }
12474         }
12475       else
12476         {
12477           /* Write JDAA chunk header */
12478           if (logging != MagickFalse)
12479             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12480               "  Write JDAA chunk, length=%.20g.",(double) length);
12481           (void) WriteBlobMSBULong(image,(size_t) length);
12482           PNGType(chunk,mng_JDAA);
12483           LogPNGChunk(logging,mng_JDAA,length);
12484           /* Write JDAT chunk(s) data */
12485           (void) WriteBlob(image,4,chunk);
12486           (void) WriteBlob(image,length,blob);
12487           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12488              (uInt) length));
12489         }
12490       blob=(unsigned char *) RelinquishMagickMemory(blob);
12491     }
12492
12493   /* Encode image as a JPEG blob */
12494   if (logging != MagickFalse)
12495     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12496       "  Creating jpeg_image_info.");
12497   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12498   if (jpeg_image_info == (ImageInfo *) NULL)
12499     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12500
12501   if (logging != MagickFalse)
12502     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12503       "  Creating jpeg_image.");
12504
12505   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12506   if (jpeg_image == (Image *) NULL)
12507     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12508   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12509
12510   (void) AcquireUniqueFilename(jpeg_image->filename);
12511   (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12512     jpeg_image->filename);
12513
12514   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12515     exception);
12516
12517   if (logging != MagickFalse)
12518     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12519       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12520       (double) jpeg_image->rows);
12521
12522   if (jng_color_type == 8 || jng_color_type == 12)
12523     jpeg_image_info->type=GrayscaleType;
12524
12525   jpeg_image_info->quality=jng_quality;
12526   jpeg_image->quality=jng_quality;
12527   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12528   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12529
12530   if (logging != MagickFalse)
12531     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12532       "  Creating blob.");
12533
12534   blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12535
12536   if (logging != MagickFalse)
12537     {
12538       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12539         "  Successfully read jpeg_image into a blob, length=%.20g.",
12540         (double) length);
12541
12542       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12543         "  Write JDAT chunk, length=%.20g.",(double) length);
12544     }
12545
12546   /* Write JDAT chunk(s) */
12547   (void) WriteBlobMSBULong(image,(size_t) length);
12548   PNGType(chunk,mng_JDAT);
12549   LogPNGChunk(logging,mng_JDAT,length);
12550   (void) WriteBlob(image,4,chunk);
12551   (void) WriteBlob(image,length,blob);
12552   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12553
12554   jpeg_image=DestroyImage(jpeg_image);
12555   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12556   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12557   blob=(unsigned char *) RelinquishMagickMemory(blob);
12558
12559   /* Write any JNG-chunk-e profiles */
12560   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12561
12562   /* Write IEND chunk */
12563   (void) WriteBlobMSBULong(image,0L);
12564   PNGType(chunk,mng_IEND);
12565   LogPNGChunk(logging,mng_IEND,0);
12566   (void) WriteBlob(image,4,chunk);
12567   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12568
12569   if (logging != MagickFalse)
12570     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12571       "  exit WriteOneJNGImage()");
12572
12573   return(status);
12574 }
12575
12576
12577 /*
12578 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12579 %                                                                             %
12580 %                                                                             %
12581 %                                                                             %
12582 %   W r i t e J N G I m a g e                                                 %
12583 %                                                                             %
12584 %                                                                             %
12585 %                                                                             %
12586 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12587 %
12588 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12589 %
12590 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
12591 %
12592 %  The format of the WriteJNGImage method is:
12593 %
12594 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12595 %        Image *image,ExceptionInfo *exception)
12596 %
12597 %  A description of each parameter follows:
12598 %
12599 %    o image_info: the image info.
12600 %
12601 %    o image:  The image.
12602 %
12603 %    o exception: return any errors or warnings in this structure.
12604 %
12605 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12606 */
12607 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12608   ExceptionInfo *exception)
12609 {
12610   MagickBooleanType
12611     have_mng_structure,
12612     logging,
12613     status;
12614
12615   MngInfo
12616     *mng_info;
12617
12618   /*
12619     Open image file.
12620   */
12621   assert(image_info != (const ImageInfo *) NULL);
12622   assert(image_info->signature == MagickSignature);
12623   assert(image != (Image *) NULL);
12624   assert(image->signature == MagickSignature);
12625   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12626   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12627   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12628   if (status == MagickFalse)
12629     return(status);
12630
12631   /*
12632     Allocate a MngInfo structure.
12633   */
12634   have_mng_structure=MagickFalse;
12635   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12636   if (mng_info == (MngInfo *) NULL)
12637     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12638   /*
12639     Initialize members of the MngInfo structure.
12640   */
12641   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12642   mng_info->image=image;
12643   have_mng_structure=MagickTrue;
12644
12645   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12646
12647   status=WriteOneJNGImage(mng_info,image_info,image,exception);
12648   (void) CloseBlob(image);
12649
12650   (void) CatchImageException(image);
12651   MngInfoFreeStruct(mng_info,&have_mng_structure);
12652   if (logging != MagickFalse)
12653     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12654   return(status);
12655 }
12656 #endif
12657
12658 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12659   ExceptionInfo *exception)
12660 {
12661   const char
12662     *option;
12663
12664   Image
12665     *next_image;
12666
12667   MagickBooleanType
12668     have_mng_structure,
12669     status;
12670
12671   volatile MagickBooleanType
12672     logging;
12673
12674   MngInfo
12675     *mng_info;
12676
12677   int
12678     image_count,
12679     need_iterations,
12680     need_matte;
12681
12682   volatile int
12683 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12684     defined(PNG_MNG_FEATURES_SUPPORTED)
12685     need_local_plte,
12686 #endif
12687     all_images_are_gray,
12688     need_defi,
12689     use_global_plte;
12690
12691   register ssize_t
12692     i;
12693
12694   unsigned char
12695     chunk[800];
12696
12697   volatile unsigned int
12698     write_jng,
12699     write_mng;
12700
12701   volatile size_t
12702     scene;
12703
12704   size_t
12705     final_delay=0,
12706     initial_delay;
12707
12708 #if (PNG_LIBPNG_VER < 10200)
12709     if (image_info->verbose)
12710       printf("Your PNG library (libpng-%s) is rather old.\n",
12711          PNG_LIBPNG_VER_STRING);
12712 #endif
12713
12714   /*
12715     Open image file.
12716   */
12717   assert(image_info != (const ImageInfo *) NULL);
12718   assert(image_info->signature == MagickSignature);
12719   assert(image != (Image *) NULL);
12720   assert(image->signature == MagickSignature);
12721   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12722   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12723   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12724   if (status == MagickFalse)
12725     return(status);
12726
12727   /*
12728     Allocate a MngInfo structure.
12729   */
12730   have_mng_structure=MagickFalse;
12731   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12732   if (mng_info == (MngInfo *) NULL)
12733     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12734   /*
12735     Initialize members of the MngInfo structure.
12736   */
12737   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12738   mng_info->image=image;
12739   have_mng_structure=MagickTrue;
12740   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12741
12742   /*
12743    * See if user has requested a specific PNG subformat to be used
12744    * for all of the PNGs in the MNG being written, e.g.,
12745    *
12746    *    convert *.png png8:animation.mng
12747    *
12748    * To do: check -define png:bit_depth and png:color_type as well,
12749    * or perhaps use mng:bit_depth and mng:color_type instead for
12750    * global settings.
12751    */
12752
12753   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12754   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12755   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12756
12757   write_jng=MagickFalse;
12758   if (image_info->compression == JPEGCompression)
12759     write_jng=MagickTrue;
12760
12761   mng_info->adjoin=image_info->adjoin &&
12762     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12763
12764   if (logging != MagickFalse)
12765     {
12766       /* Log some info about the input */
12767       Image
12768         *p;
12769
12770       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12771         "  Checking input image(s)");
12772
12773       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12774         "    Image_info depth: %.20g",(double) image_info->depth);
12775
12776       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12777         "    Type: %d",image_info->type);
12778
12779       scene=0;
12780       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12781       {
12782         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12783           "    Scene: %.20g",(double) scene++);
12784
12785         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12786           "      Image depth: %.20g",(double) p->depth);
12787
12788         if (p->alpha_trait == BlendPixelTrait)
12789           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12790             "      Matte: True");
12791
12792         else
12793           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12794             "      Matte: False");
12795
12796         if (p->storage_class == PseudoClass)
12797           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12798             "      Storage class: PseudoClass");
12799
12800         else
12801           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12802             "      Storage class: DirectClass");
12803
12804         if (p->colors)
12805           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12806             "      Number of colors: %.20g",(double) p->colors);
12807
12808         else
12809           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12810             "      Number of colors: unspecified");
12811
12812         if (mng_info->adjoin == MagickFalse)
12813           break;
12814       }
12815     }
12816
12817   use_global_plte=MagickFalse;
12818   all_images_are_gray=MagickFalse;
12819 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12820   need_local_plte=MagickTrue;
12821 #endif
12822   need_defi=MagickFalse;
12823   need_matte=MagickFalse;
12824   mng_info->framing_mode=1;
12825   mng_info->old_framing_mode=1;
12826
12827   if (write_mng)
12828       if (image_info->page != (char *) NULL)
12829         {
12830           /*
12831             Determine image bounding box.
12832           */
12833           SetGeometry(image,&mng_info->page);
12834           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12835             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12836         }
12837   if (write_mng)
12838     {
12839       unsigned int
12840         need_geom;
12841
12842       unsigned short
12843         red,
12844         green,
12845         blue;
12846
12847       mng_info->page=image->page;
12848       need_geom=MagickTrue;
12849       if (mng_info->page.width || mng_info->page.height)
12850          need_geom=MagickFalse;
12851       /*
12852         Check all the scenes.
12853       */
12854       initial_delay=image->delay;
12855       need_iterations=MagickFalse;
12856       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12857       mng_info->equal_physs=MagickTrue,
12858       mng_info->equal_gammas=MagickTrue;
12859       mng_info->equal_srgbs=MagickTrue;
12860       mng_info->equal_backgrounds=MagickTrue;
12861       image_count=0;
12862 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12863     defined(PNG_MNG_FEATURES_SUPPORTED)
12864       all_images_are_gray=MagickTrue;
12865       mng_info->equal_palettes=MagickFalse;
12866       need_local_plte=MagickFalse;
12867 #endif
12868       for (next_image=image; next_image != (Image *) NULL; )
12869       {
12870         if (need_geom)
12871           {
12872             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12873               mng_info->page.width=next_image->columns+next_image->page.x;
12874
12875             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12876               mng_info->page.height=next_image->rows+next_image->page.y;
12877           }
12878
12879         if (next_image->page.x || next_image->page.y)
12880           need_defi=MagickTrue;
12881
12882         if (next_image->alpha_trait == BlendPixelTrait)
12883           need_matte=MagickTrue;
12884
12885         if ((int) next_image->dispose >= BackgroundDispose)
12886           if ((next_image->alpha_trait == BlendPixelTrait) ||
12887                next_image->page.x || next_image->page.y ||
12888               ((next_image->columns < mng_info->page.width) &&
12889                (next_image->rows < mng_info->page.height)))
12890             mng_info->need_fram=MagickTrue;
12891
12892         if (next_image->iterations)
12893           need_iterations=MagickTrue;
12894
12895         final_delay=next_image->delay;
12896
12897         if (final_delay != initial_delay || final_delay > 1UL*
12898            next_image->ticks_per_second)
12899           mng_info->need_fram=1;
12900
12901 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12902     defined(PNG_MNG_FEATURES_SUPPORTED)
12903         /*
12904           check for global palette possibility.
12905         */
12906         if (image->alpha_trait == BlendPixelTrait)
12907            need_local_plte=MagickTrue;
12908
12909         if (need_local_plte == 0)
12910           {
12911             if (IsImageGray(image,exception) == MagickFalse)
12912               all_images_are_gray=MagickFalse;
12913             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12914             if (use_global_plte == 0)
12915               use_global_plte=mng_info->equal_palettes;
12916             need_local_plte=!mng_info->equal_palettes;
12917           }
12918 #endif
12919         if (GetNextImageInList(next_image) != (Image *) NULL)
12920           {
12921             if (next_image->background_color.red !=
12922                 next_image->next->background_color.red ||
12923                 next_image->background_color.green !=
12924                 next_image->next->background_color.green ||
12925                 next_image->background_color.blue !=
12926                 next_image->next->background_color.blue)
12927               mng_info->equal_backgrounds=MagickFalse;
12928
12929             if (next_image->gamma != next_image->next->gamma)
12930               mng_info->equal_gammas=MagickFalse;
12931
12932             if (next_image->rendering_intent !=
12933                 next_image->next->rendering_intent)
12934               mng_info->equal_srgbs=MagickFalse;
12935
12936             if ((next_image->units != next_image->next->units) ||
12937                 (next_image->resolution.x != next_image->next->resolution.x) ||
12938                 (next_image->resolution.y != next_image->next->resolution.y))
12939               mng_info->equal_physs=MagickFalse;
12940
12941             if (mng_info->equal_chrms)
12942               {
12943                 if (next_image->chromaticity.red_primary.x !=
12944                     next_image->next->chromaticity.red_primary.x ||
12945                     next_image->chromaticity.red_primary.y !=
12946                     next_image->next->chromaticity.red_primary.y ||
12947                     next_image->chromaticity.green_primary.x !=
12948                     next_image->next->chromaticity.green_primary.x ||
12949                     next_image->chromaticity.green_primary.y !=
12950                     next_image->next->chromaticity.green_primary.y ||
12951                     next_image->chromaticity.blue_primary.x !=
12952                     next_image->next->chromaticity.blue_primary.x ||
12953                     next_image->chromaticity.blue_primary.y !=
12954                     next_image->next->chromaticity.blue_primary.y ||
12955                     next_image->chromaticity.white_point.x !=
12956                     next_image->next->chromaticity.white_point.x ||
12957                     next_image->chromaticity.white_point.y !=
12958                     next_image->next->chromaticity.white_point.y)
12959                   mng_info->equal_chrms=MagickFalse;
12960               }
12961           }
12962         image_count++;
12963         next_image=GetNextImageInList(next_image);
12964       }
12965       if (image_count < 2)
12966         {
12967           mng_info->equal_backgrounds=MagickFalse;
12968           mng_info->equal_chrms=MagickFalse;
12969           mng_info->equal_gammas=MagickFalse;
12970           mng_info->equal_srgbs=MagickFalse;
12971           mng_info->equal_physs=MagickFalse;
12972           use_global_plte=MagickFalse;
12973 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12974           need_local_plte=MagickTrue;
12975 #endif
12976           need_iterations=MagickFalse;
12977         }
12978
12979      if (mng_info->need_fram == MagickFalse)
12980        {
12981          /*
12982            Only certain framing rates 100/n are exactly representable without
12983            the FRAM chunk but we'll allow some slop in VLC files
12984          */
12985          if (final_delay == 0)
12986            {
12987              if (need_iterations != MagickFalse)
12988                {
12989                  /*
12990                    It's probably a GIF with loop; don't run it *too* fast.
12991                  */
12992                  if (mng_info->adjoin)
12993                    {
12994                      final_delay=10;
12995                      (void) ThrowMagickException(exception,GetMagickModule(),
12996                        CoderWarning,
12997                        "input has zero delay between all frames; assuming",
12998                        " 10 cs `%s'","");
12999                    }
13000                }
13001              else
13002                mng_info->ticks_per_second=0;
13003            }
13004          if (final_delay != 0)
13005            mng_info->ticks_per_second=(png_uint_32)
13006               (image->ticks_per_second/final_delay);
13007          if (final_delay > 50)
13008            mng_info->ticks_per_second=2;
13009
13010          if (final_delay > 75)
13011            mng_info->ticks_per_second=1;
13012
13013          if (final_delay > 125)
13014            mng_info->need_fram=MagickTrue;
13015
13016          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13017             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13018             (final_delay != 25) && (final_delay != 50) && (final_delay !=
13019                1UL*image->ticks_per_second))
13020            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13021        }
13022
13023      if (mng_info->need_fram != MagickFalse)
13024         mng_info->ticks_per_second=1UL*image->ticks_per_second;
13025      /*
13026         If pseudocolor, we should also check to see if all the
13027         palettes are identical and write a global PLTE if they are.
13028         ../glennrp Feb 99.
13029      */
13030      /*
13031         Write the MNG version 1.0 signature and MHDR chunk.
13032      */
13033      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13034      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13035      PNGType(chunk,mng_MHDR);
13036      LogPNGChunk(logging,mng_MHDR,28L);
13037      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13038      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13039      PNGLong(chunk+12,mng_info->ticks_per_second);
13040      PNGLong(chunk+16,0L);  /* layer count=unknown */
13041      PNGLong(chunk+20,0L);  /* frame count=unknown */
13042      PNGLong(chunk+24,0L);  /* play time=unknown   */
13043      if (write_jng)
13044        {
13045          if (need_matte)
13046            {
13047              if (need_defi || mng_info->need_fram || use_global_plte)
13048                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13049
13050              else
13051                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13052            }
13053
13054          else
13055            {
13056              if (need_defi || mng_info->need_fram || use_global_plte)
13057                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13058
13059              else
13060                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13061            }
13062        }
13063
13064      else
13065        {
13066          if (need_matte)
13067            {
13068              if (need_defi || mng_info->need_fram || use_global_plte)
13069                PNGLong(chunk+28,11L);    /* simplicity=LC */
13070
13071              else
13072                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13073            }
13074
13075          else
13076            {
13077              if (need_defi || mng_info->need_fram || use_global_plte)
13078                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13079
13080              else
13081                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13082            }
13083        }
13084      (void) WriteBlob(image,32,chunk);
13085      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13086      option=GetImageOption(image_info,"mng:need-cacheoff");
13087      if (option != (const char *) NULL)
13088        {
13089          size_t
13090            length;
13091
13092          /*
13093            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13094          */
13095          PNGType(chunk,mng_nEED);
13096          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13097          (void) WriteBlobMSBULong(image,(size_t) length);
13098          LogPNGChunk(logging,mng_nEED,(size_t) length);
13099          length+=4;
13100          (void) WriteBlob(image,length,chunk);
13101          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13102        }
13103      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13104          (GetNextImageInList(image) != (Image *) NULL) &&
13105          (image->iterations != 1))
13106        {
13107          /*
13108            Write MNG TERM chunk
13109          */
13110          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13111          PNGType(chunk,mng_TERM);
13112          LogPNGChunk(logging,mng_TERM,10L);
13113          chunk[4]=3;  /* repeat animation */
13114          chunk[5]=0;  /* show last frame when done */
13115          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13116             final_delay/MagickMax(image->ticks_per_second,1)));
13117
13118          if (image->iterations == 0)
13119            PNGLong(chunk+10,PNG_UINT_31_MAX);
13120
13121          else
13122            PNGLong(chunk+10,(png_uint_32) image->iterations);
13123
13124          if (logging != MagickFalse)
13125            {
13126              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13127                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13128               final_delay/MagickMax(image->ticks_per_second,1)));
13129
13130              if (image->iterations == 0)
13131                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13132                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13133
13134              else
13135                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13136                  "     Image iterations: %.20g",(double) image->iterations);
13137            }
13138          (void) WriteBlob(image,14,chunk);
13139          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13140        }
13141      /*
13142        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13143      */
13144      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13145           mng_info->equal_srgbs)
13146        {
13147          /*
13148            Write MNG sRGB chunk
13149          */
13150          (void) WriteBlobMSBULong(image,1L);
13151          PNGType(chunk,mng_sRGB);
13152          LogPNGChunk(logging,mng_sRGB,1L);
13153
13154          if (image->rendering_intent != UndefinedIntent)
13155            chunk[4]=(unsigned char)
13156              Magick_RenderingIntent_to_PNG_RenderingIntent(
13157              (image->rendering_intent));
13158
13159          else
13160            chunk[4]=(unsigned char)
13161              Magick_RenderingIntent_to_PNG_RenderingIntent(
13162                (PerceptualIntent));
13163
13164          (void) WriteBlob(image,5,chunk);
13165          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13166          mng_info->have_write_global_srgb=MagickTrue;
13167        }
13168
13169      else
13170        {
13171          if (image->gamma && mng_info->equal_gammas)
13172            {
13173              /*
13174                 Write MNG gAMA chunk
13175              */
13176              (void) WriteBlobMSBULong(image,4L);
13177              PNGType(chunk,mng_gAMA);
13178              LogPNGChunk(logging,mng_gAMA,4L);
13179              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13180              (void) WriteBlob(image,8,chunk);
13181              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13182              mng_info->have_write_global_gama=MagickTrue;
13183            }
13184          if (mng_info->equal_chrms)
13185            {
13186              PrimaryInfo
13187                primary;
13188
13189              /*
13190                 Write MNG cHRM chunk
13191              */
13192              (void) WriteBlobMSBULong(image,32L);
13193              PNGType(chunk,mng_cHRM);
13194              LogPNGChunk(logging,mng_cHRM,32L);
13195              primary=image->chromaticity.white_point;
13196              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13197              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13198              primary=image->chromaticity.red_primary;
13199              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13200              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13201              primary=image->chromaticity.green_primary;
13202              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13203              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13204              primary=image->chromaticity.blue_primary;
13205              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13206              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13207              (void) WriteBlob(image,36,chunk);
13208              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13209              mng_info->have_write_global_chrm=MagickTrue;
13210            }
13211        }
13212      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13213        {
13214          /*
13215             Write MNG pHYs chunk
13216          */
13217          (void) WriteBlobMSBULong(image,9L);
13218          PNGType(chunk,mng_pHYs);
13219          LogPNGChunk(logging,mng_pHYs,9L);
13220
13221          if (image->units == PixelsPerInchResolution)
13222            {
13223              PNGLong(chunk+4,(png_uint_32)
13224                (image->resolution.x*100.0/2.54+0.5));
13225
13226              PNGLong(chunk+8,(png_uint_32)
13227                (image->resolution.y*100.0/2.54+0.5));
13228
13229              chunk[12]=1;
13230            }
13231
13232          else
13233            {
13234              if (image->units == PixelsPerCentimeterResolution)
13235                {
13236                  PNGLong(chunk+4,(png_uint_32)
13237                    (image->resolution.x*100.0+0.5));
13238
13239                  PNGLong(chunk+8,(png_uint_32)
13240                    (image->resolution.y*100.0+0.5));
13241
13242                  chunk[12]=1;
13243                }
13244
13245              else
13246                {
13247                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13248                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13249                  chunk[12]=0;
13250                }
13251            }
13252          (void) WriteBlob(image,13,chunk);
13253          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13254        }
13255      /*
13256        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13257        or does not cover the entire frame.
13258      */
13259      if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
13260          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13261          (image->page.width+image->page.x < mng_info->page.width))
13262          || (image->page.height && (image->page.height+image->page.y
13263          < mng_info->page.height))))
13264        {
13265          (void) WriteBlobMSBULong(image,6L);
13266          PNGType(chunk,mng_BACK);
13267          LogPNGChunk(logging,mng_BACK,6L);
13268          red=ScaleQuantumToShort(image->background_color.red);
13269          green=ScaleQuantumToShort(image->background_color.green);
13270          blue=ScaleQuantumToShort(image->background_color.blue);
13271          PNGShort(chunk+4,red);
13272          PNGShort(chunk+6,green);
13273          PNGShort(chunk+8,blue);
13274          (void) WriteBlob(image,10,chunk);
13275          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13276          if (mng_info->equal_backgrounds)
13277            {
13278              (void) WriteBlobMSBULong(image,6L);
13279              PNGType(chunk,mng_bKGD);
13280              LogPNGChunk(logging,mng_bKGD,6L);
13281              (void) WriteBlob(image,10,chunk);
13282              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13283            }
13284        }
13285
13286 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13287      if ((need_local_plte == MagickFalse) &&
13288          (image->storage_class == PseudoClass) &&
13289          (all_images_are_gray == MagickFalse))
13290        {
13291          size_t
13292            data_length;
13293
13294          /*
13295            Write MNG PLTE chunk
13296          */
13297          data_length=3*image->colors;
13298          (void) WriteBlobMSBULong(image,data_length);
13299          PNGType(chunk,mng_PLTE);
13300          LogPNGChunk(logging,mng_PLTE,data_length);
13301
13302          for (i=0; i < (ssize_t) image->colors; i++)
13303          {
13304            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13305              image->colormap[i].red) & 0xff);
13306            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13307              image->colormap[i].green) & 0xff);
13308            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13309              image->colormap[i].blue) & 0xff);
13310          }
13311
13312          (void) WriteBlob(image,data_length+4,chunk);
13313          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13314          mng_info->have_write_global_plte=MagickTrue;
13315        }
13316 #endif
13317     }
13318   scene=0;
13319   mng_info->delay=0;
13320 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13321     defined(PNG_MNG_FEATURES_SUPPORTED)
13322   mng_info->equal_palettes=MagickFalse;
13323 #endif
13324   do
13325   {
13326     if (mng_info->adjoin)
13327     {
13328 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13329     defined(PNG_MNG_FEATURES_SUPPORTED)
13330     /*
13331       If we aren't using a global palette for the entire MNG, check to
13332       see if we can use one for two or more consecutive images.
13333     */
13334     if (need_local_plte && use_global_plte && !all_images_are_gray)
13335       {
13336         if (mng_info->IsPalette)
13337           {
13338             /*
13339               When equal_palettes is true, this image has the same palette
13340               as the previous PseudoClass image
13341             */
13342             mng_info->have_write_global_plte=mng_info->equal_palettes;
13343             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13344             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13345               {
13346                 /*
13347                   Write MNG PLTE chunk
13348                 */
13349                 size_t
13350                   data_length;
13351
13352                 data_length=3*image->colors;
13353                 (void) WriteBlobMSBULong(image,data_length);
13354                 PNGType(chunk,mng_PLTE);
13355                 LogPNGChunk(logging,mng_PLTE,data_length);
13356
13357                 for (i=0; i < (ssize_t) image->colors; i++)
13358                 {
13359                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13360                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13361                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13362                 }
13363
13364                 (void) WriteBlob(image,data_length+4,chunk);
13365                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13366                    (uInt) (data_length+4)));
13367                 mng_info->have_write_global_plte=MagickTrue;
13368               }
13369           }
13370         else
13371           mng_info->have_write_global_plte=MagickFalse;
13372       }
13373 #endif
13374     if (need_defi)
13375       {
13376         ssize_t
13377           previous_x,
13378           previous_y;
13379
13380         if (scene)
13381           {
13382             previous_x=mng_info->page.x;
13383             previous_y=mng_info->page.y;
13384           }
13385         else
13386           {
13387             previous_x=0;
13388             previous_y=0;
13389           }
13390         mng_info->page=image->page;
13391         if ((mng_info->page.x !=  previous_x) ||
13392             (mng_info->page.y != previous_y))
13393           {
13394              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
13395              PNGType(chunk,mng_DEFI);
13396              LogPNGChunk(logging,mng_DEFI,12L);
13397              chunk[4]=0; /* object 0 MSB */
13398              chunk[5]=0; /* object 0 LSB */
13399              chunk[6]=0; /* visible  */
13400              chunk[7]=0; /* abstract */
13401              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13402              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13403              (void) WriteBlob(image,16,chunk);
13404              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13405           }
13406       }
13407     }
13408
13409    mng_info->write_mng=write_mng;
13410
13411    if ((int) image->dispose >= 3)
13412      mng_info->framing_mode=3;
13413
13414    if (mng_info->need_fram && mng_info->adjoin &&
13415        ((image->delay != mng_info->delay) ||
13416         (mng_info->framing_mode != mng_info->old_framing_mode)))
13417      {
13418        if (image->delay == mng_info->delay)
13419          {
13420            /*
13421              Write a MNG FRAM chunk with the new framing mode.
13422            */
13423            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
13424            PNGType(chunk,mng_FRAM);
13425            LogPNGChunk(logging,mng_FRAM,1L);
13426            chunk[4]=(unsigned char) mng_info->framing_mode;
13427            (void) WriteBlob(image,5,chunk);
13428            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13429          }
13430        else
13431          {
13432            /*
13433              Write a MNG FRAM chunk with the delay.
13434            */
13435            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13436            PNGType(chunk,mng_FRAM);
13437            LogPNGChunk(logging,mng_FRAM,10L);
13438            chunk[4]=(unsigned char) mng_info->framing_mode;
13439            chunk[5]=0;  /* frame name separator (no name) */
13440            chunk[6]=2;  /* flag for changing default delay */
13441            chunk[7]=0;  /* flag for changing frame timeout */
13442            chunk[8]=0;  /* flag for changing frame clipping */
13443            chunk[9]=0;  /* flag for changing frame sync_id */
13444            PNGLong(chunk+10,(png_uint_32)
13445              ((mng_info->ticks_per_second*
13446              image->delay)/MagickMax(image->ticks_per_second,1)));
13447            (void) WriteBlob(image,14,chunk);
13448            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13449            mng_info->delay=(png_uint_32) image->delay;
13450          }
13451        mng_info->old_framing_mode=mng_info->framing_mode;
13452      }
13453
13454 #if defined(JNG_SUPPORTED)
13455    if (image_info->compression == JPEGCompression)
13456      {
13457        ImageInfo
13458          *write_info;
13459
13460        if (logging != MagickFalse)
13461          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13462            "  Writing JNG object.");
13463        /* To do: specify the desired alpha compression method. */
13464        write_info=CloneImageInfo(image_info);
13465        write_info->compression=UndefinedCompression;
13466        status=WriteOneJNGImage(mng_info,write_info,image,exception);
13467        write_info=DestroyImageInfo(write_info);
13468      }
13469    else
13470 #endif
13471      {
13472        if (logging != MagickFalse)
13473          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13474            "  Writing PNG object.");
13475
13476        mng_info->need_blob = MagickFalse;
13477        mng_info->ping_preserve_colormap = MagickFalse;
13478
13479        /* We don't want any ancillary chunks written */
13480        mng_info->ping_exclude_bKGD=MagickTrue;
13481        mng_info->ping_exclude_cHRM=MagickTrue;
13482        mng_info->ping_exclude_date=MagickTrue;
13483        mng_info->ping_exclude_EXIF=MagickTrue;
13484        mng_info->ping_exclude_gAMA=MagickTrue;
13485        mng_info->ping_exclude_iCCP=MagickTrue;
13486        /* mng_info->ping_exclude_iTXt=MagickTrue; */
13487        mng_info->ping_exclude_oFFs=MagickTrue;
13488        mng_info->ping_exclude_pHYs=MagickTrue;
13489        mng_info->ping_exclude_sRGB=MagickTrue;
13490        mng_info->ping_exclude_tEXt=MagickTrue;
13491        mng_info->ping_exclude_tRNS=MagickTrue;
13492        mng_info->ping_exclude_vpAg=MagickTrue;
13493        mng_info->ping_exclude_zCCP=MagickTrue;
13494        mng_info->ping_exclude_zTXt=MagickTrue;
13495
13496        status=WriteOnePNGImage(mng_info,image_info,image,exception);
13497      }
13498
13499     if (status == MagickFalse)
13500       {
13501         MngInfoFreeStruct(mng_info,&have_mng_structure);
13502         (void) CloseBlob(image);
13503         return(MagickFalse);
13504       }
13505     (void) CatchImageException(image);
13506     if (GetNextImageInList(image) == (Image *) NULL)
13507       break;
13508     image=SyncNextImageInList(image);
13509     status=SetImageProgress(image,SaveImagesTag,scene++,
13510       GetImageListLength(image));
13511
13512     if (status == MagickFalse)
13513       break;
13514
13515   } while (mng_info->adjoin);
13516
13517   if (write_mng)
13518     {
13519       while (GetPreviousImageInList(image) != (Image *) NULL)
13520         image=GetPreviousImageInList(image);
13521       /*
13522         Write the MEND chunk.
13523       */
13524       (void) WriteBlobMSBULong(image,0x00000000L);
13525       PNGType(chunk,mng_MEND);
13526       LogPNGChunk(logging,mng_MEND,0L);
13527       (void) WriteBlob(image,4,chunk);
13528       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13529     }
13530   /*
13531     Relinquish resources.
13532   */
13533   (void) CloseBlob(image);
13534   MngInfoFreeStruct(mng_info,&have_mng_structure);
13535
13536   if (logging != MagickFalse)
13537     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13538
13539   return(MagickTrue);
13540 }
13541 #else /* PNG_LIBPNG_VER > 10011 */
13542
13543 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13544 {
13545   (void) image;
13546   printf("Your PNG library is too old: You have libpng-%s\n",
13547      PNG_LIBPNG_VER_STRING);
13548
13549   ThrowBinaryException(CoderError,"PNG library is too old",
13550      image_info->filename);
13551 }
13552
13553 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13554 {
13555   return(WritePNGImage(image_info,image));
13556 }
13557 #endif /* PNG_LIBPNG_VER > 10011 */
13558 #endif