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