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