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