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