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