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