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