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