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