]> granicus.if.org Git - imagemagick/blob - coders/png.c
Avoid a libpng16 warning about storing unknown chunks.
[imagemagick] / coders / png.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            PPPP   N   N   GGGG                              %
7 %                            P   P  NN  N  G                                  %
8 %                            PPPP   N N N  G  GG                              %
9 %                            P      N  NN  G   G                              %
10 %                            P      N   N   GGG                               %
11 %                                                                             %
12 %                                                                             %
13 %              Read/Write Portable Network Graphics Image Format              %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                           Glenn Randers-Pehrson                             %
18 %                               November 1997                                 %
19 %                                                                             %
20 %                                                                             %
21 %  Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization      %
22 %  dedicated to making software imaging solutions freely available.           %
23 %                                                                             %
24 %  You may not use this file except in compliance with the License.  You may  %
25 %  obtain a copy of the License at                                            %
26 %                                                                             %
27 %    http://www.imagemagick.org/script/license.php                            %
28 %                                                                             %
29 %  Unless required by applicable law or agreed to in writing, software        %
30 %  distributed under the License is distributed on an "AS IS" BASIS,          %
31 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32 %  See the License for the specific language governing permissions and        %
33 %  limitations under the License.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39
40 \f
41 /*
42   Include declarations.
43 */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
52 #include "MagickCore/color-private.h"
53 #include "MagickCore/colormap.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/histogram.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/layer.h"
65 #include "MagickCore/list.h"
66 #include "MagickCore/log.h"
67 #include "MagickCore/MagickCore.h"
68 #include "MagickCore/memory_.h"
69 #include "MagickCore/module.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/profile.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/static.h"
82 #include "MagickCore/statistic.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/utility.h"
87 #if defined(MAGICKCORE_PNG_DELEGATE)
88
89 /* Suppress libpng pedantic warnings that were added in
90  * libpng-1.2.41 and libpng-1.4.0.  If you are working on
91  * migration to libpng-1.5, remove these defines and then
92  * fix any code that generates warnings.
93  */
94 /* #define PNG_DEPRECATED   Use of this function is deprecated */
95 /* #define PNG_USE_RESULT   The result of this function must be checked */
96 /* #define PNG_NORETURN     This function does not return */
97 /* #define PNG_ALLOCATED    The result of the function is new memory */
98 /* #define PNG_DEPSTRUCT    Access to this struct member is deprecated */
99
100 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
101 #define PNG_PTR_NORETURN
102
103 #include "png.h"
104 #include "zlib.h"
105 \f
106 /* ImageMagick differences */
107 #define first_scene scene
108
109 #if PNG_LIBPNG_VER > 10011
110 /*
111   Optional declarations. Define or undefine them as you like.
112 */
113 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
114
115 /*
116   Features under construction.  Define these to work on them.
117 */
118 #undef MNG_OBJECT_BUFFERS
119 #undef MNG_BASI_SUPPORTED
120 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121 #define MNG_INSERT_LAYERS   /* Troublesome, but seem to work as of 5.4.4 */
122 #if defined(MAGICKCORE_JPEG_DELEGATE)
123 #  define JNG_SUPPORTED /* Not finished as of 5.5.2.  See "To do" comments. */
124 #endif
125 #if !defined(RGBColorMatchExact)
126 #define IsPNGColorEqual(color,target) \
127        (((color).red == (target).red) && \
128         ((color).green == (target).green) && \
129         ((color).blue == (target).blue))
130 #endif
131
132 /* Macros for left-bit-replication to ensure that pixels
133  * and PixelInfos all have the same image->depth, and for use
134  * in PNG8 quantization.
135  */
136
137
138 /* LBR01: Replicate top bit */
139
140 #define LBR01PacketRed(pixelpacket) \
141      (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
142         0 : QuantumRange);
143
144 #define LBR01PacketGreen(pixelpacket) \
145      (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
146         0 : QuantumRange);
147
148 #define LBR01PacketBlue(pixelpacket) \
149      (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
150         0 : QuantumRange);
151
152 #define LBR01PacketAlpha(pixelpacket) \
153      (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
154         0 : QuantumRange);
155
156 #define LBR01PacketRGB(pixelpacket) \
157         { \
158         LBR01PacketRed((pixelpacket)); \
159         LBR01PacketGreen((pixelpacket)); \
160         LBR01PacketBlue((pixelpacket)); \
161         }
162
163 #define LBR01PacketRGBO(pixelpacket) \
164         { \
165         LBR01PacketRGB((pixelpacket)); \
166         LBR01PacketAlpha((pixelpacket)); \
167         }
168
169 #define LBR01PixelRed(pixel) \
170         (SetPixelRed(image, \
171         ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
172         0 : QuantumRange,(pixel)));
173
174 #define LBR01PixelGreen(pixel) \
175         (SetPixelGreen(image, \
176         ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
177         0 : QuantumRange,(pixel)));
178
179 #define LBR01PixelBlue(pixel) \
180         (SetPixelBlue(image, \
181         ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
182         0 : QuantumRange,(pixel)));
183
184 #define LBR01PixelAlpha(pixel) \
185         (SetPixelAlpha(image, \
186         ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
187         0 : QuantumRange,(pixel)));
188
189 #define LBR01PixelRGB(pixel) \
190         { \
191         LBR01PixelRed((pixel)); \
192         LBR01PixelGreen((pixel)); \
193         LBR01PixelBlue((pixel)); \
194         }
195
196 #define LBR01PixelRGBA(pixel) \
197         { \
198         LBR01PixelRGB((pixel)); \
199         LBR01PixelAlpha((pixel)); \
200         }
201
202 /* LBR02: Replicate top 2 bits */
203
204 #define LBR02PacketRed(pixelpacket) \
205    { \
206      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
207      (pixelpacket).red=ScaleCharToQuantum( \
208        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
209    }
210 #define LBR02PacketGreen(pixelpacket) \
211    { \
212      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
213      (pixelpacket).green=ScaleCharToQuantum( \
214        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
215    }
216 #define LBR02PacketBlue(pixelpacket) \
217    { \
218      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
219      (pixelpacket).blue=ScaleCharToQuantum( \
220        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
221    }
222 #define LBR02PacketAlpha(pixelpacket) \
223    { \
224      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
225      (pixelpacket).alpha=ScaleCharToQuantum( \
226        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
227    }
228
229 #define LBR02PacketRGB(pixelpacket) \
230         { \
231         LBR02PacketRed((pixelpacket)); \
232         LBR02PacketGreen((pixelpacket)); \
233         LBR02PacketBlue((pixelpacket)); \
234         }
235
236 #define LBR02PacketRGBO(pixelpacket) \
237         { \
238         LBR02PacketRGB((pixelpacket)); \
239         LBR02PacketAlpha((pixelpacket)); \
240         }
241
242 #define LBR02PixelRed(pixel) \
243    { \
244      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
245        & 0xc0; \
246      SetPixelRed(image, ScaleCharToQuantum( \
247        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
248        (pixel)); \
249    }
250 #define LBR02PixelGreen(pixel) \
251    { \
252      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
253        & 0xc0; \
254      SetPixelGreen(image, ScaleCharToQuantum( \
255        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
256        (pixel)); \
257    }
258 #define LBR02PixelBlue(pixel) \
259    { \
260      unsigned char lbr_bits= \
261        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
262      SetPixelBlue(image, ScaleCharToQuantum( \
263        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
264        (pixel)); \
265    }
266 #define LBR02PixelAlpha(pixel) \
267    { \
268      unsigned char lbr_bits= \
269        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
270      SetPixelAlpha(image, ScaleCharToQuantum( \
271        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
272        (pixel) ); \
273    }
274
275 #define LBR02PixelRGB(pixel) \
276         { \
277         LBR02PixelRed((pixel)); \
278         LBR02PixelGreen((pixel)); \
279         LBR02PixelBlue((pixel)); \
280         }
281
282 #define LBR02PixelRGBA(pixel) \
283         { \
284         LBR02PixelRGB((pixel)); \
285         LBR02PixelAlpha((pixel)); \
286         }
287
288 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
289    PNG8 quantization) */
290
291 #define LBR03PacketRed(pixelpacket) \
292    { \
293      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
294      (pixelpacket).red=ScaleCharToQuantum( \
295        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
296    }
297 #define LBR03PacketGreen(pixelpacket) \
298    { \
299      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
300      (pixelpacket).green=ScaleCharToQuantum( \
301        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
302    }
303 #define LBR03PacketBlue(pixelpacket) \
304    { \
305      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
306      (pixelpacket).blue=ScaleCharToQuantum( \
307        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
308    }
309
310 #define LBR03PacketRGB(pixelpacket) \
311         { \
312         LBR03PacketRed((pixelpacket)); \
313         LBR03PacketGreen((pixelpacket)); \
314         LBR03PacketBlue((pixelpacket)); \
315         }
316
317 #define LBR03PixelRed(pixel) \
318    { \
319      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
320        & 0xe0; \
321      SetPixelRed(image, ScaleCharToQuantum( \
322        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
323    }
324 #define LBR03Green(pixel) \
325    { \
326      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
327        & 0xe0; \
328      SetPixelGreen(image, ScaleCharToQuantum( \
329        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
330    }
331 #define LBR03Blue(pixel) \
332    { \
333      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
334        & 0xe0; \
335      SetPixelBlue(image, ScaleCharToQuantum( \
336        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
337    }
338
339 #define LBR03RGB(pixel) \
340         { \
341         LBR03PixelRed((pixel)); \
342         LBR03Green((pixel)); \
343         LBR03Blue((pixel)); \
344         }
345
346 /* LBR04: Replicate top 4 bits */
347
348 #define LBR04PacketRed(pixelpacket) \
349    { \
350      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
351      (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
352    }
353 #define LBR04PacketGreen(pixelpacket) \
354    { \
355      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
356      (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
357    }
358 #define LBR04PacketBlue(pixelpacket) \
359    { \
360      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
361      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
362    }
363 #define LBR04PacketAlpha(pixelpacket) \
364    { \
365      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
366      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
367    }
368
369 #define LBR04PacketRGB(pixelpacket) \
370         { \
371         LBR04PacketRed((pixelpacket)); \
372         LBR04PacketGreen((pixelpacket)); \
373         LBR04PacketBlue((pixelpacket)); \
374         }
375
376 #define LBR04PacketRGBO(pixelpacket) \
377         { \
378         LBR04PacketRGB((pixelpacket)); \
379         LBR04PacketAlpha((pixelpacket)); \
380         }
381
382 #define LBR04PixelRed(pixel) \
383    { \
384      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
385        & 0xf0; \
386      SetPixelRed(image,\
387        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
388    }
389 #define LBR04PixelGreen(pixel) \
390    { \
391      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
392        & 0xf0; \
393      SetPixelGreen(image,\
394        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
395    }
396 #define LBR04PixelBlue(pixel) \
397    { \
398      unsigned char lbr_bits= \
399        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
400      SetPixelBlue(image,\
401        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
402    }
403 #define LBR04PixelAlpha(pixel) \
404    { \
405      unsigned char lbr_bits= \
406        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
407      SetPixelAlpha(image,\
408        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
409    }
410
411 #define LBR04PixelRGB(pixel) \
412         { \
413         LBR04PixelRed((pixel)); \
414         LBR04PixelGreen((pixel)); \
415         LBR04PixelBlue((pixel)); \
416         }
417
418 #define LBR04PixelRGBA(pixel) \
419         { \
420         LBR04PixelRGB((pixel)); \
421         LBR04PixelAlpha((pixel)); \
422         }
423
424
425 #if MAGICKCORE_QUANTUM_DEPTH > 8
426 /* LBR08: Replicate top 8 bits */
427
428 #define LBR08PacketRed(pixelpacket) \
429    { \
430      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
431      (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
432    }
433 #define LBR08PacketGreen(pixelpacket) \
434    { \
435      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
436      (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
437    }
438 #define LBR08PacketBlue(pixelpacket) \
439    { \
440      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
441      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
442    }
443 #define LBR08PacketAlpha(pixelpacket) \
444    { \
445      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
446      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
447    }
448
449 #define LBR08PacketRGB(pixelpacket) \
450         { \
451         LBR08PacketRed((pixelpacket)); \
452         LBR08PacketGreen((pixelpacket)); \
453         LBR08PacketBlue((pixelpacket)); \
454         }
455
456 #define LBR08PacketRGBO(pixelpacket) \
457         { \
458         LBR08PacketRGB((pixelpacket)); \
459         LBR08PacketAlpha((pixelpacket)); \
460         }
461
462 #define LBR08PixelRed(pixel) \
463    { \
464      unsigned char lbr_bits= \
465        ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
466      SetPixelRed(image,\
467        ScaleCharToQuantum((lbr_bits)), (pixel)); \
468    }
469 #define LBR08PixelGreen(pixel) \
470    { \
471      unsigned char lbr_bits= \
472        ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
473      SetPixelGreen(image,\
474        ScaleCharToQuantum((lbr_bits)), (pixel)); \
475    }
476 #define LBR08PixelBlue(pixel) \
477    { \
478      unsigned char lbr_bits= \
479        ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
480      SetPixelBlue(image,\
481        ScaleCharToQuantum((lbr_bits)), (pixel)); \
482    }
483 #define LBR08PixelAlpha(pixel) \
484    { \
485      unsigned char lbr_bits= \
486        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
487      SetPixelAlpha(image,\
488        ScaleCharToQuantum((lbr_bits)), (pixel)); \
489    }
490
491 #define LBR08PixelRGB(pixel) \
492         { \
493         LBR08PixelRed((pixel)); \
494         LBR08PixelGreen((pixel)); \
495         LBR08PixelBlue((pixel)); \
496         }
497
498 #define LBR08PixelRGBA(pixel) \
499         { \
500         LBR08PixelRGB((pixel)); \
501         LBR08PixelAlpha((pixel)); \
502         }
503 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
504
505
506 #if MAGICKCORE_QUANTUM_DEPTH > 16
507 /* LBR16: Replicate top 16 bits */
508
509 #define LBR16PacketRed(pixelpacket) \
510    { \
511      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
512      (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
513    }
514 #define LBR16PacketGreen(pixelpacket) \
515    { \
516      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
517      (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
518    }
519 #define LBR16PacketBlue(pixelpacket) \
520    { \
521      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
522      (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
523    }
524 #define LBR16PacketAlpha(pixelpacket) \
525    { \
526      unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
527      (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
528    }
529
530 #define LBR16PacketRGB(pixelpacket) \
531         { \
532         LBR16PacketRed((pixelpacket)); \
533         LBR16PacketGreen((pixelpacket)); \
534         LBR16PacketBlue((pixelpacket)); \
535         }
536
537 #define LBR16PacketRGBO(pixelpacket) \
538         { \
539         LBR16PacketRGB((pixelpacket)); \
540         LBR16PacketAlpha((pixelpacket)); \
541         }
542
543 #define LBR16PixelRed(pixel) \
544    { \
545      unsigned short lbr_bits= \
546        ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
547      SetPixelRed(image,\
548        ScaleShortToQuantum((lbr_bits)),(pixel)); \
549    }
550 #define LBR16PixelGreen(pixel) \
551    { \
552      unsigned short lbr_bits= \
553        ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
554      SetPixelGreen(image,\
555        ScaleShortToQuantum((lbr_bits)),(pixel)); \
556    }
557 #define LBR16PixelBlue(pixel) \
558    { \
559      unsigned short lbr_bits= \
560        ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
561      SetPixelBlue(image,\
562        ScaleShortToQuantum((lbr_bits)),(pixel)); \
563    }
564 #define LBR16PixelAlpha(pixel) \
565    { \
566      unsigned short lbr_bits= \
567        ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
568      SetPixelAlpha(image,\
569        ScaleShortToQuantum((lbr_bits)),(pixel)); \
570    }
571
572 #define LBR16PixelRGB(pixel) \
573         { \
574         LBR16PixelRed((pixel)); \
575         LBR16PixelGreen((pixel)); \
576         LBR16PixelBlue((pixel)); \
577         }
578
579 #define LBR16PixelRGBA(pixel) \
580         { \
581         LBR16PixelRGB((pixel)); \
582         LBR16PixelAlpha((pixel)); \
583         }
584 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
585
586 /*
587   Establish thread safety.
588   setjmp/longjmp is claimed to be safe on these platforms:
589   setjmp/longjmp is alleged to be unsafe on these platforms:
590 */
591 #ifndef SETJMP_IS_THREAD_SAFE
592 #define PNG_SETJMP_NOT_THREAD_SAFE
593 #endif
594
595 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
596 static SemaphoreInfo
597   *ping_semaphore = (SemaphoreInfo *) NULL;
598 #endif
599
600 /*
601   This temporary until I set up malloc'ed object attributes array.
602   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
603   waste more memory.
604 */
605 #define MNG_MAX_OBJECTS 256
606
607 /*
608   If this not defined, spec is interpreted strictly.  If it is
609   defined, an attempt will be made to recover from some errors,
610   including
611       o global PLTE too short
612 */
613 #undef MNG_LOOSE
614
615 /*
616   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
617   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
618   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
619   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
620   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
621   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
622   will be enabled by default in libpng-1.2.0.
623 */
624 #ifdef PNG_MNG_FEATURES_SUPPORTED
625 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
626 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
627 #  endif
628 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
629 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
630 #  endif
631 #endif
632
633 /*
634   Maximum valid size_t in PNG/MNG chunks is (2^31)-1
635   This macro is only defined in libpng-1.0.3 and later.
636   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
637 */
638 #ifndef PNG_UINT_31_MAX
639 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
640 #endif
641
642 /*
643   Constant strings for known chunk types.  If you need to add a chunk,
644   add a string holding the name here.   To make the code more
645   portable, we use ASCII numbers like this, not characters.
646 */
647
648 static png_byte mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
649 static png_byte mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
650 static png_byte mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
651 static png_byte mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
652 static png_byte mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
653 static png_byte mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
654 static png_byte mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
655 static png_byte mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
656 static png_byte mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
657 static png_byte mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
658 static png_byte mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
659 static png_byte mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
660 static png_byte mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
661 static png_byte mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
662 static png_byte mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
663 static png_byte mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
664 static png_byte mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
665 static png_byte mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
666 static png_byte mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
667 static png_byte mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
668 static png_byte mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
669 static png_byte mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
670 static png_byte mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
671 static png_byte mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
672 static png_byte mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
673 static png_byte mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
674 static png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
675 static png_byte mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
676 static png_byte mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
677 static png_byte mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
678 static png_byte mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
679 static png_byte mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
680 static png_byte mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
681 static png_byte mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
682
683 #if defined(JNG_SUPPORTED)
684 static png_byte mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
685 static png_byte mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
686 static png_byte mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
687 static png_byte mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
688 static png_byte mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
689 static png_byte mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
690 #endif
691
692 /*
693 Other known chunks that are not yet supported by ImageMagick:
694 static png_byte mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
695 static png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
696 static png_byte mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
697 static png_byte mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
698 static png_byte mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
699 static png_byte mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
700 static png_byte mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
701 static png_byte mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
702 */
703
704 typedef struct _MngBox
705 {
706   long
707     left,
708     right,
709     top,
710     bottom;
711 } MngBox;
712
713 typedef struct _MngPair
714 {
715   volatile long
716     a,
717     b;
718 } MngPair;
719
720 #ifdef MNG_OBJECT_BUFFERS
721 typedef struct _MngBuffer
722 {
723
724   size_t
725     height,
726     width;
727
728   Image
729     *image;
730
731   png_color
732     plte[256];
733
734   int
735     reference_count;
736
737   unsigned char
738     alpha_sample_depth,
739     compression_method,
740     color_type,
741     concrete,
742     filter_method,
743     frozen,
744     image_type,
745     interlace_method,
746     pixel_sample_depth,
747     plte_length,
748     sample_depth,
749     viewable;
750 } MngBuffer;
751 #endif
752
753 typedef struct _MngInfo
754 {
755
756 #ifdef MNG_OBJECT_BUFFERS
757   MngBuffer
758     *ob[MNG_MAX_OBJECTS];
759 #endif
760
761   Image *
762     image;
763
764   RectangleInfo
765     page;
766
767   int
768     adjoin,
769 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
770     bytes_in_read_buffer,
771     found_empty_plte,
772 #endif
773     equal_backgrounds,
774     equal_chrms,
775     equal_gammas,
776 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
777     defined(PNG_MNG_FEATURES_SUPPORTED)
778     equal_palettes,
779 #endif
780     equal_physs,
781     equal_srgbs,
782     framing_mode,
783     have_global_bkgd,
784     have_global_chrm,
785     have_global_gama,
786     have_global_phys,
787     have_global_sbit,
788     have_global_srgb,
789     have_saved_bkgd_index,
790     have_write_global_chrm,
791     have_write_global_gama,
792     have_write_global_plte,
793     have_write_global_srgb,
794     need_fram,
795     object_id,
796     old_framing_mode,
797     saved_bkgd_index;
798
799   int
800     new_number_colors;
801
802   ssize_t
803     image_found,
804     loop_count[256],
805     loop_iteration[256],
806     scenes_found,
807     x_off[MNG_MAX_OBJECTS],
808     y_off[MNG_MAX_OBJECTS];
809
810   MngBox
811     clip,
812     frame,
813     image_box,
814     object_clip[MNG_MAX_OBJECTS];
815
816   unsigned char
817     /* These flags could be combined into one byte */
818     exists[MNG_MAX_OBJECTS],
819     frozen[MNG_MAX_OBJECTS],
820     loop_active[256],
821     invisible[MNG_MAX_OBJECTS],
822     viewable[MNG_MAX_OBJECTS];
823
824   MagickOffsetType
825     loop_jump[256];
826
827   png_colorp
828     global_plte;
829
830   png_color_8
831     global_sbit;
832
833   png_byte
834 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
835     read_buffer[8],
836 #endif
837     global_trns[256];
838
839   float
840     global_gamma;
841
842   ChromaticityInfo
843     global_chrm;
844
845   RenderingIntent
846     global_srgb_intent;
847
848   unsigned int
849     delay,
850     global_plte_length,
851     global_trns_length,
852     global_x_pixels_per_unit,
853     global_y_pixels_per_unit,
854     mng_width,
855     mng_height,
856     ticks_per_second;
857
858   MagickBooleanType
859     need_blob;
860
861   unsigned int
862     IsPalette,
863     global_phys_unit_type,
864     basi_warning,
865     clon_warning,
866     dhdr_warning,
867     jhdr_warning,
868     magn_warning,
869     past_warning,
870     phyg_warning,
871     phys_warning,
872     sbit_warning,
873     show_warning,
874     mng_type,
875     write_mng,
876     write_png_colortype,
877     write_png_depth,
878     write_png_compression_level,
879     write_png_compression_strategy,
880     write_png_compression_filter,
881     write_png8,
882     write_png24,
883     write_png32,
884     write_png48,
885     write_png64;
886
887 #ifdef MNG_BASI_SUPPORTED
888   size_t
889     basi_width,
890     basi_height;
891
892   unsigned int
893     basi_depth,
894     basi_color_type,
895     basi_compression_method,
896     basi_filter_type,
897     basi_interlace_method,
898     basi_red,
899     basi_green,
900     basi_blue,
901     basi_alpha,
902     basi_viewable;
903 #endif
904
905   png_uint_16
906     magn_first,
907     magn_last,
908     magn_mb,
909     magn_ml,
910     magn_mr,
911     magn_mt,
912     magn_mx,
913     magn_my,
914     magn_methx,
915     magn_methy;
916
917   PixelInfo
918     mng_global_bkgd;
919
920   /* Added at version 6.6.6-7 */
921   MagickBooleanType
922     ping_exclude_bKGD,
923     ping_exclude_cHRM,
924     ping_exclude_date,
925     ping_exclude_EXIF,
926     ping_exclude_gAMA,
927     ping_exclude_iCCP,
928     /* ping_exclude_iTXt, */
929     ping_exclude_oFFs,
930     ping_exclude_pHYs,
931     ping_exclude_sRGB,
932     ping_exclude_tEXt,
933     ping_exclude_tRNS,
934     ping_exclude_vpAg,
935     ping_exclude_zCCP, /* hex-encoded iCCP */
936     ping_exclude_zTXt,
937     ping_preserve_colormap;
938
939 } MngInfo;
940 #endif /* VER */
941 \f
942 /*
943   Forward declarations.
944 */
945 static MagickBooleanType
946   WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
947
948 static MagickBooleanType
949   WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
950
951 #if defined(JNG_SUPPORTED)
952 static MagickBooleanType
953   WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
954 #endif
955
956 #if PNG_LIBPNG_VER > 10011
957
958
959 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
960 static MagickBooleanType
961 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
962 {
963     /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
964      *
965      * This is true if the high byte and the next highest byte of
966      * each sample of the image, the colormap, and the background color
967      * are equal to each other.  We check this by seeing if the samples
968      * are unchanged when we scale them down to 8 and back up to Quantum.
969      *
970      * We don't use the method GetImageDepth() because it doesn't check
971      * background and doesn't handle PseudoClass specially.
972      */
973
974 #define QuantumToCharToQuantumEqQuantum(quantum) \
975   ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
976
977     MagickBooleanType
978       ok_to_reduce=MagickFalse;
979
980     if (image->depth >= 16)
981       {
982
983         const Quantum
984           *p;
985
986         ok_to_reduce=
987            QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
988            QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
989            QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
990            MagickTrue : MagickFalse;
991
992         if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
993           {
994             int indx;
995
996             for (indx=0; indx < (ssize_t) image->colors; indx++)
997               {
998                 ok_to_reduce=(
999                    QuantumToCharToQuantumEqQuantum(
1000                    image->colormap[indx].red) &&
1001                    QuantumToCharToQuantumEqQuantum(
1002                    image->colormap[indx].green) &&
1003                    QuantumToCharToQuantumEqQuantum(
1004                    image->colormap[indx].blue)) ?
1005                    MagickTrue : MagickFalse;
1006
1007                 if (ok_to_reduce == MagickFalse)
1008                    break;
1009               }
1010           }
1011
1012         if ((ok_to_reduce != MagickFalse) &&
1013             (image->storage_class != PseudoClass))
1014           {
1015             ssize_t
1016               y;
1017
1018             register ssize_t
1019               x;
1020
1021             for (y=0; y < (ssize_t) image->rows; y++)
1022             {
1023               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1024
1025               if (p == (const Quantum *) NULL)
1026                 {
1027                   ok_to_reduce = MagickFalse;
1028                   break;
1029                 }
1030
1031               for (x=(ssize_t) image->columns-1; x >= 0; x--)
1032               {
1033                 ok_to_reduce=
1034                    QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1035                    QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1036                    QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
1037                    MagickTrue : MagickFalse;
1038
1039                 if (ok_to_reduce == MagickFalse)
1040                   break;
1041
1042                 p+=GetPixelChannels(image);
1043               }
1044               if (x >= 0)
1045                 break;
1046             }
1047           }
1048
1049         if (ok_to_reduce != MagickFalse)
1050           {
1051             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1052                 "    OK to reduce PNG bit depth to 8 without loss of info");
1053           }
1054         else
1055           {
1056             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1057                 "    Not OK to reduce PNG bit depth to 8 without loss of info");
1058           }
1059       }
1060
1061     return ok_to_reduce;
1062 }
1063 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1064
1065 static const char* PngColorTypeToString(const unsigned int color_type)
1066 {
1067   const char
1068     *result = "Unknown";
1069
1070   switch (color_type)
1071     {
1072     case PNG_COLOR_TYPE_GRAY:
1073       result = "Gray";
1074       break;
1075     case PNG_COLOR_TYPE_GRAY_ALPHA:
1076       result = "Gray+Alpha";
1077       break;
1078     case PNG_COLOR_TYPE_PALETTE:
1079       result = "Palette";
1080       break;
1081     case PNG_COLOR_TYPE_RGB:
1082       result = "RGB";
1083       break;
1084     case PNG_COLOR_TYPE_RGB_ALPHA:
1085       result = "RGB+Alpha";
1086       break;
1087     }
1088
1089   return result;
1090 }
1091
1092 static int
1093 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1094 {
1095   switch (intent)
1096   {
1097     case PerceptualIntent:
1098        return 0;
1099
1100     case RelativeIntent:
1101        return 1;
1102
1103     case SaturationIntent:
1104        return 2;
1105
1106     case AbsoluteIntent:
1107        return 3;
1108
1109     default:
1110        return -1;
1111   }
1112 }
1113
1114 static RenderingIntent
1115 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1116 {
1117   switch (ping_intent)
1118   {
1119     case 0:
1120       return PerceptualIntent;
1121
1122     case 1:
1123       return RelativeIntent;
1124
1125     case 2:
1126       return SaturationIntent;
1127
1128     case 3:
1129       return AbsoluteIntent;
1130
1131     default:
1132       return UndefinedIntent;
1133     }
1134 }
1135
1136 static const char *
1137 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1138 {
1139   switch (ping_intent)
1140   {
1141     case 0:
1142       return "Perceptual Intent";
1143
1144     case 1:
1145       return "Relative Intent";
1146
1147     case 2:
1148       return "Saturation Intent";
1149
1150     case 3:
1151       return "Absolute Intent";
1152
1153     default:
1154       return "Undefined Intent";
1155     }
1156 }
1157
1158 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1159 {
1160   if (x > y)
1161     return(x);
1162
1163   return(y);
1164 }
1165
1166 static const char *
1167 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1168 {
1169   switch (ping_colortype)
1170   {
1171     case 0:
1172       return "Grayscale";
1173
1174     case 2:
1175       return "Truecolor";
1176
1177     case 3:
1178       return "Indexed";
1179
1180     case 4:
1181       return "GrayAlpha";
1182
1183     case 6:
1184       return "RGBA";
1185
1186     default:
1187       return "UndefinedColorType";
1188     }
1189 }
1190
1191
1192 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1193 {
1194   if (x < y)
1195     return(x);
1196
1197   return(y);
1198 }
1199 #endif /* PNG_LIBPNG_VER > 10011 */
1200 #endif /* MAGICKCORE_PNG_DELEGATE */
1201 \f
1202 /*
1203 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1204 %                                                                             %
1205 %                                                                             %
1206 %                                                                             %
1207 %   I s M N G                                                                 %
1208 %                                                                             %
1209 %                                                                             %
1210 %                                                                             %
1211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1212 %
1213 %  IsMNG() returns MagickTrue if the image format type, identified by the
1214 %  magick string, is MNG.
1215 %
1216 %  The format of the IsMNG method is:
1217 %
1218 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1219 %
1220 %  A description of each parameter follows:
1221 %
1222 %    o magick: compare image format pattern against these bytes.
1223 %
1224 %    o length: Specifies the length of the magick string.
1225 %
1226 %
1227 */
1228 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1229 {
1230   if (length < 8)
1231     return(MagickFalse);
1232
1233   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1234     return(MagickTrue);
1235
1236   return(MagickFalse);
1237 }
1238 \f
1239 /*
1240 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1241 %                                                                             %
1242 %                                                                             %
1243 %                                                                             %
1244 %   I s J N G                                                                 %
1245 %                                                                             %
1246 %                                                                             %
1247 %                                                                             %
1248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1249 %
1250 %  IsJNG() returns MagickTrue if the image format type, identified by the
1251 %  magick string, is JNG.
1252 %
1253 %  The format of the IsJNG method is:
1254 %
1255 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1256 %
1257 %  A description of each parameter follows:
1258 %
1259 %    o magick: compare image format pattern against these bytes.
1260 %
1261 %    o length: Specifies the length of the magick string.
1262 %
1263 %
1264 */
1265 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1266 {
1267   if (length < 8)
1268     return(MagickFalse);
1269
1270   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1271     return(MagickTrue);
1272
1273   return(MagickFalse);
1274 }
1275 \f
1276 /*
1277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1278 %                                                                             %
1279 %                                                                             %
1280 %                                                                             %
1281 %   I s P N G                                                                 %
1282 %                                                                             %
1283 %                                                                             %
1284 %                                                                             %
1285 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1286 %
1287 %  IsPNG() returns MagickTrue if the image format type, identified by the
1288 %  magick string, is PNG.
1289 %
1290 %  The format of the IsPNG method is:
1291 %
1292 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1293 %
1294 %  A description of each parameter follows:
1295 %
1296 %    o magick: compare image format pattern against these bytes.
1297 %
1298 %    o length: Specifies the length of the magick string.
1299 %
1300 */
1301 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1302 {
1303   if (length < 8)
1304     return(MagickFalse);
1305
1306   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1307     return(MagickTrue);
1308
1309   return(MagickFalse);
1310 }
1311 \f
1312 #if defined(MAGICKCORE_PNG_DELEGATE)
1313 #if defined(__cplusplus) || defined(c_plusplus)
1314 extern "C" {
1315 #endif
1316
1317 #if (PNG_LIBPNG_VER > 10011)
1318 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1319 {
1320   unsigned char
1321     buffer[4];
1322
1323   assert(image != (Image *) NULL);
1324   assert(image->signature == MagickSignature);
1325   buffer[0]=(unsigned char) (value >> 24);
1326   buffer[1]=(unsigned char) (value >> 16);
1327   buffer[2]=(unsigned char) (value >> 8);
1328   buffer[3]=(unsigned char) value;
1329   return((size_t) WriteBlob(image,4,buffer));
1330 }
1331
1332 static void PNGLong(png_bytep p,png_uint_32 value)
1333 {
1334   *p++=(png_byte) ((value >> 24) & 0xff);
1335   *p++=(png_byte) ((value >> 16) & 0xff);
1336   *p++=(png_byte) ((value >> 8) & 0xff);
1337   *p++=(png_byte) (value & 0xff);
1338 }
1339
1340 #if defined(JNG_SUPPORTED)
1341 static void PNGsLong(png_bytep p,png_int_32 value)
1342 {
1343   *p++=(png_byte) ((value >> 24) & 0xff);
1344   *p++=(png_byte) ((value >> 16) & 0xff);
1345   *p++=(png_byte) ((value >> 8) & 0xff);
1346   *p++=(png_byte) (value & 0xff);
1347 }
1348 #endif
1349
1350 static void PNGShort(png_bytep p,png_uint_16 value)
1351 {
1352   *p++=(png_byte) ((value >> 8) & 0xff);
1353   *p++=(png_byte) (value & 0xff);
1354 }
1355
1356 static void PNGType(png_bytep p,png_bytep type)
1357 {
1358   (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1359 }
1360
1361 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1362    size_t length)
1363 {
1364   if (logging != MagickFalse)
1365     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1366       "  Writing %c%c%c%c chunk, length: %.20g",
1367       type[0],type[1],type[2],type[3],(double) length);
1368 }
1369 #endif /* PNG_LIBPNG_VER > 10011 */
1370
1371 #if defined(__cplusplus) || defined(c_plusplus)
1372 }
1373 #endif
1374
1375 #if PNG_LIBPNG_VER > 10011
1376 /*
1377 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1378 %                                                                             %
1379 %                                                                             %
1380 %                                                                             %
1381 %   R e a d P N G I m a g e                                                   %
1382 %                                                                             %
1383 %                                                                             %
1384 %                                                                             %
1385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1386 %
1387 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1388 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1389 %  allocates the memory necessary for the new Image structure and returns a
1390 %  pointer to the new image or set of images.
1391 %
1392 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1393 %
1394 %  The format of the ReadPNGImage method is:
1395 %
1396 %      Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1397 %
1398 %  A description of each parameter follows:
1399 %
1400 %    o image_info: the image info.
1401 %
1402 %    o exception: return any errors or warnings in this structure.
1403 %
1404 %  To do, more or less in chronological order (as of version 5.5.2,
1405 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1406 %
1407 %    Get 16-bit cheap transparency working.
1408 %
1409 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1410 %
1411 %    Preserve all unknown and not-yet-handled known chunks found in input
1412 %    PNG file and copy them into output PNG files according to the PNG
1413 %    copying rules.
1414 %
1415 %    (At this point, PNG encoding should be in full MNG compliance)
1416 %
1417 %    Provide options for choice of background to use when the MNG BACK
1418 %    chunk is not present or is not mandatory (i.e., leave transparent,
1419 %    user specified, MNG BACK, PNG bKGD)
1420 %
1421 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1422 %    efficiently by linking in the duplicate frames.].
1423 %
1424 %    Decode and act on the MHDR simplicity profile (offer option to reject
1425 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1426 %
1427 %    Upgrade to full MNG without Delta-PNG.
1428 %
1429 %        o  BACK [done a while ago except for background image ID]
1430 %        o  MOVE [done 15 May 1999]
1431 %        o  CLIP [done 15 May 1999]
1432 %        o  DISC [done 19 May 1999]
1433 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1434 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1435 %        o  SHOW
1436 %        o  PAST
1437 %        o  BASI
1438 %        o  MNG-level tEXt/iTXt/zTXt
1439 %        o  pHYg
1440 %        o  pHYs
1441 %        o  sBIT
1442 %        o  bKGD
1443 %        o  iTXt (wait for libpng implementation).
1444 %
1445 %    Use the scene signature to discover when an identical scene is
1446 %    being reused, and just point to the original image->exception instead
1447 %    of storing another set of pixels.  This not specific to MNG
1448 %    but could be applied generally.
1449 %
1450 %    Upgrade to full MNG with Delta-PNG.
1451 %
1452 %    JNG tEXt/iTXt/zTXt
1453 %
1454 %    We will not attempt to read files containing the CgBI chunk.
1455 %    They are really Xcode files meant for display on the iPhone.
1456 %    These are not valid PNG files and it is impossible to recover
1457 %    the original PNG from files that have been converted to Xcode-PNG,
1458 %    since irretrievable loss of color data has occurred due to the
1459 %    use of premultiplied alpha.
1460 */
1461
1462 #if defined(__cplusplus) || defined(c_plusplus)
1463 extern "C" {
1464 #endif
1465
1466 /*
1467   This the function that does the actual reading of data.  It is
1468   the same as the one supplied in libpng, except that it receives the
1469   datastream from the ReadBlob() function instead of standard input.
1470 */
1471 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1472 {
1473   Image
1474     *image;
1475
1476   image=(Image *) png_get_io_ptr(png_ptr);
1477   if (length)
1478     {
1479       png_size_t
1480         check;
1481
1482       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1483       if (check != length)
1484         {
1485           char
1486             msg[MaxTextExtent];
1487
1488           (void) FormatLocaleString(msg,MaxTextExtent,
1489             "Expected %.20g bytes; found %.20g bytes",(double) length,
1490             (double) check);
1491           png_warning(png_ptr,msg);
1492           png_error(png_ptr,"Read Exception");
1493         }
1494     }
1495 }
1496
1497 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1498     !defined(PNG_MNG_FEATURES_SUPPORTED)
1499 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1500  * older than libpng-1.0.3a, which was the first to allow the empty
1501  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1502  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1503  * encountered after an empty PLTE, so we have to look ahead for bKGD
1504  * chunks and remove them from the datastream that is passed to libpng,
1505  * and store their contents for later use.
1506  */
1507 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1508 {
1509   MngInfo
1510     *mng_info;
1511
1512   Image
1513     *image;
1514
1515   png_size_t
1516     check;
1517
1518   register ssize_t
1519     i;
1520
1521   i=0;
1522   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1523   image=(Image *) mng_info->image;
1524   while (mng_info->bytes_in_read_buffer && length)
1525   {
1526     data[i]=mng_info->read_buffer[i];
1527     mng_info->bytes_in_read_buffer--;
1528     length--;
1529     i++;
1530   }
1531   if (length)
1532     {
1533       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1534
1535       if (check != length)
1536         png_error(png_ptr,"Read Exception");
1537
1538       if (length == 4)
1539         {
1540           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1541               (data[3] == 0))
1542             {
1543               check=(png_size_t) ReadBlob(image,(size_t) length,
1544                 (char *) mng_info->read_buffer);
1545               mng_info->read_buffer[4]=0;
1546               mng_info->bytes_in_read_buffer=4;
1547               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1548                 mng_info->found_empty_plte=MagickTrue;
1549               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1550                 {
1551                   mng_info->found_empty_plte=MagickFalse;
1552                   mng_info->have_saved_bkgd_index=MagickFalse;
1553                 }
1554             }
1555
1556           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1557               (data[3] == 1))
1558             {
1559               check=(png_size_t) ReadBlob(image,(size_t) length,
1560                 (char *) mng_info->read_buffer);
1561               mng_info->read_buffer[4]=0;
1562               mng_info->bytes_in_read_buffer=4;
1563               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1564                 if (mng_info->found_empty_plte)
1565                   {
1566                     /*
1567                       Skip the bKGD data byte and CRC.
1568                     */
1569                     check=(png_size_t)
1570                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1571                     check=(png_size_t) ReadBlob(image,(size_t) length,
1572                       (char *) mng_info->read_buffer);
1573                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1574                     mng_info->have_saved_bkgd_index=MagickTrue;
1575                     mng_info->bytes_in_read_buffer=0;
1576                   }
1577             }
1578         }
1579     }
1580 }
1581 #endif
1582
1583 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1584 {
1585   Image
1586     *image;
1587
1588   image=(Image *) png_get_io_ptr(png_ptr);
1589   if (length)
1590     {
1591       png_size_t
1592         check;
1593
1594       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1595
1596       if (check != length)
1597         png_error(png_ptr,"WriteBlob Failed");
1598     }
1599 }
1600
1601 static void png_flush_data(png_structp png_ptr)
1602 {
1603   (void) png_ptr;
1604 }
1605
1606 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1607 static int PalettesAreEqual(Image *a,Image *b)
1608 {
1609   ssize_t
1610     i;
1611
1612   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1613     return((int) MagickFalse);
1614
1615   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1616     return((int) MagickFalse);
1617
1618   if (a->colors != b->colors)
1619     return((int) MagickFalse);
1620
1621   for (i=0; i < (ssize_t) a->colors; i++)
1622   {
1623     if ((a->colormap[i].red != b->colormap[i].red) ||
1624         (a->colormap[i].green != b->colormap[i].green) ||
1625         (a->colormap[i].blue != b->colormap[i].blue))
1626       return((int) MagickFalse);
1627   }
1628
1629   return((int) MagickTrue);
1630 }
1631 #endif
1632
1633 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1634 {
1635   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1636       mng_info->exists[i] && !mng_info->frozen[i])
1637     {
1638 #ifdef MNG_OBJECT_BUFFERS
1639       if (mng_info->ob[i] != (MngBuffer *) NULL)
1640         {
1641           if (mng_info->ob[i]->reference_count > 0)
1642             mng_info->ob[i]->reference_count--;
1643
1644           if (mng_info->ob[i]->reference_count == 0)
1645             {
1646               if (mng_info->ob[i]->image != (Image *) NULL)
1647                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1648
1649               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1650             }
1651         }
1652       mng_info->ob[i]=(MngBuffer *) NULL;
1653 #endif
1654       mng_info->exists[i]=MagickFalse;
1655       mng_info->invisible[i]=MagickFalse;
1656       mng_info->viewable[i]=MagickFalse;
1657       mng_info->frozen[i]=MagickFalse;
1658       mng_info->x_off[i]=0;
1659       mng_info->y_off[i]=0;
1660       mng_info->object_clip[i].left=0;
1661       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1662       mng_info->object_clip[i].top=0;
1663       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1664     }
1665 }
1666
1667 static void MngInfoFreeStruct(MngInfo *mng_info,
1668     MagickBooleanType *have_mng_structure)
1669 {
1670   if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1671     {
1672       register ssize_t
1673         i;
1674
1675       for (i=1; i < MNG_MAX_OBJECTS; i++)
1676         MngInfoDiscardObject(mng_info,i);
1677
1678       if (mng_info->global_plte != (png_colorp) NULL)
1679         mng_info->global_plte=(png_colorp)
1680           RelinquishMagickMemory(mng_info->global_plte);
1681
1682       mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1683       *have_mng_structure=MagickFalse;
1684     }
1685 }
1686
1687 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1688 {
1689   MngBox
1690     box;
1691
1692   box=box1;
1693   if (box.left < box2.left)
1694     box.left=box2.left;
1695
1696   if (box.top < box2.top)
1697     box.top=box2.top;
1698
1699   if (box.right > box2.right)
1700     box.right=box2.right;
1701
1702   if (box.bottom > box2.bottom)
1703     box.bottom=box2.bottom;
1704
1705   return box;
1706 }
1707
1708 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1709 {
1710    MngBox
1711       box;
1712
1713   /*
1714     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1715   */
1716   box.left=(ssize_t) ((p[0]  << 24) | (p[1]  << 16) | (p[2]  << 8) | p[3]);
1717   box.right=(ssize_t) ((p[4]  << 24) | (p[5]  << 16) | (p[6]  << 8) | p[7]);
1718   box.top=(ssize_t) ((p[8]  << 24) | (p[9]  << 16) | (p[10] << 8) | p[11]);
1719   box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1720   if (delta_type != 0)
1721     {
1722       box.left+=previous_box.left;
1723       box.right+=previous_box.right;
1724       box.top+=previous_box.top;
1725       box.bottom+=previous_box.bottom;
1726     }
1727
1728   return(box);
1729 }
1730
1731 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1732   unsigned char *p)
1733 {
1734   MngPair
1735     pair;
1736   /*
1737     Read two ssize_ts from CLON, MOVE or PAST chunk
1738   */
1739   pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1740   pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1741
1742   if (delta_type != 0)
1743     {
1744       pair.a+=previous_pair.a;
1745       pair.b+=previous_pair.b;
1746     }
1747
1748   return(pair);
1749 }
1750
1751 static long mng_get_long(unsigned char *p)
1752 {
1753   return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1754 }
1755
1756 typedef struct _PNGErrorInfo
1757 {
1758   Image
1759     *image;
1760
1761   ExceptionInfo
1762     *exception;
1763 } PNGErrorInfo;
1764
1765 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1766 {
1767   ExceptionInfo
1768     *exception;
1769
1770   Image
1771     *image;
1772
1773   PNGErrorInfo
1774     *error_info;
1775
1776   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1777   image=error_info->image;
1778   exception=error_info->exception;
1779
1780   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781     "  libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1782
1783   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1784     "`%s'",image->filename);
1785
1786 #if (PNG_LIBPNG_VER < 10500)
1787   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1788    * are building with libpng-1.4.x and can be ignored.
1789    */
1790   longjmp(ping->jmpbuf,1);
1791 #else
1792   png_longjmp(ping,1);
1793 #endif
1794 }
1795
1796 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1797 {
1798   ExceptionInfo
1799     *exception;
1800
1801   Image
1802     *image;
1803
1804   PNGErrorInfo
1805     *error_info;
1806
1807   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1808     png_error(ping, message);
1809
1810   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1811   image=error_info->image;
1812   exception=error_info->exception;
1813   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1814     "  libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1815
1816   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1817     message,"`%s'",image->filename);
1818 }
1819
1820 #ifdef PNG_USER_MEM_SUPPORTED
1821 #if PNG_LIBPNG_VER >= 14000
1822 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1823 #else
1824 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1825 #endif
1826 {
1827   (void) png_ptr;
1828   return((png_voidp) AcquireMagickMemory((size_t) size));
1829 }
1830
1831 /*
1832   Free a pointer.  It is removed from the list at the same time.
1833 */
1834 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1835 {
1836   (void) png_ptr;
1837   ptr=RelinquishMagickMemory(ptr);
1838   return((png_free_ptr) NULL);
1839 }
1840 #endif
1841
1842 #if defined(__cplusplus) || defined(c_plusplus)
1843 }
1844 #endif
1845
1846 static int
1847 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1848    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1849 {
1850   register ssize_t
1851     i;
1852
1853   register unsigned char
1854     *dp;
1855
1856   register png_charp
1857     sp;
1858
1859   png_uint_32
1860     length,
1861     nibbles;
1862
1863   StringInfo
1864     *profile;
1865
1866   const unsigned char
1867     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1868                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1869                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1870                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1871                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1872                  13,14,15};
1873
1874   sp=text[ii].text+1;
1875   /* look for newline */
1876   while (*sp != '\n')
1877      sp++;
1878
1879   /* look for length */
1880   while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1881      sp++;
1882
1883   length=(png_uint_32) StringToLong(sp);
1884
1885   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1886        "      length: %lu",(unsigned long) length);
1887
1888   while (*sp != ' ' && *sp != '\n')
1889      sp++;
1890
1891   /* allocate space */
1892   if (length == 0)
1893   {
1894     png_warning(ping,"invalid profile length");
1895     return(MagickFalse);
1896   }
1897
1898   profile=BlobToStringInfo((const void *) NULL,length);
1899
1900   if (profile == (StringInfo *) NULL)
1901   {
1902     png_warning(ping, "unable to copy profile");
1903     return(MagickFalse);
1904   }
1905
1906   /* copy profile, skipping white space and column 1 "=" signs */
1907   dp=GetStringInfoDatum(profile);
1908   nibbles=length*2;
1909
1910   for (i=0; i < (ssize_t) nibbles; i++)
1911   {
1912     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1913     {
1914       if (*sp == '\0')
1915         {
1916           png_warning(ping, "ran out of profile data");
1917           profile=DestroyStringInfo(profile);
1918           return(MagickFalse);
1919         }
1920       sp++;
1921     }
1922
1923     if (i%2 == 0)
1924       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1925
1926     else
1927       (*dp++)+=unhex[(int) *sp++];
1928   }
1929   /*
1930     We have already read "Raw profile type.
1931   */
1932   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1933   profile=DestroyStringInfo(profile);
1934
1935   if (image_info->verbose)
1936     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1937
1938   return MagickTrue;
1939 }
1940
1941 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1942 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1943 {
1944   Image
1945     *image;
1946
1947
1948   /* The unknown chunk structure contains the chunk data:
1949      png_byte name[5];
1950      png_byte *data;
1951      png_size_t size;
1952
1953      Note that libpng has already taken care of the CRC handling.
1954   */
1955
1956   LogMagickEvent(CoderEvent,GetMagickModule(),
1957      " read_vpag_chunk: found %c%c%c%c chunk",
1958        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1959
1960   if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1961       chunk->name[2] != 65 ||chunk-> name[3] != 103)
1962     return(0); /* Did not recognize */
1963
1964   /* recognized vpAg */
1965
1966   if (chunk->size != 9)
1967     return(-1); /* Error return */
1968
1969   if (chunk->data[8] != 0)
1970     return(0);  /* ImageMagick requires pixel units */
1971
1972   image=(Image *) png_get_user_chunk_ptr(ping);
1973
1974   image->page.width=(size_t) ((chunk->data[0] << 24) |
1975      (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1976
1977   image->page.height=(size_t) ((chunk->data[4] << 24) |
1978      (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1979
1980   /* Return one of the following: */
1981      /* return(-n);  chunk had an error */
1982      /* return(0);  did not recognize */
1983      /* return(n);  success */
1984
1985   return(1);
1986
1987 }
1988 #endif
1989
1990 /*
1991 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1992 %                                                                             %
1993 %                                                                             %
1994 %                                                                             %
1995 %   R e a d O n e P N G I m a g e                                             %
1996 %                                                                             %
1997 %                                                                             %
1998 %                                                                             %
1999 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2000 %
2001 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2002 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
2003 %  necessary for the new Image structure and returns a pointer to the new
2004 %  image.
2005 %
2006 %  The format of the ReadOnePNGImage method is:
2007 %
2008 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2009 %         ExceptionInfo *exception)
2010 %
2011 %  A description of each parameter follows:
2012 %
2013 %    o mng_info: Specifies a pointer to a MngInfo structure.
2014 %
2015 %    o image_info: the image info.
2016 %
2017 %    o exception: return any errors or warnings in this structure.
2018 %
2019 */
2020 static Image *ReadOnePNGImage(MngInfo *mng_info,
2021     const ImageInfo *image_info, ExceptionInfo *exception)
2022 {
2023   /* Read one PNG image */
2024
2025   /* To do: Read the tIME chunk into the date:modify property */
2026   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2027
2028   Image
2029     *image;
2030
2031   int
2032     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2033     num_raw_profiles,
2034     num_text,
2035     num_text_total,
2036     num_passes,
2037     number_colors,
2038     pass,
2039     ping_bit_depth,
2040     ping_color_type,
2041     ping_file_depth,
2042     ping_interlace_method,
2043     ping_compression_method,
2044     ping_filter_method,
2045     ping_num_trans,
2046     unit_type;
2047
2048   double
2049     file_gamma;
2050
2051   MagickBooleanType
2052     logging,
2053     ping_found_cHRM,
2054     ping_found_gAMA,
2055     ping_found_iCCP,
2056     ping_found_sRGB,
2057     ping_found_sRGB_cHRM,
2058     status;
2059
2060   PixelInfo
2061     transparent_color;
2062
2063   PNGErrorInfo
2064     error_info;
2065
2066   png_bytep
2067      ping_trans_alpha;
2068
2069   png_color_16p
2070      ping_background,
2071      ping_trans_color;
2072
2073   png_info
2074     *end_info,
2075     *ping_info;
2076
2077   png_struct
2078     *ping;
2079
2080   png_textp
2081     text;
2082
2083   png_uint_32
2084     ping_height,
2085     ping_width,
2086     x_resolution,
2087     y_resolution;
2088
2089   QuantumInfo
2090     *quantum_info;
2091
2092   ssize_t
2093     ping_rowbytes,
2094     y;
2095
2096   register unsigned char
2097     *p;
2098
2099   register ssize_t
2100     i,
2101     x;
2102
2103   register Quantum
2104     *q;
2105
2106   size_t
2107     length,
2108     row_offset;
2109
2110   ssize_t
2111     j;
2112
2113   unsigned char
2114     *volatile ping_pixels;
2115
2116 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2117   png_byte unused_chunks[]=
2118   {
2119     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2120     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2121     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2122     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2123     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2124     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2125 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2126                           /* ignore the APNG chunks */
2127      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2128     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2129     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2130 #endif
2131   };
2132 #endif
2133
2134   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2135     "  Enter ReadOnePNGImage()");
2136
2137 #if (PNG_LIBPNG_VER < 10200)
2138   if (image_info->verbose)
2139     printf("Your PNG library (libpng-%s) is rather old.\n",
2140        PNG_LIBPNG_VER_STRING);
2141 #endif
2142
2143 #if (PNG_LIBPNG_VER >= 10400)
2144 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
2145   if (image_info->verbose)
2146     {
2147       printf("Your PNG library (libpng-%s) is an old beta version.\n",
2148            PNG_LIBPNG_VER_STRING);
2149       printf("Please update it.\n");
2150     }
2151 #  endif
2152 #endif
2153
2154
2155   quantum_info = (QuantumInfo *) NULL;
2156   image=mng_info->image;
2157
2158   if (logging != MagickFalse)
2159   {
2160     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2161       " Before reading:");
2162
2163     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2164       "  image->alpha_trait=%d",(int) image->alpha_trait);
2165
2166     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2167       "  image->rendering_intent=%d",(int) image->rendering_intent);
2168
2169     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2170       "  image->colorspace=%d",(int) image->colorspace);
2171
2172     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2173       "  image->gamma=%f", image->gamma);
2174   }
2175   intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2176
2177   /* Set to an out-of-range color unless tRNS chunk is present */
2178   transparent_color.red=65537;
2179   transparent_color.green=65537;
2180   transparent_color.blue=65537;
2181   transparent_color.alpha=65537;
2182
2183   number_colors=0;
2184   num_text = 0;
2185   num_text_total = 0;
2186   num_raw_profiles = 0;
2187
2188   ping_found_cHRM = MagickFalse;
2189   ping_found_gAMA = MagickFalse;
2190   ping_found_iCCP = MagickFalse;
2191   ping_found_sRGB = MagickFalse;
2192
2193   /*
2194     Allocate the PNG structures
2195   */
2196 #ifdef PNG_USER_MEM_SUPPORTED
2197  error_info.image=image;
2198  error_info.exception=exception;
2199  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2200    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2201    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2202 #else
2203   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2204     MagickPNGErrorHandler,MagickPNGWarningHandler);
2205 #endif
2206   if (ping == (png_struct *) NULL)
2207     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2208
2209   ping_info=png_create_info_struct(ping);
2210
2211   if (ping_info == (png_info *) NULL)
2212     {
2213       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2214       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2215     }
2216
2217   end_info=png_create_info_struct(ping);
2218
2219   if (end_info == (png_info *) NULL)
2220     {
2221       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2222       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2223     }
2224
2225   ping_pixels=(unsigned char *) NULL;
2226
2227   if (setjmp(png_jmpbuf(ping)))
2228     {
2229       /*
2230         PNG image is corrupt.
2231       */
2232       png_destroy_read_struct(&ping,&ping_info,&end_info);
2233
2234 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2235       UnlockSemaphoreInfo(ping_semaphore);
2236 #endif
2237
2238       if (ping_pixels != (unsigned char *) NULL)
2239         ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2240
2241       if (logging != MagickFalse)
2242         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2243           "  exit ReadOnePNGImage() with error.");
2244
2245       if (image != (Image *) NULL)
2246         {
2247           InheritException(exception,exception);
2248           image->columns=0;
2249         }
2250
2251       return(GetFirstImageInList(image));
2252     }
2253
2254   /* {  For navigation to end of SETJMP-protected block.  Within this
2255    *    block, use png_error() instead of Throwing an Exception, to ensure
2256    *    that libpng is able to clean up, and that the semaphore is unlocked.
2257    */
2258
2259 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
2260   LockSemaphoreInfo(ping_semaphore);
2261 #endif
2262
2263   /*
2264     Prepare PNG for reading.
2265   */
2266
2267   mng_info->image_found++;
2268   png_set_sig_bytes(ping,8);
2269
2270   if (LocaleCompare(image_info->magick,"MNG") == 0)
2271     {
2272 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2273       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2274       png_set_read_fn(ping,image,png_get_data);
2275 #else
2276 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2277       png_permit_empty_plte(ping,MagickTrue);
2278       png_set_read_fn(ping,image,png_get_data);
2279 #else
2280       mng_info->image=image;
2281       mng_info->bytes_in_read_buffer=0;
2282       mng_info->found_empty_plte=MagickFalse;
2283       mng_info->have_saved_bkgd_index=MagickFalse;
2284       png_set_read_fn(ping,mng_info,mng_get_data);
2285 #endif
2286 #endif
2287     }
2288
2289   else
2290     png_set_read_fn(ping,image,png_get_data);
2291
2292 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2293   /* Ignore unused chunks and all unknown chunks except for vpAg */
2294 #if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2295   png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2296 #else
2297   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2298 #endif
2299   png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2300   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2301      (int)sizeof(unused_chunks)/5);
2302   /* Callback for other unknown chunks */
2303   png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2304 #endif
2305
2306 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2307 #  if (PNG_LIBPNG_VER >= 10400)
2308     /* Limit the size of the chunk storage cache used for sPLT, text,
2309      * and unknown chunks.
2310      */
2311     png_set_chunk_cache_max(ping, 32767);
2312 #  endif
2313 #endif
2314
2315 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2316     /* Disable new libpng-1.5.10 feature */
2317     png_set_check_for_invalid_index (ping, 0);
2318 #endif
2319
2320 #if (PNG_LIBPNG_VER < 10400)
2321 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2322    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2323   /* Disable thread-unsafe features of pnggccrd */
2324   if (png_access_version_number() >= 10200)
2325   {
2326     png_uint_32 mmx_disable_mask=0;
2327     png_uint_32 asm_flags;
2328
2329     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2330                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2331                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2332                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2333     asm_flags=png_get_asm_flags(ping);
2334     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2335   }
2336 #  endif
2337 #endif
2338
2339   png_read_info(ping,ping_info);
2340
2341   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2342                &ping_bit_depth,&ping_color_type,
2343                &ping_interlace_method,&ping_compression_method,
2344                &ping_filter_method);
2345
2346   ping_file_depth = ping_bit_depth;
2347
2348   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2349   {
2350       char
2351         msg[MaxTextExtent];
2352
2353       (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2354       (void) SetImageProperty(image,"png:IHDR.color-type-orig ",msg,exception);
2355
2356       (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2357       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig  ",msg,exception);
2358   }
2359
2360   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2361                       &ping_trans_color);
2362
2363   (void) png_get_bKGD(ping, ping_info, &ping_background);
2364
2365   if (ping_bit_depth < 8)
2366     {
2367        png_set_packing(ping);
2368        ping_bit_depth = 8;
2369     }
2370
2371   image->depth=ping_bit_depth;
2372   image->depth=GetImageQuantumDepth(image,MagickFalse);
2373   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2374
2375   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2376       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2377     {
2378       image->rendering_intent=UndefinedIntent;
2379       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2380       image->gamma=1.000;
2381       (void) ResetMagickMemory(&image->chromaticity,0,
2382         sizeof(image->chromaticity));
2383     }
2384
2385   if (logging != MagickFalse)
2386     {
2387       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2388         "    PNG width: %.20g, height: %.20g",
2389         (double) ping_width, (double) ping_height);
2390
2391       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2392         "    PNG color_type: %d, bit_depth: %d",
2393         ping_color_type, ping_bit_depth);
2394
2395       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2396         "    PNG compression_method: %d",
2397         ping_compression_method);
2398
2399       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2400         "    PNG interlace_method: %d, filter_method: %d",
2401         ping_interlace_method,ping_filter_method);
2402     }
2403
2404   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2405     {
2406       ping_found_gAMA=MagickTrue;
2407       if (logging != MagickFalse)
2408         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2409           "    Found PNG gAMA chunk.");
2410     }
2411
2412   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2413     {
2414       ping_found_cHRM=MagickTrue;
2415       if (logging != MagickFalse)
2416         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2417           "    Found PNG cHRM chunk.");
2418     }
2419
2420   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2421     {
2422       ping_found_iCCP=MagickTrue;
2423       if (logging != MagickFalse)
2424         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2425           "    Found PNG iCCP chunk.");
2426     }
2427
2428   if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
2429     {
2430       ping_found_sRGB=MagickTrue;
2431       if (logging != MagickFalse)
2432         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2433           "    Found PNG sRGB chunk.");
2434     }
2435
2436 #ifdef PNG_READ_iCCP_SUPPORTED
2437   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2438     {
2439       int
2440         compression;
2441
2442 #if (PNG_LIBPNG_VER < 10500)
2443       png_charp
2444         info;
2445 #else
2446       png_bytep
2447         info;
2448 #endif
2449
2450       png_charp
2451         name;
2452
2453       png_uint_32
2454         profile_length;
2455
2456       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2457         &profile_length);
2458
2459       if (profile_length != 0)
2460         {
2461           StringInfo
2462             *profile;
2463
2464           if (logging != MagickFalse)
2465             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2466               "    Reading PNG iCCP chunk.");
2467           profile=BlobToStringInfo(info,profile_length);
2468           if (profile == (StringInfo *) NULL)
2469             {
2470               png_warning(ping, "ICC profile is NULL");
2471               profile=DestroyStringInfo(profile);
2472             }
2473           else
2474             {
2475               (void) SetImageProfile(image,"icc",profile,exception);
2476               profile=DestroyStringInfo(profile);
2477             }
2478       }
2479     }
2480 #endif
2481 #if defined(PNG_READ_sRGB_SUPPORTED)
2482   {
2483     if (mng_info->have_global_srgb)
2484       {
2485         image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2486           (mng_info->global_srgb_intent);
2487       }
2488
2489     if (png_get_sRGB(ping,ping_info,&intent))
2490       {
2491         image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2492           (intent);
2493
2494         if (logging != MagickFalse)
2495           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2496             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2497       }
2498   }
2499 #endif
2500   {
2501      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2502        if (mng_info->have_global_gama)
2503          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2504
2505      if (png_get_gAMA(ping,ping_info,&file_gamma))
2506        {
2507          image->gamma=(float) file_gamma;
2508          if (logging != MagickFalse)
2509            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2510              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2511        }
2512   }
2513
2514   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2515     {
2516       if (mng_info->have_global_chrm != MagickFalse)
2517         {
2518           (void) png_set_cHRM(ping,ping_info,
2519             mng_info->global_chrm.white_point.x,
2520             mng_info->global_chrm.white_point.y,
2521             mng_info->global_chrm.red_primary.x,
2522             mng_info->global_chrm.red_primary.y,
2523             mng_info->global_chrm.green_primary.x,
2524             mng_info->global_chrm.green_primary.y,
2525             mng_info->global_chrm.blue_primary.x,
2526             mng_info->global_chrm.blue_primary.y);
2527         }
2528     }
2529
2530   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2531     {
2532       (void) png_get_cHRM(ping,ping_info,
2533         &image->chromaticity.white_point.x,
2534         &image->chromaticity.white_point.y,
2535         &image->chromaticity.red_primary.x,
2536         &image->chromaticity.red_primary.y,
2537         &image->chromaticity.green_primary.x,
2538         &image->chromaticity.green_primary.y,
2539         &image->chromaticity.blue_primary.x,
2540         &image->chromaticity.blue_primary.y);
2541
2542        if (image->chromaticity.red_primary.x>0.6399f &&
2543            image->chromaticity.red_primary.x<0.6401f &&
2544            image->chromaticity.red_primary.y>0.3299f &&
2545            image->chromaticity.red_primary.y<0.3301f &&
2546            image->chromaticity.green_primary.x>0.2999f &&
2547            image->chromaticity.green_primary.x<0.3001f &&
2548            image->chromaticity.green_primary.y>0.5999f &&
2549            image->chromaticity.green_primary.y<0.6001f &&
2550            image->chromaticity.blue_primary.x>0.1499f &&
2551            image->chromaticity.blue_primary.x<0.1501f &&
2552            image->chromaticity.blue_primary.y>0.0599f &&
2553            image->chromaticity.blue_primary.y<0.0601f &&
2554            image->chromaticity.white_point.x>0.3126f &&
2555            image->chromaticity.white_point.x<0.3128f &&
2556            image->chromaticity.white_point.y>0.3289f &&
2557            image->chromaticity.white_point.y<0.3291f)
2558           ping_found_sRGB_cHRM=MagickTrue;
2559
2560       ping_found_cHRM=MagickTrue;
2561     }
2562
2563   if (image->rendering_intent != UndefinedIntent)
2564     {
2565       if (ping_found_sRGB != MagickTrue &&
2566           (ping_found_gAMA != MagickTrue ||
2567           (image->gamma > .45 && image->gamma < .46)) &&
2568           (ping_found_cHRM != MagickTrue ||
2569           ping_found_sRGB_cHRM == MagickTrue) &&
2570           ping_found_iCCP != MagickTrue)
2571       {
2572          png_set_sRGB(ping,ping_info,
2573             Magick_RenderingIntent_to_PNG_RenderingIntent
2574             (image->rendering_intent));
2575          if (ping_found_gAMA != MagickTrue)
2576             png_set_gAMA(ping,ping_info,1.000f/2.200f);
2577          file_gamma=1.000f/2.200f;
2578          ping_found_sRGB=MagickTrue;
2579          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2580            "    Setting sRGB and gAMA as if in input");
2581       }
2582     }
2583
2584 #if defined(PNG_oFFs_SUPPORTED)
2585   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2586     {
2587       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2588       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2589
2590       if (logging != MagickFalse)
2591         if (image->page.x || image->page.y)
2592           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2593             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2594             image->page.x,(double) image->page.y);
2595     }
2596 #endif
2597 #if defined(PNG_pHYs_SUPPORTED)
2598   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2599     {
2600       if (mng_info->have_global_phys)
2601         {
2602           png_set_pHYs(ping,ping_info,
2603                        mng_info->global_x_pixels_per_unit,
2604                        mng_info->global_y_pixels_per_unit,
2605                        mng_info->global_phys_unit_type);
2606         }
2607     }
2608
2609   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2610     {
2611       /*
2612         Set image resolution.
2613       */
2614       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2615         &unit_type);
2616       image->resolution.x=(double) x_resolution;
2617       image->resolution.y=(double) y_resolution;
2618
2619       if (unit_type == PNG_RESOLUTION_METER)
2620         {
2621           image->units=PixelsPerCentimeterResolution;
2622           image->resolution.x=(double) x_resolution/100.0;
2623           image->resolution.y=(double) y_resolution/100.0;
2624         }
2625
2626       if (logging != MagickFalse)
2627         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2628           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2629           (double) x_resolution,(double) y_resolution,unit_type);
2630     }
2631 #endif
2632
2633   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2634     {
2635       png_colorp
2636         palette;
2637
2638       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2639
2640       if ((number_colors == 0) &&
2641           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2642         {
2643           if (mng_info->global_plte_length)
2644             {
2645               png_set_PLTE(ping,ping_info,mng_info->global_plte,
2646                 (int) mng_info->global_plte_length);
2647
2648               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2649               {
2650                 if (mng_info->global_trns_length)
2651                   {
2652                     png_warning(ping,
2653                       "global tRNS has more entries than global PLTE");
2654                   }
2655                 else
2656                   {
2657                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
2658                        (int) mng_info->global_trns_length,NULL);
2659                   }
2660                }
2661 #ifdef PNG_READ_bKGD_SUPPORTED
2662               if (
2663 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2664                    mng_info->have_saved_bkgd_index ||
2665 #endif
2666                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2667                     {
2668                       png_color_16
2669                          background;
2670
2671 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2672                       if (mng_info->have_saved_bkgd_index)
2673                         background.index=mng_info->saved_bkgd_index;
2674 #endif
2675                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2676                         background.index=ping_background->index;
2677
2678                       background.red=(png_uint_16)
2679                         mng_info->global_plte[background.index].red;
2680
2681                       background.green=(png_uint_16)
2682                         mng_info->global_plte[background.index].green;
2683
2684                       background.blue=(png_uint_16)
2685                         mng_info->global_plte[background.index].blue;
2686
2687                       background.gray=(png_uint_16)
2688                         mng_info->global_plte[background.index].green;
2689
2690                       png_set_bKGD(ping,ping_info,&background);
2691                     }
2692 #endif
2693                 }
2694               else
2695                 png_error(ping,"No global PLTE in file");
2696             }
2697         }
2698
2699 #ifdef PNG_READ_bKGD_SUPPORTED
2700   if (mng_info->have_global_bkgd &&
2701           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2702       image->background_color=mng_info->mng_global_bkgd;
2703
2704   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2705     {
2706       unsigned int
2707         bkgd_scale;
2708
2709       /*
2710         Set image background color.
2711       */
2712       if (logging != MagickFalse)
2713         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2714           "    Reading PNG bKGD chunk.");
2715
2716       /* Scale background components to 16-bit, then scale
2717        * to quantum depth
2718        */
2719         if (logging != MagickFalse)
2720           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2721             "    raw ping_background=(%d,%d,%d).",ping_background->red,
2722             ping_background->green,ping_background->blue);
2723
2724         bkgd_scale = 1;
2725
2726         if (ping_file_depth == 1)
2727            bkgd_scale = 255;
2728
2729         else if (ping_file_depth == 2)
2730            bkgd_scale = 85;
2731
2732         else if (ping_file_depth == 4)
2733            bkgd_scale = 17;
2734
2735         if (ping_file_depth <= 8)
2736            bkgd_scale *= 257;
2737
2738         ping_background->red *= bkgd_scale;
2739         ping_background->green *= bkgd_scale;
2740         ping_background->blue *= bkgd_scale;
2741
2742         if (logging != MagickFalse)
2743           {
2744             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2745               "    bkgd_scale=%d.",bkgd_scale);
2746
2747             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2748               "    ping_background=(%d,%d,%d).",ping_background->red,
2749               ping_background->green,ping_background->blue);
2750           }
2751
2752         image->background_color.red=
2753             ScaleShortToQuantum(ping_background->red);
2754
2755         image->background_color.green=
2756             ScaleShortToQuantum(ping_background->green);
2757
2758         image->background_color.blue=
2759           ScaleShortToQuantum(ping_background->blue);
2760
2761         image->background_color.alpha=OpaqueAlpha;
2762
2763         if (logging != MagickFalse)
2764           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2765             "    image->background_color=(%.20g,%.20g,%.20g).",
2766             (double) image->background_color.red,
2767             (double) image->background_color.green,
2768             (double) image->background_color.blue);
2769     }
2770 #endif /* PNG_READ_bKGD_SUPPORTED */
2771
2772   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2773     {
2774       /*
2775         Image has a tRNS chunk.
2776       */
2777       int
2778         max_sample;
2779
2780       size_t
2781         one=1;
2782
2783       if (logging != MagickFalse)
2784         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2785           "    Reading PNG tRNS chunk.");
2786
2787       max_sample = (int) ((one << ping_file_depth) - 1);
2788
2789       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2790           (int)ping_trans_color->gray > max_sample) ||
2791           (ping_color_type == PNG_COLOR_TYPE_RGB &&
2792           ((int)ping_trans_color->red > max_sample ||
2793           (int)ping_trans_color->green > max_sample ||
2794           (int)ping_trans_color->blue > max_sample)))
2795         {
2796           if (logging != MagickFalse)
2797             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2798               "    Ignoring PNG tRNS chunk with out-of-range sample.");
2799           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2800           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2801           image->alpha_trait=UndefinedPixelTrait;
2802         }
2803       else
2804         {
2805           int
2806              scale_to_short;
2807
2808           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
2809
2810           /* Scale transparent_color to short */
2811           transparent_color.red= scale_to_short*ping_trans_color->red;
2812           transparent_color.green= scale_to_short*ping_trans_color->green;
2813           transparent_color.blue= scale_to_short*ping_trans_color->blue;
2814           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2815
2816           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2817             {
2818               if (logging != MagickFalse)
2819               {
2820                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2821                   "    Raw tRNS graylevel is %d.",ping_trans_color->gray);
2822
2823                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2824                   "    scaled graylevel is %.20g.",transparent_color.alpha);
2825               }
2826               transparent_color.red=transparent_color.alpha;
2827               transparent_color.green=transparent_color.alpha;
2828               transparent_color.blue=transparent_color.alpha;
2829             }
2830         }
2831     }
2832 #if defined(PNG_READ_sBIT_SUPPORTED)
2833   if (mng_info->have_global_sbit)
2834     {
2835       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2836         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2837     }
2838 #endif
2839   num_passes=png_set_interlace_handling(ping);
2840
2841   png_read_update_info(ping,ping_info);
2842
2843   ping_rowbytes=png_get_rowbytes(ping,ping_info);
2844
2845   /*
2846     Initialize image structure.
2847   */
2848   mng_info->image_box.left=0;
2849   mng_info->image_box.right=(ssize_t) ping_width;
2850   mng_info->image_box.top=0;
2851   mng_info->image_box.bottom=(ssize_t) ping_height;
2852   if (mng_info->mng_type == 0)
2853     {
2854       mng_info->mng_width=ping_width;
2855       mng_info->mng_height=ping_height;
2856       mng_info->frame=mng_info->image_box;
2857       mng_info->clip=mng_info->image_box;
2858     }
2859
2860   else
2861     {
2862       image->page.y=mng_info->y_off[mng_info->object_id];
2863     }
2864
2865   image->compression=ZipCompression;
2866   image->columns=ping_width;
2867   image->rows=ping_height;
2868
2869   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2870       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2871     {
2872       if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
2873           image->gamma == 1.0) && ping_found_sRGB != MagickTrue)
2874         {
2875           /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
2876            * image->colorspace to GRAY, and reset image->chromaticity.
2877            */
2878           SetImageColorspace(image,GRAYColorspace,exception);
2879         }
2880     }
2881   
2882   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2883       "  image->colorspace=%d",(int) image->colorspace);
2884
2885   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2886       ((int) ping_bit_depth < 16 &&
2887       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2888     {
2889       size_t
2890         one;
2891
2892       image->storage_class=PseudoClass;
2893       one=1;
2894       image->colors=one << ping_file_depth;
2895 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2896       if (image->colors > 256)
2897         image->colors=256;
2898 #else
2899       if (image->colors > 65536L)
2900         image->colors=65536L;
2901 #endif
2902       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2903         {
2904           png_colorp
2905             palette;
2906
2907           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2908           image->colors=(size_t) number_colors;
2909
2910           if (logging != MagickFalse)
2911             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2912               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2913         }
2914     }
2915
2916   if (image->storage_class == PseudoClass)
2917     {
2918       /*
2919         Initialize image colormap.
2920       */
2921       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
2922         png_error(ping,"Memory allocation failed");
2923
2924       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2925         {
2926           png_colorp
2927             palette;
2928
2929           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2930
2931           for (i=0; i < (ssize_t) number_colors; i++)
2932           {
2933             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2934             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2935             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2936           }
2937
2938           for ( ; i < (ssize_t) image->colors; i++)
2939           {
2940             image->colormap[i].red=0;
2941             image->colormap[i].green=0;
2942             image->colormap[i].blue=0;
2943           }
2944         }
2945
2946       else
2947         {
2948           size_t
2949             scale;
2950
2951           scale=(QuantumRange/((1UL << ping_file_depth)-1));
2952
2953           if (scale < 1)
2954              scale=1;
2955
2956           for (i=0; i < (ssize_t) image->colors; i++)
2957           {
2958             image->colormap[i].red=(Quantum) (i*scale);
2959             image->colormap[i].green=(Quantum) (i*scale);
2960             image->colormap[i].blue=(Quantum) (i*scale);
2961           }
2962        }
2963     }
2964
2965    /* Set some properties for reporting by "identify" */
2966     {
2967       char
2968         msg[MaxTextExtent];
2969
2970      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
2971         ping_interlace_method in value */
2972
2973      (void) FormatLocaleString(msg,MaxTextExtent,
2974          "%d, %d",(int) ping_width, (int) ping_height);
2975      (void) SetImageProperty(image,"png:IHDR.width,height    ",msg,exception);
2976
2977      (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
2978      (void) SetImageProperty(image,"png:IHDR.bit_depth       ",msg,exception);
2979
2980      (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
2981          (int) ping_color_type,
2982          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
2983      (void) SetImageProperty(image,"png:IHDR.color_type      ",msg,exception);
2984
2985      if (ping_interlace_method == 0)
2986        {
2987          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
2988             (int) ping_interlace_method);
2989        }
2990      else if (ping_interlace_method == 1)
2991        {
2992          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
2993             (int) ping_interlace_method);
2994        }
2995      else
2996        {
2997          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
2998             (int) ping_interlace_method);
2999        }
3000        (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3001
3002      if (number_colors != 0)
3003        {
3004          (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3005             (int) number_colors);
3006          (void) SetImageProperty(image,"png:PLTE.number_colors   ",msg,
3007             exception);
3008        }
3009    }
3010
3011   /*
3012     Read image scanlines.
3013   */
3014   if (image->delay != 0)
3015     mng_info->scenes_found++;
3016
3017   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3018       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3019       (image_info->first_scene+image_info->number_scenes))))
3020     {
3021       /* This happens later in non-ping decodes */
3022       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3023         image->storage_class=DirectClass;
3024
3025       if (logging != MagickFalse)
3026         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3027           "    Skipping PNG image data for scene %.20g",(double)
3028           mng_info->scenes_found-1);
3029       png_destroy_read_struct(&ping,&ping_info,&end_info);
3030
3031 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3032       UnlockSemaphoreInfo(ping_semaphore);
3033 #endif
3034
3035       if (logging != MagickFalse)
3036         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3037           "  exit ReadOnePNGImage().");
3038
3039       return(image);
3040     }
3041
3042   if (logging != MagickFalse)
3043     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3044       "    Reading PNG IDAT chunk(s)");
3045
3046   if (num_passes > 1)
3047     ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
3048       ping_rowbytes*sizeof(*ping_pixels));
3049
3050   else
3051     ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
3052       sizeof(*ping_pixels));
3053
3054   if (ping_pixels == (unsigned char *) NULL)
3055     png_error(ping,"Memory allocation failed");
3056
3057   if (logging != MagickFalse)
3058     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3059       "    Converting PNG pixels to pixel packets");
3060   /*
3061     Convert PNG pixels to pixel packets.
3062   */
3063   quantum_info=AcquireQuantumInfo(image_info,image);
3064
3065   if (quantum_info == (QuantumInfo *) NULL)
3066      png_error(ping,"Failed to allocate quantum_info");
3067
3068   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3069
3070   {
3071
3072    MagickBooleanType
3073      found_transparent_pixel;
3074
3075   found_transparent_pixel=MagickFalse;
3076
3077   if (image->storage_class == DirectClass)
3078     {
3079       for (pass=0; pass < num_passes; pass++)
3080       {
3081         /*
3082           Convert image to DirectClass pixel packets.
3083         */
3084         image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3085             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3086             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3087             BlendPixelTrait : UndefinedPixelTrait;
3088
3089         for (y=0; y < (ssize_t) image->rows; y++)
3090         {
3091           if (num_passes > 1)
3092             row_offset=ping_rowbytes*y;
3093
3094           else
3095             row_offset=0;
3096
3097           png_read_row(ping,ping_pixels+row_offset,NULL);
3098
3099           if (pass < num_passes-1)
3100             continue;
3101
3102           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3103
3104           if (q == (Quantum *) NULL)
3105             break;
3106
3107           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3108             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3109               GrayQuantum,ping_pixels+row_offset,exception);
3110
3111           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3112             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3113               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3114
3115           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3116             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3117               RGBAQuantum,ping_pixels+row_offset,exception);
3118
3119           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3120             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3121               IndexQuantum,ping_pixels+row_offset,exception);
3122
3123           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3124             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3125               RGBQuantum,ping_pixels+row_offset,exception);
3126
3127           if (found_transparent_pixel == MagickFalse)
3128             {
3129               /* Is there a transparent pixel in the row? */
3130               if (y== 0 && logging != MagickFalse)
3131                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3132                    "    Looking for cheap transparent pixel");
3133
3134               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3135               {
3136                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3137                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3138                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3139                   {
3140                     if (logging != MagickFalse)
3141                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3142                         "    ...got one.");
3143
3144                     found_transparent_pixel = MagickTrue;
3145                     break;
3146                   }
3147                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3148                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3149                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3150                     transparent_color.red &&
3151                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3152                     transparent_color.green &&
3153                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3154                     transparent_color.blue))
3155                   {
3156                     if (logging != MagickFalse)
3157                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3158                         "    ...got one.");
3159                     found_transparent_pixel = MagickTrue;
3160                     break;
3161                   }
3162                 q+=GetPixelChannels(image);
3163               }
3164             }
3165
3166           if ((image->previous == (Image *) NULL) && (num_passes == 1))
3167             {
3168               status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3169                   image->rows);
3170
3171               if (status == MagickFalse)
3172                 break;
3173             }
3174           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3175             break;
3176         }
3177
3178         if ((image->previous == (Image *) NULL) && (num_passes != 1))
3179           {
3180             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3181             if (status == MagickFalse)
3182               break;
3183           }
3184       }
3185     }
3186
3187   else /* image->storage_class != DirectClass */
3188
3189     for (pass=0; pass < num_passes; pass++)
3190     {
3191       Quantum
3192         *quantum_scanline;
3193
3194       register Quantum
3195         *r;
3196
3197       /*
3198         Convert grayscale image to PseudoClass pixel packets.
3199       */
3200       if (logging != MagickFalse)
3201         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3202           "    Converting grayscale pixels to pixel packets");
3203
3204       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3205         BlendPixelTrait : UndefinedPixelTrait;
3206
3207       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3208         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3209         sizeof(*quantum_scanline));
3210
3211       if (quantum_scanline == (Quantum *) NULL)
3212         png_error(ping,"Memory allocation failed");
3213
3214       for (y=0; y < (ssize_t) image->rows; y++)
3215       {
3216         Quantum
3217            alpha;
3218
3219         if (num_passes > 1)
3220           row_offset=ping_rowbytes*y;
3221
3222         else
3223           row_offset=0;
3224
3225         png_read_row(ping,ping_pixels+row_offset,NULL);
3226
3227         if (pass < num_passes-1)
3228           continue;
3229
3230         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3231
3232         if (q == (Quantum *) NULL)
3233           break;
3234
3235         p=ping_pixels+row_offset;
3236         r=quantum_scanline;
3237
3238         switch (ping_bit_depth)
3239         {
3240           case 8:
3241           {
3242
3243             if (ping_color_type == 4)
3244               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3245               {
3246                 *r++=*p++;
3247
3248                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3249
3250                 SetPixelAlpha(image,alpha,q);
3251
3252                 if (alpha != OpaqueAlpha)
3253                   found_transparent_pixel = MagickTrue;
3254
3255                 q+=GetPixelChannels(image);
3256               }
3257
3258             else
3259               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3260                 *r++=*p++;
3261
3262             break;
3263           }
3264
3265           case 16:
3266           {
3267             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3268             {
3269 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3270               size_t
3271                 quantum;
3272
3273               if (image->colors > 256)
3274                 quantum=((*p++) << 8);
3275
3276               else
3277                 quantum=0;
3278
3279               quantum|=(*p++);
3280               *r=ScaleShortToQuantum(quantum);
3281               r++;
3282
3283               if (ping_color_type == 4)
3284                 {
3285                   if (image->colors > 256)
3286                     quantum=((*p++) << 8);
3287                   else
3288                     quantum=0;
3289
3290                   quantum|=(*p++);
3291
3292                   alpha=ScaleShortToQuantum(quantum);
3293                   SetPixelAlpha(image,alpha,q);
3294
3295                   if (alpha != OpaqueAlpha)
3296                     found_transparent_pixel = MagickTrue;
3297
3298                   q+=GetPixelChannels(image);
3299                 }
3300
3301 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3302               *r++=(*p++);
3303               p++; /* strip low byte */
3304
3305               if (ping_color_type == 4)
3306                 {
3307                   SetPixelAlpha(image,*p++,q);
3308
3309                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3310                     found_transparent_pixel = MagickTrue;
3311
3312                   p++;
3313                   q+=GetPixelChannels(image);
3314                 }
3315 #endif
3316             }
3317
3318             break;
3319           }
3320
3321           default:
3322             break;
3323         }
3324
3325         /*
3326           Transfer image scanline.
3327         */
3328         r=quantum_scanline;
3329
3330         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3331
3332         if (q == (Quantum *) NULL)
3333           break;
3334         for (x=0; x < (ssize_t) image->columns; x++)
3335         {
3336           SetPixelIndex(image,*r++,q);
3337           q+=GetPixelChannels(image);
3338         }
3339
3340         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3341           break;
3342
3343         if ((image->previous == (Image *) NULL) && (num_passes == 1))
3344           {
3345             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3346               image->rows);
3347
3348             if (status == MagickFalse)
3349               break;
3350           }
3351       }
3352
3353       if ((image->previous == (Image *) NULL) && (num_passes != 1))
3354         {
3355           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3356
3357           if (status == MagickFalse)
3358             break;
3359         }
3360
3361       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3362     }
3363
3364     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3365       UndefinedPixelTrait;
3366
3367     if (logging != MagickFalse)
3368       {
3369         if (found_transparent_pixel != MagickFalse)
3370           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3371             "    Found transparent pixel");
3372         else
3373           {
3374             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3375               "    No transparent pixel was found");
3376
3377             ping_color_type&=0x03;
3378           }
3379       }
3380     }
3381
3382   if (quantum_info != (QuantumInfo *) NULL)
3383     quantum_info=DestroyQuantumInfo(quantum_info);
3384
3385   if (image->storage_class == PseudoClass)
3386     {
3387       PixelTrait
3388         alpha_trait;
3389
3390       alpha_trait=image->alpha_trait;
3391       image->alpha_trait=UndefinedPixelTrait;
3392       (void) SyncImage(image,exception);
3393       image->alpha_trait=alpha_trait;
3394     }
3395
3396   png_read_end(ping,end_info);
3397
3398   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3399       (ssize_t) image_info->first_scene && image->delay != 0)
3400     {
3401       png_destroy_read_struct(&ping,&ping_info,&end_info);
3402       ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3403       image->colors=2;
3404       (void) SetImageBackgroundColor(image,exception);
3405 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3406       UnlockSemaphoreInfo(ping_semaphore);
3407 #endif
3408       if (logging != MagickFalse)
3409         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3410           "  exit ReadOnePNGImage() early.");
3411       return(image);
3412     }
3413
3414   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3415     {
3416       ClassType
3417         storage_class;
3418
3419       /*
3420         Image has a transparent background.
3421       */
3422       storage_class=image->storage_class;
3423       image->alpha_trait=BlendPixelTrait;
3424
3425 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3426
3427       if (storage_class == PseudoClass)
3428         {
3429           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3430             {
3431               for (x=0; x < ping_num_trans; x++)
3432               {
3433                  image->colormap[x].alpha_trait=BlendPixelTrait;
3434                  image->colormap[x].alpha =
3435                    ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3436               }
3437             }
3438
3439           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3440             {
3441               for (x=0; x < (int) image->colors; x++)
3442               {
3443                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3444                      transparent_color.alpha)
3445                  {
3446                     image->colormap[x].alpha_trait=BlendPixelTrait;
3447                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3448                  }
3449               }
3450             }
3451           (void) SyncImage(image,exception);
3452         }
3453
3454 #if 1 /* Should have already been done above, but glennrp problem P10
3455        * needs this.
3456        */
3457       else
3458         {
3459           for (y=0; y < (ssize_t) image->rows; y++)
3460           {
3461             image->storage_class=storage_class;
3462             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3463
3464             if (q == (Quantum *) NULL)
3465               break;
3466
3467
3468             /* Caution: on a Q8 build, this does not distinguish between
3469              * 16-bit colors that differ only in the low byte
3470              */
3471             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3472             {
3473               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3474                   transparent_color.red &&
3475                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3476                   transparent_color.green &&
3477                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3478                   transparent_color.blue)
3479                 {
3480                   SetPixelAlpha(image,TransparentAlpha,q);
3481                 }
3482
3483 #if 0 /* I have not found a case where this is needed. */
3484               else
3485                 {
3486                   SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3487                 }
3488 #endif
3489
3490               q+=GetPixelChannels(image);
3491             }
3492
3493             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3494                break;
3495           }
3496         }
3497 #endif
3498
3499       image->storage_class=DirectClass;
3500     }
3501
3502   for (j = 0; j < 2; j++)
3503   {
3504     if (j == 0)
3505       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3506           MagickTrue : MagickFalse;
3507     else
3508       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3509           MagickTrue : MagickFalse;
3510
3511     if (status != MagickFalse)
3512       for (i=0; i < (ssize_t) num_text; i++)
3513       {
3514         /* Check for a profile */
3515
3516         if (logging != MagickFalse)
3517           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3518             "    Reading PNG text chunk");
3519
3520         if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3521           {
3522             (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3523                (int) i,exception);
3524             num_raw_profiles++;
3525           }
3526
3527         else
3528           {
3529             char
3530               *value;
3531
3532             length=text[i].text_length;
3533             value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3534               sizeof(*value));
3535             if (value == (char *) NULL)
3536               {
3537                 png_error(ping,"Memory allocation failed");
3538                 break;
3539               }
3540             *value='\0';
3541             (void) ConcatenateMagickString(value,text[i].text,length+2);
3542
3543             /* Don't save "density" or "units" property if we have a pHYs
3544              * chunk
3545              */
3546             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3547                 (LocaleCompare(text[i].key,"density") != 0 &&
3548                 LocaleCompare(text[i].key,"units") != 0))
3549                (void) SetImageProperty(image,text[i].key,value,exception);
3550
3551             if (logging != MagickFalse)
3552             {
3553               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3554                 "      length: %lu",(unsigned long) length);
3555               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3556                 "      Keyword: %s",text[i].key);
3557             }
3558
3559             value=DestroyString(value);
3560           }
3561       }
3562       num_text_total += num_text;
3563     }
3564
3565 #ifdef MNG_OBJECT_BUFFERS
3566   /*
3567     Store the object if necessary.
3568   */
3569   if (object_id && !mng_info->frozen[object_id])
3570     {
3571       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3572         {
3573           /*
3574             create a new object buffer.
3575           */
3576           mng_info->ob[object_id]=(MngBuffer *)
3577             AcquireMagickMemory(sizeof(MngBuffer));
3578
3579           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3580             {
3581               mng_info->ob[object_id]->image=(Image *) NULL;
3582               mng_info->ob[object_id]->reference_count=1;
3583             }
3584         }
3585
3586       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3587           mng_info->ob[object_id]->frozen)
3588         {
3589           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3590              png_error(ping,"Memory allocation failed");
3591
3592           if (mng_info->ob[object_id]->frozen)
3593             png_error(ping,"Cannot overwrite frozen MNG object buffer");
3594         }
3595
3596       else
3597         {
3598
3599           if (mng_info->ob[object_id]->image != (Image *) NULL)
3600             mng_info->ob[object_id]->image=DestroyImage
3601                 (mng_info->ob[object_id]->image);
3602
3603           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3604             exception);
3605
3606           if (mng_info->ob[object_id]->image != (Image *) NULL)
3607             mng_info->ob[object_id]->image->file=(FILE *) NULL;
3608
3609           else
3610             png_error(ping, "Cloning image for object buffer failed");
3611
3612           if (ping_width > 250000L || ping_height > 250000L)
3613              png_error(ping,"PNG Image dimensions are too large.");
3614
3615           mng_info->ob[object_id]->width=ping_width;
3616           mng_info->ob[object_id]->height=ping_height;
3617           mng_info->ob[object_id]->color_type=ping_color_type;
3618           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3619           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3620           mng_info->ob[object_id]->compression_method=
3621              ping_compression_method;
3622           mng_info->ob[object_id]->filter_method=ping_filter_method;
3623
3624           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3625             {
3626               png_colorp
3627                 plte;
3628
3629               /*
3630                 Copy the PLTE to the object buffer.
3631               */
3632               png_get_PLTE(ping,ping_info,&plte,&number_colors);
3633               mng_info->ob[object_id]->plte_length=number_colors;
3634
3635               for (i=0; i < number_colors; i++)
3636               {
3637                 mng_info->ob[object_id]->plte[i]=plte[i];
3638               }
3639             }
3640
3641           else
3642               mng_info->ob[object_id]->plte_length=0;
3643         }
3644     }
3645 #endif
3646
3647    /* Set image->alpha_trait to MagickTrue if the input colortype supports
3648     * alpha or if a valid tRNS chunk is present, no matter whether there
3649     * is actual transparency present.
3650     */
3651     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3652         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3653         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3654         BlendPixelTrait : UndefinedPixelTrait;
3655
3656 #if 0  /* I'm not sure what's wrong here but it does not work. */
3657     if (image->alpha_trait == BlendPixelTrait)
3658     {
3659       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3660         (void) SetImageType(image,GrayscaleMatteType,exception);
3661
3662       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3663         (void) SetImageType(image,PaletteMatteType,exception);
3664
3665       else
3666         (void) SetImageType(image,TrueColorMatteType,exception);
3667     }
3668
3669     else
3670     {
3671       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3672         (void) SetImageType(image,GrayscaleType,exception);
3673
3674       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3675         (void) SetImageType(image,PaletteType,exception);
3676
3677       else
3678         (void) SetImageType(image,TrueColorType,exception);
3679     }
3680 #endif
3681
3682    /* Set more properties for identify to retrieve */
3683    {
3684      char
3685        msg[MaxTextExtent];
3686
3687      if (num_text_total != 0)
3688        {
3689          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3690          (void) FormatLocaleString(msg,MaxTextExtent,
3691             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3692          (void) SetImageProperty(image,"png:text                 ",msg,
3693                 exception);
3694        }
3695
3696      if (num_raw_profiles != 0)
3697        {
3698          (void) FormatLocaleString(msg,MaxTextExtent,
3699             "%d were found", num_raw_profiles);
3700          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3701                 exception);
3702        }
3703
3704      if (ping_found_cHRM != MagickFalse)
3705        {
3706          (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3707             "chunk was found (see Chromaticity, above)");
3708          (void) SetImageProperty(image,"png:cHRM                 ",msg,
3709                 exception);
3710        }
3711
3712      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3713        {
3714          (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3715             "chunk was found (see Background color, above)");
3716          (void) SetImageProperty(image,"png:bKGD                 ",msg,
3717                 exception);
3718        }
3719
3720      (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3721         "chunk was found");
3722
3723 #if defined(PNG_iCCP_SUPPORTED)
3724      if (ping_found_iCCP != MagickFalse)
3725         (void) SetImageProperty(image,"png:iCCP                 ",msg,
3726                 exception);
3727 #endif
3728
3729      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3730         (void) SetImageProperty(image,"png:tRNS                 ",msg,
3731                 exception);
3732
3733 #if defined(PNG_sRGB_SUPPORTED)
3734      if (ping_found_sRGB != MagickFalse)
3735        {
3736          (void) FormatLocaleString(msg,MaxTextExtent,
3737             "intent=%d (%s)",
3738             (int) intent,
3739             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3740          (void) SetImageProperty(image,"png:sRGB                 ",msg,
3741                  exception);
3742        }
3743 #endif
3744
3745      if (ping_found_gAMA != MagickFalse)
3746        {
3747          (void) FormatLocaleString(msg,MaxTextExtent,
3748             "gamma=%.8g (See Gamma, above)",
3749             file_gamma);
3750          (void) SetImageProperty(image,"png:gAMA                 ",msg,
3751                 exception);
3752        }
3753
3754 #if defined(PNG_pHYs_SUPPORTED)
3755      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3756        {
3757          (void) FormatLocaleString(msg,MaxTextExtent,
3758             "x_res=%.10g, y_res=%.10g, units=%d",
3759             (double) x_resolution,(double) y_resolution, unit_type);
3760          (void) SetImageProperty(image,"png:pHYs                 ",msg,
3761                 exception);
3762        }
3763 #endif
3764
3765 #if defined(PNG_oFFs_SUPPORTED)
3766      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3767        {
3768          (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3769             (double) image->page.x,(double) image->page.y);
3770          (void) SetImageProperty(image,"png:oFFs                 ",msg,
3771                 exception);
3772        }
3773 #endif
3774
3775      if ((image->page.width != 0 && image->page.width != image->columns) ||
3776          (image->page.height != 0 && image->page.height != image->rows))
3777        {
3778          (void) FormatLocaleString(msg,MaxTextExtent,
3779             "width=%.20g, height=%.20g",
3780             (double) image->page.width,(double) image->page.height);
3781          (void) SetImageProperty(image,"png:vpAg                 ",msg,
3782                 exception);
3783        }
3784    }
3785
3786   /*
3787     Relinquish resources.
3788   */
3789   png_destroy_read_struct(&ping,&ping_info,&end_info);
3790
3791   ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3792
3793   if (logging != MagickFalse)
3794     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3795       "  exit ReadOnePNGImage()");
3796
3797 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
3798   UnlockSemaphoreInfo(ping_semaphore);
3799 #endif
3800
3801   /* }  for navigation to beginning of SETJMP-protected block, revert to
3802    *    Throwing an Exception when an error occurs.
3803    */
3804
3805   return(image);
3806
3807 /* end of reading one PNG image */
3808 }
3809
3810 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3811 {
3812   Image
3813     *image,
3814     *previous;
3815
3816   MagickBooleanType
3817     have_mng_structure,
3818     logging,
3819     status;
3820
3821   MngInfo
3822     *mng_info;
3823
3824   char
3825     magic_number[MaxTextExtent];
3826
3827   ssize_t
3828     count;
3829
3830   /*
3831     Open image file.
3832   */
3833   assert(image_info != (const ImageInfo *) NULL);
3834   assert(image_info->signature == MagickSignature);
3835
3836   if (image_info->debug != MagickFalse)
3837     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3838       image_info->filename);
3839
3840   assert(exception != (ExceptionInfo *) NULL);
3841   assert(exception->signature == MagickSignature);
3842   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3843   image=AcquireImage(image_info,exception);
3844   mng_info=(MngInfo *) NULL;
3845   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3846
3847   if (status == MagickFalse)
3848     ThrowReaderException(FileOpenError,"UnableToOpenFile");
3849
3850   /*
3851     Verify PNG signature.
3852   */
3853   count=ReadBlob(image,8,(unsigned char *) magic_number);
3854
3855   if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3856     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3857
3858   /*
3859     Allocate a MngInfo structure.
3860   */
3861   have_mng_structure=MagickFalse;
3862   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3863
3864   if (mng_info == (MngInfo *) NULL)
3865     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3866
3867   /*
3868     Initialize members of the MngInfo structure.
3869   */
3870   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3871   mng_info->image=image;
3872   have_mng_structure=MagickTrue;
3873
3874   previous=image;
3875   image=ReadOnePNGImage(mng_info,image_info,exception);
3876   MngInfoFreeStruct(mng_info,&have_mng_structure);
3877
3878   if (image == (Image *) NULL)
3879     {
3880       if (previous != (Image *) NULL)
3881         {
3882           if (previous->signature != MagickSignature)
3883             ThrowReaderException(CorruptImageError,"CorruptImage");
3884
3885           (void) CloseBlob(previous);
3886           (void) DestroyImageList(previous);
3887         }
3888
3889       if (logging != MagickFalse)
3890         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3891           "exit ReadPNGImage() with error");
3892
3893       return((Image *) NULL);
3894     }
3895
3896   (void) CloseBlob(image);
3897
3898   if ((image->columns == 0) || (image->rows == 0))
3899     {
3900       if (logging != MagickFalse)
3901         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3902           "exit ReadPNGImage() with error.");
3903
3904       ThrowReaderException(CorruptImageError,"CorruptImage");
3905     }
3906
3907   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
3908       ((image->gamma < .45) || (image->gamma > .46)) &&
3909            !(image->chromaticity.red_primary.x>0.6399f &&
3910            image->chromaticity.red_primary.x<0.6401f &&
3911            image->chromaticity.red_primary.y>0.3299f &&
3912            image->chromaticity.red_primary.y<0.3301f &&
3913            image->chromaticity.green_primary.x>0.2999f &&
3914            image->chromaticity.green_primary.x<0.3001f &&
3915            image->chromaticity.green_primary.y>0.5999f &&
3916            image->chromaticity.green_primary.y<0.6001f &&
3917            image->chromaticity.blue_primary.x>0.1499f &&
3918            image->chromaticity.blue_primary.x<0.1501f &&
3919            image->chromaticity.blue_primary.y>0.0599f &&
3920            image->chromaticity.blue_primary.y<0.0601f &&
3921            image->chromaticity.white_point.x>0.3126f &&
3922            image->chromaticity.white_point.x<0.3128f &&
3923            image->chromaticity.white_point.y>0.3289f &&
3924            image->chromaticity.white_point.y<0.3291f))
3925     SetImageColorspace(image,RGBColorspace,exception);
3926
3927   if (logging != MagickFalse)
3928     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3929         "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3930             (double) image->page.width,(double) image->page.height,
3931             (double) image->page.x,(double) image->page.y);
3932
3933   if (logging != MagickFalse)
3934     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3935
3936   return(image);
3937 }
3938
3939
3940
3941 #if defined(JNG_SUPPORTED)
3942 /*
3943 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3944 %                                                                             %
3945 %                                                                             %
3946 %                                                                             %
3947 %   R e a d O n e J N G I m a g e                                             %
3948 %                                                                             %
3949 %                                                                             %
3950 %                                                                             %
3951 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3952 %
3953 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3954 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
3955 %  necessary for the new Image structure and returns a pointer to the new
3956 %  image.
3957 %
3958 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
3959 %
3960 %  The format of the ReadOneJNGImage method is:
3961 %
3962 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3963 %         ExceptionInfo *exception)
3964 %
3965 %  A description of each parameter follows:
3966 %
3967 %    o mng_info: Specifies a pointer to a MngInfo structure.
3968 %
3969 %    o image_info: the image info.
3970 %
3971 %    o exception: return any errors or warnings in this structure.
3972 %
3973 */
3974 static Image *ReadOneJNGImage(MngInfo *mng_info,
3975     const ImageInfo *image_info, ExceptionInfo *exception)
3976 {
3977   Image
3978     *alpha_image,
3979     *color_image,
3980     *image,
3981     *jng_image;
3982
3983   ImageInfo
3984     *alpha_image_info,
3985     *color_image_info;
3986
3987   MagickBooleanType
3988     logging;
3989
3990   ssize_t
3991     y;
3992
3993   MagickBooleanType
3994     status;
3995
3996   png_uint_32
3997     jng_height,
3998     jng_width;
3999
4000   png_byte
4001     jng_color_type,
4002     jng_image_sample_depth,
4003     jng_image_compression_method,
4004     jng_image_interlace_method,
4005     jng_alpha_sample_depth,
4006     jng_alpha_compression_method,
4007     jng_alpha_filter_method,
4008     jng_alpha_interlace_method;
4009
4010   register const Quantum
4011     *s;
4012
4013   register ssize_t
4014     i,
4015     x;
4016
4017   register Quantum
4018     *q;
4019
4020   register unsigned char
4021     *p;
4022
4023   unsigned int
4024     read_JSEP,
4025     reading_idat,
4026     skip_to_iend;
4027
4028   size_t
4029     length;
4030
4031   jng_alpha_compression_method=0;
4032   jng_alpha_sample_depth=8;
4033   jng_color_type=0;
4034   jng_height=0;
4035   jng_width=0;
4036   alpha_image=(Image *) NULL;
4037   color_image=(Image *) NULL;
4038   alpha_image_info=(ImageInfo *) NULL;
4039   color_image_info=(ImageInfo *) NULL;
4040
4041   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4042     "  Enter ReadOneJNGImage()");
4043
4044   image=mng_info->image;
4045
4046   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4047     {
4048       /*
4049         Allocate next image structure.
4050       */
4051       if (logging != MagickFalse)
4052         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4053            "  AcquireNextImage()");
4054
4055       AcquireNextImage(image_info,image,exception);
4056
4057       if (GetNextImageInList(image) == (Image *) NULL)
4058         return((Image *) NULL);
4059
4060       image=SyncNextImageInList(image);
4061     }
4062   mng_info->image=image;
4063
4064   /*
4065     Signature bytes have already been read.
4066   */
4067
4068   read_JSEP=MagickFalse;
4069   reading_idat=MagickFalse;
4070   skip_to_iend=MagickFalse;
4071   for (;;)
4072   {
4073     char
4074       type[MaxTextExtent];
4075
4076     unsigned char
4077       *chunk;
4078
4079     unsigned int
4080       count;
4081
4082     /*
4083       Read a new JNG chunk.
4084     */
4085     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4086       2*GetBlobSize(image));
4087
4088     if (status == MagickFalse)
4089       break;
4090
4091     type[0]='\0';
4092     (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4093     length=ReadBlobMSBLong(image);
4094     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4095
4096     if (logging != MagickFalse)
4097       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4098         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4099         type[0],type[1],type[2],type[3],(double) length);
4100
4101     if (length > PNG_UINT_31_MAX || count == 0)
4102       ThrowReaderException(CorruptImageError,"CorruptImage");
4103
4104     p=NULL;
4105     chunk=(unsigned char *) NULL;
4106
4107     if (length)
4108       {
4109         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4110
4111         if (chunk == (unsigned char *) NULL)
4112           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4113
4114         for (i=0; i < (ssize_t) length; i++)
4115           chunk[i]=(unsigned char) ReadBlobByte(image);
4116
4117         p=chunk;
4118       }
4119
4120     (void) ReadBlobMSBLong(image);  /* read crc word */
4121
4122     if (skip_to_iend)
4123       {
4124         if (length)
4125           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4126
4127         continue;
4128       }
4129
4130     if (memcmp(type,mng_JHDR,4) == 0)
4131       {
4132         if (length == 16)
4133           {
4134             jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4135                 (p[2] << 8) | p[3]);
4136             jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4137                 (p[6] << 8) | p[7]);
4138             jng_color_type=p[8];
4139             jng_image_sample_depth=p[9];
4140             jng_image_compression_method=p[10];
4141             jng_image_interlace_method=p[11];
4142
4143             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4144               NoInterlace;
4145
4146             jng_alpha_sample_depth=p[12];
4147             jng_alpha_compression_method=p[13];
4148             jng_alpha_filter_method=p[14];
4149             jng_alpha_interlace_method=p[15];
4150
4151             if (logging != MagickFalse)
4152               {
4153                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4154                   "    jng_width:      %16lu",(unsigned long) jng_width);
4155
4156                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4157                   "    jng_width:      %16lu",(unsigned long) jng_height);
4158
4159                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4160                   "    jng_color_type: %16d",jng_color_type);
4161
4162                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4163                   "    jng_image_sample_depth:      %3d",
4164                   jng_image_sample_depth);
4165
4166                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4167                   "    jng_image_compression_method:%3d",
4168                   jng_image_compression_method);
4169
4170                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4171                   "    jng_image_interlace_method:  %3d",
4172                   jng_image_interlace_method);
4173
4174                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4175                   "    jng_alpha_sample_depth:      %3d",
4176                   jng_alpha_sample_depth);
4177
4178                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4179                   "    jng_alpha_compression_method:%3d",
4180                   jng_alpha_compression_method);
4181
4182                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4183                   "    jng_alpha_filter_method:     %3d",
4184                   jng_alpha_filter_method);
4185
4186                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4187                   "    jng_alpha_interlace_method:  %3d",
4188                   jng_alpha_interlace_method);
4189               }
4190           }
4191
4192         if (length)
4193           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4194
4195         continue;
4196       }
4197
4198
4199     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4200         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4201          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4202       {
4203         /*
4204            o create color_image
4205            o open color_blob, attached to color_image
4206            o if (color type has alpha)
4207                open alpha_blob, attached to alpha_image
4208         */
4209
4210         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4211
4212         if (color_image_info == (ImageInfo *) NULL)
4213           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4214
4215         GetImageInfo(color_image_info);
4216         color_image=AcquireImage(color_image_info,exception);
4217
4218         if (color_image == (Image *) NULL)
4219           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4220
4221         if (logging != MagickFalse)
4222           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4223             "    Creating color_blob.");
4224
4225         (void) AcquireUniqueFilename(color_image->filename);
4226         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4227           exception);
4228
4229         if (status == MagickFalse)
4230           return((Image *) NULL);
4231
4232         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4233           {
4234             alpha_image_info=(ImageInfo *)
4235               AcquireMagickMemory(sizeof(ImageInfo));
4236
4237             if (alpha_image_info == (ImageInfo *) NULL)
4238               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4239
4240             GetImageInfo(alpha_image_info);
4241             alpha_image=AcquireImage(alpha_image_info,exception);
4242
4243             if (alpha_image == (Image *) NULL)
4244               {
4245                 alpha_image=DestroyImage(alpha_image);
4246                 ThrowReaderException(ResourceLimitError,
4247                   "MemoryAllocationFailed");
4248               }
4249
4250             if (logging != MagickFalse)
4251               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4252                 "    Creating alpha_blob.");
4253
4254             (void) AcquireUniqueFilename(alpha_image->filename);
4255             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4256               exception);
4257
4258             if (status == MagickFalse)
4259               return((Image *) NULL);
4260
4261             if (jng_alpha_compression_method == 0)
4262               {
4263                 unsigned char
4264                   data[18];
4265
4266                 if (logging != MagickFalse)
4267                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4268                     "    Writing IHDR chunk to alpha_blob.");
4269
4270                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4271                   "\211PNG\r\n\032\n");
4272
4273                 (void) WriteBlobMSBULong(alpha_image,13L);
4274                 PNGType(data,mng_IHDR);
4275                 LogPNGChunk(logging,mng_IHDR,13L);
4276                 PNGLong(data+4,jng_width);
4277                 PNGLong(data+8,jng_height);
4278                 data[12]=jng_alpha_sample_depth;
4279                 data[13]=0; /* color_type gray */
4280                 data[14]=0; /* compression method 0 */
4281                 data[15]=0; /* filter_method 0 */
4282                 data[16]=0; /* interlace_method 0 */
4283                 (void) WriteBlob(alpha_image,17,data);
4284                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4285               }
4286           }
4287         reading_idat=MagickTrue;
4288       }
4289
4290     if (memcmp(type,mng_JDAT,4) == 0)
4291       {
4292         /* Copy chunk to color_image->blob */
4293
4294         if (logging != MagickFalse)
4295           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4296             "    Copying JDAT chunk data to color_blob.");
4297
4298         (void) WriteBlob(color_image,length,chunk);
4299
4300         if (length)
4301           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4302
4303         continue;
4304       }
4305
4306     if (memcmp(type,mng_IDAT,4) == 0)
4307       {
4308         png_byte
4309            data[5];
4310
4311         /* Copy IDAT header and chunk data to alpha_image->blob */
4312
4313         if (image_info->ping == MagickFalse)
4314           {
4315             if (logging != MagickFalse)
4316               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4317                 "    Copying IDAT chunk data to alpha_blob.");
4318
4319             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4320             PNGType(data,mng_IDAT);
4321             LogPNGChunk(logging,mng_IDAT,length);
4322             (void) WriteBlob(alpha_image,4,data);
4323             (void) WriteBlob(alpha_image,length,chunk);
4324             (void) WriteBlobMSBULong(alpha_image,
4325               crc32(crc32(0,data,4),chunk,(uInt) length));
4326           }
4327
4328         if (length)
4329           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4330
4331         continue;
4332       }
4333
4334     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4335       {
4336         /* Copy chunk data to alpha_image->blob */
4337
4338         if (image_info->ping == MagickFalse)
4339           {
4340             if (logging != MagickFalse)
4341               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4342                 "    Copying JDAA chunk data to alpha_blob.");
4343
4344             (void) WriteBlob(alpha_image,length,chunk);
4345           }
4346
4347         if (length)
4348           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4349
4350         continue;
4351       }
4352
4353     if (memcmp(type,mng_JSEP,4) == 0)
4354       {
4355         read_JSEP=MagickTrue;
4356
4357         if (length)
4358           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4359
4360         continue;
4361       }
4362
4363     if (memcmp(type,mng_bKGD,4) == 0)
4364       {
4365         if (length == 2)
4366           {
4367             image->background_color.red=ScaleCharToQuantum(p[1]);
4368             image->background_color.green=image->background_color.red;
4369             image->background_color.blue=image->background_color.red;
4370           }
4371
4372         if (length == 6)
4373           {
4374             image->background_color.red=ScaleCharToQuantum(p[1]);
4375             image->background_color.green=ScaleCharToQuantum(p[3]);
4376             image->background_color.blue=ScaleCharToQuantum(p[5]);
4377           }
4378
4379         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4380         continue;
4381       }
4382
4383     if (memcmp(type,mng_gAMA,4) == 0)
4384       {
4385         if (length == 4)
4386           image->gamma=((float) mng_get_long(p))*0.00001;
4387
4388         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4389         continue;
4390       }
4391
4392     if (memcmp(type,mng_cHRM,4) == 0)
4393       {
4394         if (length == 32)
4395           {
4396             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4397             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4398             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4399             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4400             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4401             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4402             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4403             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4404           }
4405
4406         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4407         continue;
4408       }
4409
4410     if (memcmp(type,mng_sRGB,4) == 0)
4411       {
4412         if (length == 1)
4413           {
4414             image->rendering_intent=
4415               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4416             image->gamma=1.000f/2.200f;
4417             image->chromaticity.red_primary.x=0.6400f;
4418             image->chromaticity.red_primary.y=0.3300f;
4419             image->chromaticity.green_primary.x=0.3000f;
4420             image->chromaticity.green_primary.y=0.6000f;
4421             image->chromaticity.blue_primary.x=0.1500f;
4422             image->chromaticity.blue_primary.y=0.0600f;
4423             image->chromaticity.white_point.x=0.3127f;
4424             image->chromaticity.white_point.y=0.3290f;
4425           }
4426
4427         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4428         continue;
4429       }
4430
4431     if (memcmp(type,mng_oFFs,4) == 0)
4432       {
4433         if (length > 8)
4434           {
4435             image->page.x=(ssize_t) mng_get_long(p);
4436             image->page.y=(ssize_t) mng_get_long(&p[4]);
4437
4438             if ((int) p[8] != 0)
4439               {
4440                 image->page.x/=10000;
4441                 image->page.y/=10000;
4442               }
4443           }
4444
4445         if (length)
4446           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4447
4448         continue;
4449       }
4450
4451     if (memcmp(type,mng_pHYs,4) == 0)
4452       {
4453         if (length > 8)
4454           {
4455             image->resolution.x=(double) mng_get_long(p);
4456             image->resolution.y=(double) mng_get_long(&p[4]);
4457             if ((int) p[8] == PNG_RESOLUTION_METER)
4458               {
4459                 image->units=PixelsPerCentimeterResolution;
4460                 image->resolution.x=image->resolution.x/100.0f;
4461                 image->resolution.y=image->resolution.y/100.0f;
4462               }
4463           }
4464
4465         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4466         continue;
4467       }
4468
4469 #if 0
4470     if (memcmp(type,mng_iCCP,4) == 0)
4471       {
4472         /* To do: */
4473         if (length)
4474           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4475
4476         continue;
4477       }
4478 #endif
4479
4480     if (length)
4481       chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4482
4483     if (memcmp(type,mng_IEND,4))
4484       continue;
4485
4486     break;
4487   }
4488
4489
4490   /* IEND found */
4491
4492   /*
4493     Finish up reading image data:
4494
4495        o read main image from color_blob.
4496
4497        o close color_blob.
4498
4499        o if (color_type has alpha)
4500             if alpha_encoding is PNG
4501                read secondary image from alpha_blob via ReadPNG
4502             if alpha_encoding is JPEG
4503                read secondary image from alpha_blob via ReadJPEG
4504
4505        o close alpha_blob.
4506
4507        o copy intensity of secondary image into
4508          alpha samples of main image.
4509
4510        o destroy the secondary image.
4511   */
4512
4513   (void) CloseBlob(color_image);
4514
4515   if (logging != MagickFalse)
4516     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4517       "    Reading jng_image from color_blob.");
4518
4519   (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4520     color_image->filename);
4521
4522   color_image_info->ping=MagickFalse;   /* To do: avoid this */
4523   jng_image=ReadImage(color_image_info,exception);
4524
4525   if (jng_image == (Image *) NULL)
4526     return((Image *) NULL);
4527
4528   (void) RelinquishUniqueFileResource(color_image->filename);
4529   color_image=DestroyImage(color_image);
4530   color_image_info=DestroyImageInfo(color_image_info);
4531
4532   if (jng_image == (Image *) NULL)
4533     return((Image *) NULL);
4534
4535   if (logging != MagickFalse)
4536     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4537       "    Copying jng_image pixels to main image.");
4538
4539   image->rows=jng_height;
4540   image->columns=jng_width;
4541
4542   for (y=0; y < (ssize_t) image->rows; y++)
4543   {
4544     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4545     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4546     for (x=(ssize_t) image->columns; x != 0; x--)
4547     {
4548       SetPixelRed(image,GetPixelRed(jng_image,s),q);
4549       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4550       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4551       q+=GetPixelChannels(image);
4552       s+=GetPixelChannels(jng_image);
4553     }
4554
4555     if (SyncAuthenticPixels(image,exception) == MagickFalse)
4556       break;
4557   }
4558
4559   jng_image=DestroyImage(jng_image);
4560
4561   if (image_info->ping == MagickFalse)
4562     {
4563      if (jng_color_type >= 12)
4564        {
4565          if (jng_alpha_compression_method == 0)
4566            {
4567              png_byte
4568                data[5];
4569              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4570              PNGType(data,mng_IEND);
4571              LogPNGChunk(logging,mng_IEND,0L);
4572              (void) WriteBlob(alpha_image,4,data);
4573              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4574            }
4575
4576          (void) CloseBlob(alpha_image);
4577
4578          if (logging != MagickFalse)
4579            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4580              "    Reading alpha from alpha_blob.");
4581
4582          (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4583            "%s",alpha_image->filename);
4584
4585          jng_image=ReadImage(alpha_image_info,exception);
4586
4587          if (jng_image != (Image *) NULL)
4588            for (y=0; y < (ssize_t) image->rows; y++)
4589            {
4590              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4591                exception);
4592              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4593
4594              if (image->alpha_trait == BlendPixelTrait)
4595                for (x=(ssize_t) image->columns; x != 0; x--)
4596                {
4597                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4598                   q+=GetPixelChannels(image);
4599                   s+=GetPixelChannels(jng_image);
4600                }
4601
4602              else
4603                for (x=(ssize_t) image->columns; x != 0; x--)
4604                {
4605                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4606                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
4607                     image->alpha_trait=BlendPixelTrait;
4608                   q+=GetPixelChannels(image);
4609                   s+=GetPixelChannels(jng_image);
4610                }
4611
4612              if (SyncAuthenticPixels(image,exception) == MagickFalse)
4613                break;
4614            }
4615          (void) RelinquishUniqueFileResource(alpha_image->filename);
4616          alpha_image=DestroyImage(alpha_image);
4617          alpha_image_info=DestroyImageInfo(alpha_image_info);
4618          if (jng_image != (Image *) NULL)
4619            jng_image=DestroyImage(jng_image);
4620        }
4621     }
4622
4623   /* Read the JNG image.  */
4624
4625   if (mng_info->mng_type == 0)
4626     {
4627       mng_info->mng_width=jng_width;
4628       mng_info->mng_height=jng_height;
4629     }
4630
4631   if (image->page.width == 0 && image->page.height == 0)
4632     {
4633       image->page.width=jng_width;
4634       image->page.height=jng_height;
4635     }
4636
4637   if (image->page.x == 0 && image->page.y == 0)
4638     {
4639       image->page.x=mng_info->x_off[mng_info->object_id];
4640       image->page.y=mng_info->y_off[mng_info->object_id];
4641     }
4642
4643   else
4644     {
4645       image->page.y=mng_info->y_off[mng_info->object_id];
4646     }
4647
4648   mng_info->image_found++;
4649   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4650     2*GetBlobSize(image));
4651
4652   if (logging != MagickFalse)
4653     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4654       "  exit ReadOneJNGImage()");
4655
4656   return(image);
4657 }
4658
4659 /*
4660 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4661 %                                                                             %
4662 %                                                                             %
4663 %                                                                             %
4664 %   R e a d J N G I m a g e                                                   %
4665 %                                                                             %
4666 %                                                                             %
4667 %                                                                             %
4668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4669 %
4670 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4671 %  (including the 8-byte signature)  and returns it.  It allocates the memory
4672 %  necessary for the new Image structure and returns a pointer to the new
4673 %  image.
4674 %
4675 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4676 %
4677 %  The format of the ReadJNGImage method is:
4678 %
4679 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4680 %         *exception)
4681 %
4682 %  A description of each parameter follows:
4683 %
4684 %    o image_info: the image info.
4685 %
4686 %    o exception: return any errors or warnings in this structure.
4687 %
4688 */
4689
4690 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4691 {
4692   Image
4693     *image,
4694     *previous;
4695
4696   MagickBooleanType
4697     have_mng_structure,
4698     logging,
4699     status;
4700
4701   MngInfo
4702     *mng_info;
4703
4704   char
4705     magic_number[MaxTextExtent];
4706
4707   size_t
4708     count;
4709
4710   /*
4711     Open image file.
4712   */
4713   assert(image_info != (const ImageInfo *) NULL);
4714   assert(image_info->signature == MagickSignature);
4715   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4716   assert(exception != (ExceptionInfo *) NULL);
4717   assert(exception->signature == MagickSignature);
4718   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4719   image=AcquireImage(image_info,exception);
4720   mng_info=(MngInfo *) NULL;
4721   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4722
4723   if (status == MagickFalse)
4724     return((Image *) NULL);
4725
4726   if (LocaleCompare(image_info->magick,"JNG") != 0)
4727     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4728
4729   /* Verify JNG signature.  */
4730
4731   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4732
4733   if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4734     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4735
4736   /* Allocate a MngInfo structure.  */
4737
4738   have_mng_structure=MagickFalse;
4739   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4740
4741   if (mng_info == (MngInfo *) NULL)
4742     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4743
4744   /* Initialize members of the MngInfo structure.  */
4745
4746   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4747   have_mng_structure=MagickTrue;
4748
4749   mng_info->image=image;
4750   previous=image;
4751   image=ReadOneJNGImage(mng_info,image_info,exception);
4752   MngInfoFreeStruct(mng_info,&have_mng_structure);
4753
4754   if (image == (Image *) NULL)
4755     {
4756       if (IsImageObject(previous) != MagickFalse)
4757         {
4758           (void) CloseBlob(previous);
4759           (void) DestroyImageList(previous);
4760         }
4761
4762       if (logging != MagickFalse)
4763         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4764           "exit ReadJNGImage() with error");
4765
4766       return((Image *) NULL);
4767     }
4768   (void) CloseBlob(image);
4769
4770   if (image->columns == 0 || image->rows == 0)
4771     {
4772       if (logging != MagickFalse)
4773         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4774           "exit ReadJNGImage() with error");
4775
4776       ThrowReaderException(CorruptImageError,"CorruptImage");
4777     }
4778
4779   if (logging != MagickFalse)
4780     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4781
4782   return(image);
4783 }
4784 #endif
4785
4786 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4787 {
4788   char
4789     page_geometry[MaxTextExtent];
4790
4791   Image
4792     *image,
4793     *previous;
4794
4795   MagickBooleanType
4796     logging,
4797     have_mng_structure;
4798
4799   volatile int
4800     first_mng_object,
4801     object_id,
4802     term_chunk_found,
4803     skip_to_iend;
4804
4805   volatile ssize_t
4806     image_count=0;
4807
4808   MagickBooleanType
4809     status;
4810
4811   MagickOffsetType
4812     offset;
4813
4814   MngInfo
4815     *mng_info;
4816
4817   MngBox
4818     default_fb,
4819     fb,
4820     previous_fb;
4821
4822 #if defined(MNG_INSERT_LAYERS)
4823   PixelInfo
4824     mng_background_color;
4825 #endif
4826
4827   register unsigned char
4828     *p;
4829
4830   register ssize_t
4831     i;
4832
4833   size_t
4834     count;
4835
4836   ssize_t
4837     loop_level;
4838
4839   volatile short
4840     skipping_loop;
4841
4842 #if defined(MNG_INSERT_LAYERS)
4843   unsigned int
4844     mandatory_back=0;
4845 #endif
4846
4847   volatile unsigned int
4848 #ifdef MNG_OBJECT_BUFFERS
4849     mng_background_object=0,
4850 #endif
4851     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4852
4853   size_t
4854     default_frame_timeout,
4855     frame_timeout,
4856 #if defined(MNG_INSERT_LAYERS)
4857     image_height,
4858     image_width,
4859 #endif
4860     length;
4861
4862   /* These delays are all measured in image ticks_per_second,
4863    * not in MNG ticks_per_second
4864    */
4865   volatile size_t
4866     default_frame_delay,
4867     final_delay,
4868     final_image_delay,
4869     frame_delay,
4870 #if defined(MNG_INSERT_LAYERS)
4871     insert_layers,
4872 #endif
4873     mng_iterations=1,
4874     simplicity=0,
4875     subframe_height=0,
4876     subframe_width=0;
4877
4878   previous_fb.top=0;
4879   previous_fb.bottom=0;
4880   previous_fb.left=0;
4881   previous_fb.right=0;
4882   default_fb.top=0;
4883   default_fb.bottom=0;
4884   default_fb.left=0;
4885   default_fb.right=0;
4886
4887   /* Open image file.  */
4888
4889   assert(image_info != (const ImageInfo *) NULL);
4890   assert(image_info->signature == MagickSignature);
4891   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4892   assert(exception != (ExceptionInfo *) NULL);
4893   assert(exception->signature == MagickSignature);
4894   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4895   image=AcquireImage(image_info,exception);
4896   mng_info=(MngInfo *) NULL;
4897   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4898
4899   if (status == MagickFalse)
4900     return((Image *) NULL);
4901
4902   first_mng_object=MagickFalse;
4903   skipping_loop=(-1);
4904   have_mng_structure=MagickFalse;
4905
4906   /* Allocate a MngInfo structure.  */
4907
4908   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4909
4910   if (mng_info == (MngInfo *) NULL)
4911     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4912
4913   /* Initialize members of the MngInfo structure.  */
4914
4915   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4916   mng_info->image=image;
4917   have_mng_structure=MagickTrue;
4918
4919   if (LocaleCompare(image_info->magick,"MNG") == 0)
4920     {
4921       char
4922         magic_number[MaxTextExtent];
4923
4924       /* Verify MNG signature.  */
4925       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4926       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4927         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4928
4929       /* Initialize some nonzero members of the MngInfo structure.  */
4930       for (i=0; i < MNG_MAX_OBJECTS; i++)
4931       {
4932         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4933         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4934       }
4935       mng_info->exists[0]=MagickTrue;
4936     }
4937
4938   first_mng_object=MagickTrue;
4939   mng_type=0;
4940 #if defined(MNG_INSERT_LAYERS)
4941   insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4942 #endif
4943   default_frame_delay=0;
4944   default_frame_timeout=0;
4945   frame_delay=0;
4946   final_delay=1;
4947   mng_info->ticks_per_second=1UL*image->ticks_per_second;
4948   object_id=0;
4949   skip_to_iend=MagickFalse;
4950   term_chunk_found=MagickFalse;
4951   mng_info->framing_mode=1;
4952 #if defined(MNG_INSERT_LAYERS)
4953   mandatory_back=MagickFalse;
4954 #endif
4955 #if defined(MNG_INSERT_LAYERS)
4956   mng_background_color=image->background_color;
4957 #endif
4958   default_fb=mng_info->frame;
4959   previous_fb=mng_info->frame;
4960   do
4961   {
4962     char
4963       type[MaxTextExtent];
4964
4965     if (LocaleCompare(image_info->magick,"MNG") == 0)
4966       {
4967         unsigned char
4968           *chunk;
4969
4970         /*
4971           Read a new chunk.
4972         */
4973         type[0]='\0';
4974         (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4975         length=ReadBlobMSBLong(image);
4976         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4977
4978         if (logging != MagickFalse)
4979           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4980            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
4981            type[0],type[1],type[2],type[3],(double) length);
4982
4983         if (length > PNG_UINT_31_MAX)
4984           status=MagickFalse;
4985
4986         if (count == 0)
4987           ThrowReaderException(CorruptImageError,"CorruptImage");
4988
4989         p=NULL;
4990         chunk=(unsigned char *) NULL;
4991
4992         if (length)
4993           {
4994             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4995
4996             if (chunk == (unsigned char *) NULL)
4997               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4998
4999             for (i=0; i < (ssize_t) length; i++)
5000               chunk[i]=(unsigned char) ReadBlobByte(image);
5001
5002             p=chunk;
5003           }
5004
5005         (void) ReadBlobMSBLong(image);  /* read crc word */
5006
5007 #if !defined(JNG_SUPPORTED)
5008         if (memcmp(type,mng_JHDR,4) == 0)
5009           {
5010             skip_to_iend=MagickTrue;
5011
5012             if (mng_info->jhdr_warning == 0)
5013               (void) ThrowMagickException(exception,GetMagickModule(),
5014                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5015
5016             mng_info->jhdr_warning++;
5017           }
5018 #endif
5019         if (memcmp(type,mng_DHDR,4) == 0)
5020           {
5021             skip_to_iend=MagickTrue;
5022
5023             if (mng_info->dhdr_warning == 0)
5024               (void) ThrowMagickException(exception,GetMagickModule(),
5025                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5026
5027             mng_info->dhdr_warning++;
5028           }
5029         if (memcmp(type,mng_MEND,4) == 0)
5030           break;
5031
5032         if (skip_to_iend)
5033           {
5034             if (memcmp(type,mng_IEND,4) == 0)
5035               skip_to_iend=MagickFalse;
5036
5037             if (length)
5038               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5039
5040             if (logging != MagickFalse)
5041               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5042                 "  Skip to IEND.");
5043
5044             continue;
5045           }
5046
5047         if (memcmp(type,mng_MHDR,4) == 0)
5048           {
5049             mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5050                 (p[2] << 8) | p[3]);
5051
5052             mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5053                 (p[6] << 8) | p[7]);
5054
5055             if (logging != MagickFalse)
5056               {
5057                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5058                   "  MNG width: %.20g",(double) mng_info->mng_width);
5059                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5060                   "  MNG height: %.20g",(double) mng_info->mng_height);
5061               }
5062
5063             p+=8;
5064             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5065
5066             if (mng_info->ticks_per_second == 0)
5067               default_frame_delay=0;
5068
5069             else
5070               default_frame_delay=1UL*image->ticks_per_second/
5071                 mng_info->ticks_per_second;
5072
5073             frame_delay=default_frame_delay;
5074             simplicity=0;
5075
5076             if (length > 16)
5077               {
5078                 p+=16;
5079                 simplicity=(size_t) mng_get_long(p);
5080               }
5081
5082             mng_type=1;    /* Full MNG */
5083
5084             if ((simplicity != 0) && ((simplicity | 11) == 11))
5085               mng_type=2; /* LC */
5086
5087             if ((simplicity != 0) && ((simplicity | 9) == 9))
5088               mng_type=3; /* VLC */
5089
5090 #if defined(MNG_INSERT_LAYERS)
5091             if (mng_type != 3)
5092               insert_layers=MagickTrue;
5093 #endif
5094             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5095               {
5096                 /* Allocate next image structure.  */
5097                 AcquireNextImage(image_info,image,exception);
5098
5099                 if (GetNextImageInList(image) == (Image *) NULL)
5100                   return((Image *) NULL);
5101
5102                 image=SyncNextImageInList(image);
5103                 mng_info->image=image;
5104               }
5105
5106             if ((mng_info->mng_width > 65535L) ||
5107                 (mng_info->mng_height > 65535L))
5108               ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5109
5110             (void) FormatLocaleString(page_geometry,MaxTextExtent,
5111               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5112               mng_info->mng_height);
5113
5114             mng_info->frame.left=0;
5115             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5116             mng_info->frame.top=0;
5117             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5118             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5119
5120             for (i=0; i < MNG_MAX_OBJECTS; i++)
5121               mng_info->object_clip[i]=mng_info->frame;
5122
5123             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5124             continue;
5125           }
5126
5127         if (memcmp(type,mng_TERM,4) == 0)
5128           {
5129             int
5130               repeat=0;
5131
5132
5133             if (length)
5134               repeat=p[0];
5135
5136             if (repeat == 3)
5137               {
5138                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5139                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5140
5141                 if (mng_iterations == PNG_UINT_31_MAX)
5142                   mng_iterations=0;
5143
5144                 image->iterations=mng_iterations;
5145                 term_chunk_found=MagickTrue;
5146               }
5147
5148             if (logging != MagickFalse)
5149               {
5150                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5151                   "    repeat=%d",repeat);
5152
5153                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5154                   "    final_delay=%.20g",(double) final_delay);
5155
5156                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5157                   "    image->iterations=%.20g",(double) image->iterations);
5158               }
5159
5160             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5161             continue;
5162           }
5163         if (memcmp(type,mng_DEFI,4) == 0)
5164           {
5165             if (mng_type == 3)
5166               (void) ThrowMagickException(exception,GetMagickModule(),
5167                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5168                 image->filename);
5169
5170             object_id=(p[0] << 8) | p[1];
5171
5172             if (mng_type == 2 && object_id != 0)
5173               (void) ThrowMagickException(exception,GetMagickModule(),
5174                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5175                 image->filename);
5176
5177             if (object_id > MNG_MAX_OBJECTS)
5178               {
5179                 /*
5180                   Instead of using a warning we should allocate a larger
5181                   MngInfo structure and continue.
5182                 */
5183                 (void) ThrowMagickException(exception,GetMagickModule(),
5184                   CoderError,"object id too large","`%s'",image->filename);
5185                 object_id=MNG_MAX_OBJECTS;
5186               }
5187
5188             if (mng_info->exists[object_id])
5189               if (mng_info->frozen[object_id])
5190                 {
5191                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5192                   (void) ThrowMagickException(exception,
5193                     GetMagickModule(),CoderError,
5194                     "DEFI cannot redefine a frozen MNG object","`%s'",
5195                     image->filename);
5196                   continue;
5197                 }
5198
5199             mng_info->exists[object_id]=MagickTrue;
5200
5201             if (length > 2)
5202               mng_info->invisible[object_id]=p[2];
5203
5204             /*
5205               Extract object offset info.
5206             */
5207             if (length > 11)
5208               {
5209                 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5210                     (p[5] << 16) | (p[6] << 8) | p[7]);
5211
5212                 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5213                     (p[9] << 16) | (p[10] << 8) | p[11]);
5214
5215                 if (logging != MagickFalse)
5216                   {
5217                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5218                       "  x_off[%d]: %.20g",object_id,(double)
5219                       mng_info->x_off[object_id]);
5220
5221                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5222                       "  y_off[%d]: %.20g",object_id,(double)
5223                       mng_info->y_off[object_id]);
5224                   }
5225               }
5226
5227             /*
5228               Extract object clipping info.
5229             */
5230             if (length > 27)
5231               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5232                 &p[12]);
5233
5234             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5235             continue;
5236           }
5237         if (memcmp(type,mng_bKGD,4) == 0)
5238           {
5239             mng_info->have_global_bkgd=MagickFalse;
5240
5241             if (length > 5)
5242               {
5243                 mng_info->mng_global_bkgd.red=
5244                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5245
5246                 mng_info->mng_global_bkgd.green=
5247                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5248
5249                 mng_info->mng_global_bkgd.blue=
5250                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5251
5252                 mng_info->have_global_bkgd=MagickTrue;
5253               }
5254
5255             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5256             continue;
5257           }
5258         if (memcmp(type,mng_BACK,4) == 0)
5259           {
5260 #if defined(MNG_INSERT_LAYERS)
5261             if (length > 6)
5262               mandatory_back=p[6];
5263
5264             else
5265               mandatory_back=0;
5266
5267             if (mandatory_back && length > 5)
5268               {
5269                 mng_background_color.red=
5270                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5271
5272                 mng_background_color.green=
5273                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5274
5275                 mng_background_color.blue=
5276                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5277
5278                 mng_background_color.alpha=OpaqueAlpha;
5279               }
5280
5281 #ifdef MNG_OBJECT_BUFFERS
5282             if (length > 8)
5283               mng_background_object=(p[7] << 8) | p[8];
5284 #endif
5285 #endif
5286             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5287             continue;
5288           }
5289
5290         if (memcmp(type,mng_PLTE,4) == 0)
5291           {
5292             /* Read global PLTE.  */
5293
5294             if (length && (length < 769))
5295               {
5296                 if (mng_info->global_plte == (png_colorp) NULL)
5297                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5298                     sizeof(*mng_info->global_plte));
5299
5300                 for (i=0; i < (ssize_t) (length/3); i++)
5301                 {
5302                   mng_info->global_plte[i].red=p[3*i];
5303                   mng_info->global_plte[i].green=p[3*i+1];
5304                   mng_info->global_plte[i].blue=p[3*i+2];
5305                 }
5306
5307                 mng_info->global_plte_length=(unsigned int) (length/3);
5308               }
5309 #ifdef MNG_LOOSE
5310             for ( ; i < 256; i++)
5311             {
5312               mng_info->global_plte[i].red=i;
5313               mng_info->global_plte[i].green=i;
5314               mng_info->global_plte[i].blue=i;
5315             }
5316
5317             if (length)
5318               mng_info->global_plte_length=256;
5319 #endif
5320             else
5321               mng_info->global_plte_length=0;
5322
5323             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5324             continue;
5325           }
5326
5327         if (memcmp(type,mng_tRNS,4) == 0)
5328           {
5329             /* read global tRNS */
5330
5331             if (length < 257)
5332               for (i=0; i < (ssize_t) length; i++)
5333                 mng_info->global_trns[i]=p[i];
5334
5335 #ifdef MNG_LOOSE
5336             for ( ; i < 256; i++)
5337               mng_info->global_trns[i]=255;
5338 #endif
5339             mng_info->global_trns_length=(unsigned int) length;
5340             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5341             continue;
5342           }
5343         if (memcmp(type,mng_gAMA,4) == 0)
5344           {
5345             if (length == 4)
5346               {
5347                 ssize_t
5348                   igamma;
5349
5350                 igamma=mng_get_long(p);
5351                 mng_info->global_gamma=((float) igamma)*0.00001;
5352                 mng_info->have_global_gama=MagickTrue;
5353               }
5354
5355             else
5356               mng_info->have_global_gama=MagickFalse;
5357
5358             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5359             continue;
5360           }
5361
5362         if (memcmp(type,mng_cHRM,4) == 0)
5363           {
5364             /* Read global cHRM */
5365
5366             if (length == 32)
5367               {
5368                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5369                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5370                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5371                 mng_info->global_chrm.red_primary.y=0.00001*
5372                   mng_get_long(&p[12]);
5373                 mng_info->global_chrm.green_primary.x=0.00001*
5374                   mng_get_long(&p[16]);
5375                 mng_info->global_chrm.green_primary.y=0.00001*
5376                   mng_get_long(&p[20]);
5377                 mng_info->global_chrm.blue_primary.x=0.00001*
5378                   mng_get_long(&p[24]);
5379                 mng_info->global_chrm.blue_primary.y=0.00001*
5380                   mng_get_long(&p[28]);
5381                 mng_info->have_global_chrm=MagickTrue;
5382               }
5383             else
5384               mng_info->have_global_chrm=MagickFalse;
5385
5386             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5387             continue;
5388           }
5389
5390         if (memcmp(type,mng_sRGB,4) == 0)
5391           {
5392             /*
5393               Read global sRGB.
5394             */
5395             if (length)
5396               {
5397                 mng_info->global_srgb_intent=
5398                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5399                 mng_info->have_global_srgb=MagickTrue;
5400               }
5401             else
5402               mng_info->have_global_srgb=MagickFalse;
5403
5404             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5405             continue;
5406           }
5407
5408         if (memcmp(type,mng_iCCP,4) == 0)
5409           {
5410             /* To do: */
5411
5412             /*
5413               Read global iCCP.
5414             */
5415             if (length)
5416               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5417
5418             continue;
5419           }
5420
5421         if (memcmp(type,mng_FRAM,4) == 0)
5422           {
5423             if (mng_type == 3)
5424               (void) ThrowMagickException(exception,GetMagickModule(),
5425                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5426                 image->filename);
5427
5428             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5429               image->delay=frame_delay;
5430
5431             frame_delay=default_frame_delay;
5432             frame_timeout=default_frame_timeout;
5433             fb=default_fb;
5434
5435             if (length)
5436               if (p[0])
5437                 mng_info->framing_mode=p[0];
5438
5439             if (logging != MagickFalse)
5440               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5441                 "    Framing_mode=%d",mng_info->framing_mode);
5442
5443             if (length > 6)
5444               {
5445                 /* Note the delay and frame clipping boundaries.  */
5446
5447                 p++; /* framing mode */
5448
5449                 while (*p && ((p-chunk) < (ssize_t) length))
5450                   p++;  /* frame name */
5451
5452                 p++;  /* frame name terminator */
5453
5454                 if ((p-chunk) < (ssize_t) (length-4))
5455                   {
5456                     int
5457                       change_delay,
5458                       change_timeout,
5459                       change_clipping;
5460
5461                     change_delay=(*p++);
5462                     change_timeout=(*p++);
5463                     change_clipping=(*p++);
5464                     p++; /* change_sync */
5465
5466                     if (change_delay)
5467                       {
5468                         frame_delay=1UL*image->ticks_per_second*
5469                           mng_get_long(p);
5470
5471                         if (mng_info->ticks_per_second != 0)
5472                           frame_delay/=mng_info->ticks_per_second;
5473
5474                         else
5475                           frame_delay=PNG_UINT_31_MAX;
5476
5477                         if (change_delay == 2)
5478                           default_frame_delay=frame_delay;
5479
5480                         p+=4;
5481
5482                         if (logging != MagickFalse)
5483                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5484                             "    Framing_delay=%.20g",(double) frame_delay);
5485                       }
5486
5487                     if (change_timeout)
5488                       {
5489                         frame_timeout=1UL*image->ticks_per_second*
5490                           mng_get_long(p);
5491
5492                         if (mng_info->ticks_per_second != 0)
5493                           frame_timeout/=mng_info->ticks_per_second;
5494
5495                         else
5496                           frame_timeout=PNG_UINT_31_MAX;
5497
5498                         if (change_delay == 2)
5499                           default_frame_timeout=frame_timeout;
5500
5501                         p+=4;
5502
5503                         if (logging != MagickFalse)
5504                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5505                             "    Framing_timeout=%.20g",(double) frame_timeout);
5506                       }
5507
5508                     if (change_clipping)
5509                       {
5510                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5511                         p+=17;
5512                         previous_fb=fb;
5513
5514                         if (logging != MagickFalse)
5515                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5516                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5517                             (double) fb.left,(double) fb.right,(double) fb.top,
5518                             (double) fb.bottom);
5519
5520                         if (change_clipping == 2)
5521                           default_fb=fb;
5522                       }
5523                   }
5524               }
5525             mng_info->clip=fb;
5526             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5527
5528             subframe_width=(size_t) (mng_info->clip.right
5529                -mng_info->clip.left);
5530
5531             subframe_height=(size_t) (mng_info->clip.bottom
5532                -mng_info->clip.top);
5533             /*
5534               Insert a background layer behind the frame if framing_mode is 4.
5535             */
5536 #if defined(MNG_INSERT_LAYERS)
5537             if (logging != MagickFalse)
5538               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5539                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
5540                 subframe_width,(double) subframe_height);
5541
5542             if (insert_layers && (mng_info->framing_mode == 4) &&
5543                 (subframe_width) && (subframe_height))
5544               {
5545                 /* Allocate next image structure.  */
5546                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5547                   {
5548                     AcquireNextImage(image_info,image,exception);
5549
5550                     if (GetNextImageInList(image) == (Image *) NULL)
5551                       {
5552                         image=DestroyImageList(image);
5553                         MngInfoFreeStruct(mng_info,&have_mng_structure);
5554                         return((Image *) NULL);
5555                       }
5556
5557                     image=SyncNextImageInList(image);
5558                   }
5559
5560                 mng_info->image=image;
5561
5562                 if (term_chunk_found)
5563                   {
5564                     image->start_loop=MagickTrue;
5565                     image->iterations=mng_iterations;
5566                     term_chunk_found=MagickFalse;
5567                   }
5568
5569                 else
5570                     image->start_loop=MagickFalse;
5571
5572                 image->columns=subframe_width;
5573                 image->rows=subframe_height;
5574                 image->page.width=subframe_width;
5575                 image->page.height=subframe_height;
5576                 image->page.x=mng_info->clip.left;
5577                 image->page.y=mng_info->clip.top;
5578                 image->background_color=mng_background_color;
5579                 image->alpha_trait=UndefinedPixelTrait;
5580                 image->delay=0;
5581                 (void) SetImageBackgroundColor(image,exception);
5582
5583                 if (logging != MagickFalse)
5584                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5585                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5586                     (double) mng_info->clip.left,(double) mng_info->clip.right,
5587                     (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5588               }
5589 #endif
5590             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5591             continue;
5592           }
5593         if (memcmp(type,mng_CLIP,4) == 0)
5594           {
5595             unsigned int
5596               first_object,
5597               last_object;
5598
5599             /*
5600               Read CLIP.
5601             */
5602             first_object=(p[0] << 8) | p[1];
5603             last_object=(p[2] << 8) | p[3];
5604
5605             for (i=(int) first_object; i <= (int) last_object; i++)
5606             {
5607               if (mng_info->exists[i] && !mng_info->frozen[i])
5608                 {
5609                   MngBox
5610                     box;
5611
5612                   box=mng_info->object_clip[i];
5613                   mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5614                 }
5615             }
5616
5617             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5618             continue;
5619           }
5620         if (memcmp(type,mng_SAVE,4) == 0)
5621           {
5622             for (i=1; i < MNG_MAX_OBJECTS; i++)
5623               if (mng_info->exists[i])
5624                 {
5625                  mng_info->frozen[i]=MagickTrue;
5626 #ifdef MNG_OBJECT_BUFFERS
5627                  if (mng_info->ob[i] != (MngBuffer *) NULL)
5628                     mng_info->ob[i]->frozen=MagickTrue;
5629 #endif
5630                 }
5631
5632             if (length)
5633               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5634
5635             continue;
5636           }
5637
5638         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5639           {
5640             /* Read DISC or SEEK.  */
5641
5642             if ((length == 0) || !memcmp(type,mng_SEEK,4))
5643               {
5644                 for (i=1; i < MNG_MAX_OBJECTS; i++)
5645                   MngInfoDiscardObject(mng_info,i);
5646               }
5647
5648             else
5649               {
5650                 register ssize_t
5651                   j;
5652
5653                 for (j=0; j < (ssize_t) length; j+=2)
5654                 {
5655                   i=p[j] << 8 | p[j+1];
5656                   MngInfoDiscardObject(mng_info,i);
5657                 }
5658               }
5659
5660             if (length)
5661               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5662
5663             continue;
5664           }
5665
5666         if (memcmp(type,mng_MOVE,4) == 0)
5667           {
5668             size_t
5669               first_object,
5670               last_object;
5671
5672             /* read MOVE */
5673
5674             first_object=(p[0] << 8) | p[1];
5675             last_object=(p[2] << 8) | p[3];
5676             for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5677             {
5678               if (mng_info->exists[i] && !mng_info->frozen[i])
5679                 {
5680                   MngPair
5681                     new_pair;
5682
5683                   MngPair
5684                     old_pair;
5685
5686                   old_pair.a=mng_info->x_off[i];
5687                   old_pair.b=mng_info->y_off[i];
5688                   new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5689                   mng_info->x_off[i]=new_pair.a;
5690                   mng_info->y_off[i]=new_pair.b;
5691                 }
5692             }
5693
5694             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5695             continue;
5696           }
5697
5698         if (memcmp(type,mng_LOOP,4) == 0)
5699           {
5700             ssize_t loop_iters=1;
5701             loop_level=chunk[0];
5702             mng_info->loop_active[loop_level]=1;  /* mark loop active */
5703
5704             /* Record starting point.  */
5705             loop_iters=mng_get_long(&chunk[1]);
5706
5707             if (logging != MagickFalse)
5708               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5709                 "  LOOP level %.20g has %.20g iterations ",(double) loop_level,
5710                 (double) loop_iters);
5711
5712             if (loop_iters == 0)
5713               skipping_loop=loop_level;
5714
5715             else
5716               {
5717                 mng_info->loop_jump[loop_level]=TellBlob(image);
5718                 mng_info->loop_count[loop_level]=loop_iters;
5719               }
5720
5721             mng_info->loop_iteration[loop_level]=0;
5722             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5723             continue;
5724           }
5725
5726         if (memcmp(type,mng_ENDL,4) == 0)
5727           {
5728             loop_level=chunk[0];
5729
5730             if (skipping_loop > 0)
5731               {
5732                 if (skipping_loop == loop_level)
5733                   {
5734                     /*
5735                       Found end of zero-iteration loop.
5736                     */
5737                     skipping_loop=(-1);
5738                     mng_info->loop_active[loop_level]=0;
5739                   }
5740               }
5741
5742             else
5743               {
5744                 if (mng_info->loop_active[loop_level] == 1)
5745                   {
5746                     mng_info->loop_count[loop_level]--;
5747                     mng_info->loop_iteration[loop_level]++;
5748
5749                     if (logging != MagickFalse)
5750                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5751                         "  ENDL: LOOP level %.20g has %.20g remaining iters ",
5752                         (double) loop_level,(double)
5753                         mng_info->loop_count[loop_level]);
5754
5755                     if (mng_info->loop_count[loop_level] != 0)
5756                       {
5757                         offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5758                           SEEK_SET);
5759
5760                         if (offset < 0)
5761                           ThrowReaderException(CorruptImageError,
5762                             "ImproperImageHeader");
5763                       }
5764
5765                     else
5766                       {
5767                         short
5768                           last_level;
5769
5770                         /*
5771                           Finished loop.
5772                         */
5773                         mng_info->loop_active[loop_level]=0;
5774                         last_level=(-1);
5775                         for (i=0; i < loop_level; i++)
5776                           if (mng_info->loop_active[i] == 1)
5777                             last_level=(short) i;
5778                         loop_level=last_level;
5779                       }
5780                   }
5781               }
5782
5783             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5784             continue;
5785           }
5786
5787         if (memcmp(type,mng_CLON,4) == 0)
5788           {
5789             if (mng_info->clon_warning == 0)
5790               (void) ThrowMagickException(exception,GetMagickModule(),
5791                 CoderError,"CLON is not implemented yet","`%s'",
5792                 image->filename);
5793
5794             mng_info->clon_warning++;
5795           }
5796
5797         if (memcmp(type,mng_MAGN,4) == 0)
5798           {
5799             png_uint_16
5800               magn_first,
5801               magn_last,
5802               magn_mb,
5803               magn_ml,
5804               magn_mr,
5805               magn_mt,
5806               magn_mx,
5807               magn_my,
5808               magn_methx,
5809               magn_methy;
5810
5811             if (length > 1)
5812               magn_first=(p[0] << 8) | p[1];
5813
5814             else
5815               magn_first=0;
5816
5817             if (length > 3)
5818               magn_last=(p[2] << 8) | p[3];
5819
5820             else
5821               magn_last=magn_first;
5822 #ifndef MNG_OBJECT_BUFFERS
5823             if (magn_first || magn_last)
5824               if (mng_info->magn_warning == 0)
5825                 {
5826                   (void) ThrowMagickException(exception,
5827                      GetMagickModule(),CoderError,
5828                      "MAGN is not implemented yet for nonzero objects",
5829                      "`%s'",image->filename);
5830
5831                    mng_info->magn_warning++;
5832                 }
5833 #endif
5834             if (length > 4)
5835               magn_methx=p[4];
5836
5837             else
5838               magn_methx=0;
5839
5840             if (length > 6)
5841               magn_mx=(p[5] << 8) | p[6];
5842
5843             else
5844               magn_mx=1;
5845
5846             if (magn_mx == 0)
5847               magn_mx=1;
5848
5849             if (length > 8)
5850               magn_my=(p[7] << 8) | p[8];
5851
5852             else
5853               magn_my=magn_mx;
5854
5855             if (magn_my == 0)
5856               magn_my=1;
5857
5858             if (length > 10)
5859               magn_ml=(p[9] << 8) | p[10];
5860
5861             else
5862               magn_ml=magn_mx;
5863
5864             if (magn_ml == 0)
5865               magn_ml=1;
5866
5867             if (length > 12)
5868               magn_mr=(p[11] << 8) | p[12];
5869
5870             else
5871               magn_mr=magn_mx;
5872
5873             if (magn_mr == 0)
5874               magn_mr=1;
5875
5876             if (length > 14)
5877               magn_mt=(p[13] << 8) | p[14];
5878
5879             else
5880               magn_mt=magn_my;
5881
5882             if (magn_mt == 0)
5883               magn_mt=1;
5884
5885             if (length > 16)
5886               magn_mb=(p[15] << 8) | p[16];
5887
5888             else
5889               magn_mb=magn_my;
5890
5891             if (magn_mb == 0)
5892               magn_mb=1;
5893
5894             if (length > 17)
5895               magn_methy=p[17];
5896
5897             else
5898               magn_methy=magn_methx;
5899
5900
5901             if (magn_methx > 5 || magn_methy > 5)
5902               if (mng_info->magn_warning == 0)
5903                 {
5904                   (void) ThrowMagickException(exception,
5905                      GetMagickModule(),CoderError,
5906                      "Unknown MAGN method in MNG datastream","`%s'",
5907                      image->filename);
5908
5909                    mng_info->magn_warning++;
5910                 }
5911 #ifdef MNG_OBJECT_BUFFERS
5912           /* Magnify existing objects in the range magn_first to magn_last */
5913 #endif
5914             if (magn_first == 0 || magn_last == 0)
5915               {
5916                 /* Save the magnification factors for object 0 */
5917                 mng_info->magn_mb=magn_mb;
5918                 mng_info->magn_ml=magn_ml;
5919                 mng_info->magn_mr=magn_mr;
5920                 mng_info->magn_mt=magn_mt;
5921                 mng_info->magn_mx=magn_mx;
5922                 mng_info->magn_my=magn_my;
5923                 mng_info->magn_methx=magn_methx;
5924                 mng_info->magn_methy=magn_methy;
5925               }
5926           }
5927
5928         if (memcmp(type,mng_PAST,4) == 0)
5929           {
5930             if (mng_info->past_warning == 0)
5931               (void) ThrowMagickException(exception,GetMagickModule(),
5932                 CoderError,"PAST is not implemented yet","`%s'",
5933                 image->filename);
5934
5935             mng_info->past_warning++;
5936           }
5937
5938         if (memcmp(type,mng_SHOW,4) == 0)
5939           {
5940             if (mng_info->show_warning == 0)
5941               (void) ThrowMagickException(exception,GetMagickModule(),
5942                 CoderError,"SHOW is not implemented yet","`%s'",
5943                 image->filename);
5944
5945             mng_info->show_warning++;
5946           }
5947
5948         if (memcmp(type,mng_sBIT,4) == 0)
5949           {
5950             if (length < 4)
5951               mng_info->have_global_sbit=MagickFalse;
5952
5953             else
5954               {
5955                 mng_info->global_sbit.gray=p[0];
5956                 mng_info->global_sbit.red=p[0];
5957                 mng_info->global_sbit.green=p[1];
5958                 mng_info->global_sbit.blue=p[2];
5959                 mng_info->global_sbit.alpha=p[3];
5960                 mng_info->have_global_sbit=MagickTrue;
5961              }
5962           }
5963         if (memcmp(type,mng_pHYs,4) == 0)
5964           {
5965             if (length > 8)
5966               {
5967                 mng_info->global_x_pixels_per_unit=
5968                     (size_t) mng_get_long(p);
5969                 mng_info->global_y_pixels_per_unit=
5970                     (size_t) mng_get_long(&p[4]);
5971                 mng_info->global_phys_unit_type=p[8];
5972                 mng_info->have_global_phys=MagickTrue;
5973               }
5974
5975             else
5976               mng_info->have_global_phys=MagickFalse;
5977           }
5978         if (memcmp(type,mng_pHYg,4) == 0)
5979           {
5980             if (mng_info->phyg_warning == 0)
5981               (void) ThrowMagickException(exception,GetMagickModule(),
5982                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5983
5984             mng_info->phyg_warning++;
5985           }
5986         if (memcmp(type,mng_BASI,4) == 0)
5987           {
5988             skip_to_iend=MagickTrue;
5989
5990             if (mng_info->basi_warning == 0)
5991               (void) ThrowMagickException(exception,GetMagickModule(),
5992                 CoderError,"BASI is not implemented yet","`%s'",
5993                 image->filename);
5994
5995             mng_info->basi_warning++;
5996 #ifdef MNG_BASI_SUPPORTED
5997             basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5998                (p[2] << 8) | p[3]);
5999             basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6000                (p[6] << 8) | p[7]);
6001             basi_color_type=p[8];
6002             basi_compression_method=p[9];
6003             basi_filter_type=p[10];
6004             basi_interlace_method=p[11];
6005             if (length > 11)
6006               basi_red=(p[12] << 8) & p[13];
6007
6008             else
6009               basi_red=0;
6010
6011             if (length > 13)
6012               basi_green=(p[14] << 8) & p[15];
6013
6014             else
6015               basi_green=0;
6016
6017             if (length > 15)
6018               basi_blue=(p[16] << 8) & p[17];
6019
6020             else
6021               basi_blue=0;
6022
6023             if (length > 17)
6024               basi_alpha=(p[18] << 8) & p[19];
6025
6026             else
6027               {
6028                 if (basi_sample_depth == 16)
6029                   basi_alpha=65535L;
6030                 else
6031                   basi_alpha=255;
6032               }
6033
6034             if (length > 19)
6035               basi_viewable=p[20];
6036
6037             else
6038               basi_viewable=0;
6039
6040 #endif
6041             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6042             continue;
6043           }
6044
6045         if (memcmp(type,mng_IHDR,4)
6046 #if defined(JNG_SUPPORTED)
6047             && memcmp(type,mng_JHDR,4)
6048 #endif
6049             )
6050           {
6051             /* Not an IHDR or JHDR chunk */
6052             if (length)
6053               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6054
6055             continue;
6056           }
6057 /* Process IHDR */
6058         if (logging != MagickFalse)
6059           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6060             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6061
6062         mng_info->exists[object_id]=MagickTrue;
6063         mng_info->viewable[object_id]=MagickTrue;
6064
6065         if (mng_info->invisible[object_id])
6066           {
6067             if (logging != MagickFalse)
6068               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6069                 "  Skipping invisible object");
6070
6071             skip_to_iend=MagickTrue;
6072             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6073             continue;
6074           }
6075 #if defined(MNG_INSERT_LAYERS)
6076         if (length < 8)
6077           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6078
6079         image_width=(size_t) mng_get_long(p);
6080         image_height=(size_t) mng_get_long(&p[4]);
6081 #endif
6082         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6083
6084         /*
6085           Insert a transparent background layer behind the entire animation
6086           if it is not full screen.
6087         */
6088 #if defined(MNG_INSERT_LAYERS)
6089         if (insert_layers && mng_type && first_mng_object)
6090           {
6091             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6092                 (image_width < mng_info->mng_width) ||
6093                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6094                 (image_height < mng_info->mng_height) ||
6095                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6096               {
6097                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6098                   {
6099                     /*
6100                       Allocate next image structure.
6101                     */
6102                     AcquireNextImage(image_info,image,exception);
6103
6104                     if (GetNextImageInList(image) == (Image *) NULL)
6105                       {
6106                         image=DestroyImageList(image);
6107                         MngInfoFreeStruct(mng_info,&have_mng_structure);
6108                         return((Image *) NULL);
6109                       }
6110
6111                     image=SyncNextImageInList(image);
6112                   }
6113                 mng_info->image=image;
6114
6115                 if (term_chunk_found)
6116                   {
6117                     image->start_loop=MagickTrue;
6118                     image->iterations=mng_iterations;
6119                     term_chunk_found=MagickFalse;
6120                   }
6121
6122                 else
6123                     image->start_loop=MagickFalse;
6124
6125                 /* Make a background rectangle.  */
6126
6127                 image->delay=0;
6128                 image->columns=mng_info->mng_width;
6129                 image->rows=mng_info->mng_height;
6130                 image->page.width=mng_info->mng_width;
6131                 image->page.height=mng_info->mng_height;
6132                 image->page.x=0;
6133                 image->page.y=0;
6134                 image->background_color=mng_background_color;
6135                 (void) SetImageBackgroundColor(image,exception);
6136                 if (logging != MagickFalse)
6137                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6138                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6139                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6140               }
6141           }
6142         /*
6143           Insert a background layer behind the upcoming image if
6144           framing_mode is 3, and we haven't already inserted one.
6145         */
6146         if (insert_layers && (mng_info->framing_mode == 3) &&
6147                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6148                 (simplicity & 0x08)))
6149           {
6150             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6151             {
6152               /*
6153                 Allocate next image structure.
6154               */
6155               AcquireNextImage(image_info,image,exception);
6156
6157               if (GetNextImageInList(image) == (Image *) NULL)
6158                 {
6159                   image=DestroyImageList(image);
6160                   MngInfoFreeStruct(mng_info,&have_mng_structure);
6161                   return((Image *) NULL);
6162                 }
6163
6164               image=SyncNextImageInList(image);
6165             }
6166
6167             mng_info->image=image;
6168
6169             if (term_chunk_found)
6170               {
6171                 image->start_loop=MagickTrue;
6172                 image->iterations=mng_iterations;
6173                 term_chunk_found=MagickFalse;
6174               }
6175
6176             else
6177                 image->start_loop=MagickFalse;
6178
6179             image->delay=0;
6180             image->columns=subframe_width;
6181             image->rows=subframe_height;
6182             image->page.width=subframe_width;
6183             image->page.height=subframe_height;
6184             image->page.x=mng_info->clip.left;
6185             image->page.y=mng_info->clip.top;
6186             image->background_color=mng_background_color;
6187             image->alpha_trait=UndefinedPixelTrait;
6188             (void) SetImageBackgroundColor(image,exception);
6189
6190             if (logging != MagickFalse)
6191               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6192                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6193                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6194                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6195           }
6196 #endif /* MNG_INSERT_LAYERS */
6197         first_mng_object=MagickFalse;
6198
6199         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6200           {
6201             /*
6202               Allocate next image structure.
6203             */
6204             AcquireNextImage(image_info,image,exception);
6205
6206             if (GetNextImageInList(image) == (Image *) NULL)
6207               {
6208                 image=DestroyImageList(image);
6209                 MngInfoFreeStruct(mng_info,&have_mng_structure);
6210                 return((Image *) NULL);
6211               }
6212
6213             image=SyncNextImageInList(image);
6214           }
6215         mng_info->image=image;
6216         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6217           GetBlobSize(image));
6218
6219         if (status == MagickFalse)
6220           break;
6221
6222         if (term_chunk_found)
6223           {
6224             image->start_loop=MagickTrue;
6225             term_chunk_found=MagickFalse;
6226           }
6227
6228         else
6229             image->start_loop=MagickFalse;
6230
6231         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6232           {
6233             image->delay=frame_delay;
6234             frame_delay=default_frame_delay;
6235           }
6236
6237         else
6238           image->delay=0;
6239
6240         image->page.width=mng_info->mng_width;
6241         image->page.height=mng_info->mng_height;
6242         image->page.x=mng_info->x_off[object_id];
6243         image->page.y=mng_info->y_off[object_id];
6244         image->iterations=mng_iterations;
6245
6246         /*
6247           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6248         */
6249
6250         if (logging != MagickFalse)
6251           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6252             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6253             type[2],type[3]);
6254
6255         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6256
6257         if (offset < 0)
6258           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6259       }
6260
6261     previous=image;
6262     mng_info->image=image;
6263     mng_info->mng_type=mng_type;
6264     mng_info->object_id=object_id;
6265
6266     if (memcmp(type,mng_IHDR,4) == 0)
6267       image=ReadOnePNGImage(mng_info,image_info,exception);
6268
6269 #if defined(JNG_SUPPORTED)
6270     else
6271       image=ReadOneJNGImage(mng_info,image_info,exception);
6272 #endif
6273
6274     if (image == (Image *) NULL)
6275       {
6276         if (IsImageObject(previous) != MagickFalse)
6277           {
6278             (void) DestroyImageList(previous);
6279             (void) CloseBlob(previous);
6280           }
6281
6282         MngInfoFreeStruct(mng_info,&have_mng_structure);
6283         return((Image *) NULL);
6284       }
6285
6286     if (image->columns == 0 || image->rows == 0)
6287       {
6288         (void) CloseBlob(image);
6289         image=DestroyImageList(image);
6290         MngInfoFreeStruct(mng_info,&have_mng_structure);
6291         return((Image *) NULL);
6292       }
6293
6294     mng_info->image=image;
6295
6296     if (mng_type)
6297       {
6298         MngBox
6299           crop_box;
6300
6301         if (mng_info->magn_methx || mng_info->magn_methy)
6302           {
6303             png_uint_32
6304                magnified_height,
6305                magnified_width;
6306
6307             if (logging != MagickFalse)
6308               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6309                 "  Processing MNG MAGN chunk");
6310
6311             if (mng_info->magn_methx == 1)
6312               {
6313                 magnified_width=mng_info->magn_ml;
6314
6315                 if (image->columns > 1)
6316                    magnified_width += mng_info->magn_mr;
6317
6318                 if (image->columns > 2)
6319                    magnified_width += (png_uint_32)
6320                       ((image->columns-2)*(mng_info->magn_mx));
6321               }
6322
6323             else
6324               {
6325                 magnified_width=(png_uint_32) image->columns;
6326
6327                 if (image->columns > 1)
6328                    magnified_width += mng_info->magn_ml-1;
6329
6330                 if (image->columns > 2)
6331                    magnified_width += mng_info->magn_mr-1;
6332
6333                 if (image->columns > 3)
6334                    magnified_width += (png_uint_32)
6335                       ((image->columns-3)*(mng_info->magn_mx-1));
6336               }
6337
6338             if (mng_info->magn_methy == 1)
6339               {
6340                 magnified_height=mng_info->magn_mt;
6341
6342                 if (image->rows > 1)
6343                    magnified_height += mng_info->magn_mb;
6344
6345                 if (image->rows > 2)
6346                    magnified_height += (png_uint_32)
6347                       ((image->rows-2)*(mng_info->magn_my));
6348               }
6349
6350             else
6351               {
6352                 magnified_height=(png_uint_32) image->rows;
6353
6354                 if (image->rows > 1)
6355                    magnified_height += mng_info->magn_mt-1;
6356
6357                 if (image->rows > 2)
6358                    magnified_height += mng_info->magn_mb-1;
6359
6360                 if (image->rows > 3)
6361                    magnified_height += (png_uint_32)
6362                       ((image->rows-3)*(mng_info->magn_my-1));
6363               }
6364
6365             if (magnified_height > image->rows ||
6366                 magnified_width > image->columns)
6367               {
6368                 Image
6369                   *large_image;
6370
6371                 int
6372                   yy;
6373
6374                 Quantum
6375                   *next,
6376                   *prev;
6377
6378                 png_uint_16
6379                   magn_methx,
6380                   magn_methy;
6381
6382                 ssize_t
6383                   m,
6384                   y;
6385
6386                 register Quantum
6387                   *n,
6388                   *q;
6389
6390                 register ssize_t
6391                   x;
6392
6393                 /* Allocate next image structure.  */
6394
6395                 if (logging != MagickFalse)
6396                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6397                     "    Allocate magnified image");
6398
6399                 AcquireNextImage(image_info,image,exception);
6400
6401                 if (GetNextImageInList(image) == (Image *) NULL)
6402                   {
6403                     image=DestroyImageList(image);
6404                     MngInfoFreeStruct(mng_info,&have_mng_structure);
6405                     return((Image *) NULL);
6406                   }
6407
6408                 large_image=SyncNextImageInList(image);
6409
6410                 large_image->columns=magnified_width;
6411                 large_image->rows=magnified_height;
6412
6413                 magn_methx=mng_info->magn_methx;
6414                 magn_methy=mng_info->magn_methy;
6415
6416 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6417 #define QM unsigned short
6418                 if (magn_methx != 1 || magn_methy != 1)
6419                   {
6420                   /*
6421                      Scale pixels to unsigned shorts to prevent
6422                      overflow of intermediate values of interpolations
6423                   */
6424                      for (y=0; y < (ssize_t) image->rows; y++)
6425                      {
6426                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6427                           exception);
6428
6429                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6430                        {
6431                           SetPixelRed(image,ScaleQuantumToShort(
6432                             GetPixelRed(image,q)),q);
6433                           SetPixelGreen(image,ScaleQuantumToShort(
6434                             GetPixelGreen(image,q)),q);
6435                           SetPixelBlue(image,ScaleQuantumToShort(
6436                             GetPixelBlue(image,q)),q);
6437                           SetPixelAlpha(image,ScaleQuantumToShort(
6438                             GetPixelAlpha(image,q)),q);
6439                           q+=GetPixelChannels(image);
6440                        }
6441
6442                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6443                          break;
6444                      }
6445                   }
6446 #else
6447 #define QM Quantum
6448 #endif
6449
6450                 if (image->alpha_trait == BlendPixelTrait)
6451                    (void) SetImageBackgroundColor(large_image,exception);
6452
6453                 else
6454                   {
6455                     large_image->background_color.alpha=OpaqueAlpha;
6456                     (void) SetImageBackgroundColor(large_image,exception);
6457
6458                     if (magn_methx == 4)
6459                       magn_methx=2;
6460
6461                     if (magn_methx == 5)
6462                       magn_methx=3;
6463
6464                     if (magn_methy == 4)
6465                       magn_methy=2;
6466
6467                     if (magn_methy == 5)
6468                       magn_methy=3;
6469                   }
6470
6471                 /* magnify the rows into the right side of the large image */
6472
6473                 if (logging != MagickFalse)
6474                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6475                     "    Magnify the rows to %.20g",(double) large_image->rows);
6476                 m=(ssize_t) mng_info->magn_mt;
6477                 yy=0;
6478                 length=(size_t) image->columns*GetPixelChannels(image);
6479                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6480                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6481
6482                 if ((prev == (Quantum *) NULL) ||
6483                     (next == (Quantum *) NULL))
6484                   {
6485                      image=DestroyImageList(image);
6486                      MngInfoFreeStruct(mng_info,&have_mng_structure);
6487                      ThrowReaderException(ResourceLimitError,
6488                        "MemoryAllocationFailed");
6489                   }
6490
6491                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6492                 (void) CopyMagickMemory(next,n,length);
6493
6494                 for (y=0; y < (ssize_t) image->rows; y++)
6495                 {
6496                   if (y == 0)
6497                     m=(ssize_t) mng_info->magn_mt;
6498
6499                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6500                     m=(ssize_t) mng_info->magn_mb;
6501
6502                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6503                     m=(ssize_t) mng_info->magn_mb;
6504
6505                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6506                     m=1;
6507
6508                   else
6509                     m=(ssize_t) mng_info->magn_my;
6510
6511                   n=prev;
6512                   prev=next;
6513                   next=n;
6514
6515                   if (y < (ssize_t) image->rows-1)
6516                     {
6517                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6518                           exception);
6519                       (void) CopyMagickMemory(next,n,length);
6520                     }
6521
6522                   for (i=0; i < m; i++, yy++)
6523                   {
6524                     register Quantum
6525                       *pixels;
6526
6527                     assert(yy < (ssize_t) large_image->rows);
6528                     pixels=prev;
6529                     n=next;
6530                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6531                       1,exception);
6532                     q+=(large_image->columns-image->columns)*
6533                       GetPixelChannels(large_image);
6534
6535                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
6536                     {
6537                       /* To do: get color as function of indexes[x] */
6538                       /*
6539                       if (image->storage_class == PseudoClass)
6540                         {
6541                         }
6542                       */
6543
6544                       if (magn_methy <= 1)
6545                         {
6546                           /* replicate previous */
6547                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6548                           SetPixelGreen(large_image,GetPixelGreen(image,
6549                              pixels),q);
6550                           SetPixelBlue(large_image,GetPixelBlue(image,
6551                              pixels),q);
6552                           SetPixelAlpha(large_image,GetPixelAlpha(image,
6553                              pixels),q);
6554                         }
6555
6556                       else if (magn_methy == 2 || magn_methy == 4)
6557                         {
6558                           if (i == 0)
6559                             {
6560                               SetPixelRed(large_image,GetPixelRed(image,
6561                                  pixels),q);
6562                               SetPixelGreen(large_image,GetPixelGreen(image,
6563                                  pixels),q);
6564                               SetPixelBlue(large_image,GetPixelBlue(image,
6565                                  pixels),q);
6566                               SetPixelAlpha(large_image,GetPixelAlpha(image,
6567                                  pixels),q);
6568                             }
6569
6570                           else
6571                             {
6572                               /* Interpolate */
6573                               SetPixelRed(large_image,((QM) (((ssize_t)
6574                                  (2*i*(GetPixelRed(image,n)
6575                                  -GetPixelRed(image,pixels)+m))/
6576                                  ((ssize_t) (m*2))
6577                                  +GetPixelRed(image,pixels)))),q);
6578                               SetPixelGreen(large_image,((QM) (((ssize_t)
6579                                  (2*i*(GetPixelGreen(image,n)
6580                                  -GetPixelGreen(image,pixels)+m))/
6581                                  ((ssize_t) (m*2))
6582                                  +GetPixelGreen(image,pixels)))),q);
6583                               SetPixelBlue(large_image,((QM) (((ssize_t)
6584                                  (2*i*(GetPixelBlue(image,n)
6585                                  -GetPixelBlue(image,pixels)+m))/
6586                                  ((ssize_t) (m*2))
6587                                  +GetPixelBlue(image,pixels)))),q);
6588
6589                               if (image->alpha_trait == BlendPixelTrait)
6590                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
6591                                     (2*i*(GetPixelAlpha(image,n)
6592                                     -GetPixelAlpha(image,pixels)+m))
6593                                     /((ssize_t) (m*2))+
6594                                    GetPixelAlpha(image,pixels)))),q);
6595                             }
6596
6597                           if (magn_methy == 4)
6598                             {
6599                               /* Replicate nearest */
6600                               if (i <= ((m+1) << 1))
6601                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6602                                     pixels),q);
6603                               else
6604                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6605                                     n),q);
6606                             }
6607                         }
6608
6609                       else /* if (magn_methy == 3 || magn_methy == 5) */
6610                         {
6611                           /* Replicate nearest */
6612                           if (i <= ((m+1) << 1))
6613                           {
6614                              SetPixelRed(large_image,GetPixelRed(image,
6615                                     pixels),q);
6616                              SetPixelGreen(large_image,GetPixelGreen(image,
6617                                     pixels),q);
6618                              SetPixelBlue(large_image,GetPixelBlue(image,
6619                                     pixels),q);
6620                              SetPixelAlpha(large_image,GetPixelAlpha(image,
6621                                     pixels),q);
6622                           }
6623
6624                           else
6625                           {
6626                              SetPixelRed(large_image,GetPixelRed(image,n),q);
6627                              SetPixelGreen(large_image,GetPixelGreen(image,n),
6628                                     q);
6629                              SetPixelBlue(large_image,GetPixelBlue(image,n),
6630                                     q);
6631                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6632                                     q);
6633                           }
6634
6635                           if (magn_methy == 5)
6636                             {
6637                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6638                                  (GetPixelAlpha(image,n)
6639                                  -GetPixelAlpha(image,pixels))
6640                                  +m))/((ssize_t) (m*2))
6641                                  +GetPixelAlpha(image,pixels)),q);
6642                             }
6643                         }
6644                       n+=GetPixelChannels(image);
6645                       q+=GetPixelChannels(large_image);
6646                       pixels+=GetPixelChannels(image);
6647                     } /* x */
6648
6649                     if (SyncAuthenticPixels(large_image,exception) == 0)
6650                       break;
6651
6652                   } /* i */
6653                 } /* y */
6654
6655                 prev=(Quantum *) RelinquishMagickMemory(prev);
6656                 next=(Quantum *) RelinquishMagickMemory(next);
6657
6658                 length=image->columns;
6659
6660                 if (logging != MagickFalse)
6661                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6662                     "    Delete original image");
6663
6664                 DeleteImageFromList(&image);
6665
6666                 image=large_image;
6667
6668                 mng_info->image=image;
6669
6670                 /* magnify the columns */
6671                 if (logging != MagickFalse)
6672                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6673                     "    Magnify the columns to %.20g",(double) image->columns);
6674
6675                 for (y=0; y < (ssize_t) image->rows; y++)
6676                 {
6677                   register Quantum
6678                     *pixels;
6679
6680                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6681                   pixels=q+(image->columns-length)*GetPixelChannels(image);
6682                   n=pixels+GetPixelChannels(image);
6683
6684                   for (x=(ssize_t) (image->columns-length);
6685                     x < (ssize_t) image->columns; x++)
6686                   {
6687                     /* To do: Rewrite using Get/Set***PixelChannel() */
6688
6689                     if (x == (ssize_t) (image->columns-length))
6690                       m=(ssize_t) mng_info->magn_ml;
6691
6692                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6693                       m=(ssize_t) mng_info->magn_mr;
6694
6695                     else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6696                       m=(ssize_t) mng_info->magn_mr;
6697
6698                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6699                       m=1;
6700
6701                     else
6702                       m=(ssize_t) mng_info->magn_mx;
6703
6704                     for (i=0; i < m; i++)
6705                     {
6706                       if (magn_methx <= 1)
6707                         {
6708                           /* replicate previous */
6709                           SetPixelRed(image,GetPixelRed(image,pixels),q);
6710                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6711                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6712                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6713                         }
6714
6715                       else if (magn_methx == 2 || magn_methx == 4)
6716                         {
6717                           if (i == 0)
6718                           {
6719                             SetPixelRed(image,GetPixelRed(image,pixels),q);
6720                             SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6721                             SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6722                             SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6723                           }
6724
6725                           /* To do: Rewrite using Get/Set***PixelChannel() */
6726                           else
6727                             {
6728                               /* Interpolate */
6729                               SetPixelRed(image,(QM) ((2*i*(
6730                                  GetPixelRed(image,n)
6731                                  -GetPixelRed(image,pixels))+m)
6732                                  /((ssize_t) (m*2))+
6733                                  GetPixelRed(image,pixels)),q);
6734
6735                               SetPixelGreen(image,(QM) ((2*i*(
6736                                  GetPixelGreen(image,n)
6737                                  -GetPixelGreen(image,pixels))+m)
6738                                  /((ssize_t) (m*2))+
6739                                  GetPixelGreen(image,pixels)),q);
6740
6741                               SetPixelBlue(image,(QM) ((2*i*(
6742                                  GetPixelBlue(image,n)
6743                                  -GetPixelBlue(image,pixels))+m)
6744                                  /((ssize_t) (m*2))+
6745                                  GetPixelBlue(image,pixels)),q);
6746                               if (image->alpha_trait == BlendPixelTrait)
6747                                  SetPixelAlpha(image,(QM) ((2*i*(
6748                                    GetPixelAlpha(image,n)
6749                                    -GetPixelAlpha(image,pixels))+m)
6750                                    /((ssize_t) (m*2))+
6751                                    GetPixelAlpha(image,pixels)),q);
6752                             }
6753
6754                           if (magn_methx == 4)
6755                             {
6756                               /* Replicate nearest */
6757                               if (i <= ((m+1) << 1))
6758                               {
6759                                  SetPixelAlpha(image,
6760                                    GetPixelAlpha(image,pixels)+0,q);
6761                               }
6762                               else
6763                               {
6764                                  SetPixelAlpha(image,
6765                                    GetPixelAlpha(image,n)+0,q);
6766                               }
6767                             }
6768                         }
6769
6770                       else /* if (magn_methx == 3 || magn_methx == 5) */
6771                         {
6772                           /* Replicate nearest */
6773                           if (i <= ((m+1) << 1))
6774                           {
6775                              SetPixelRed(image,GetPixelRed(image,pixels),q);
6776                              SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6777                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6778                              SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6779                           }
6780
6781                           else
6782                           {
6783                              SetPixelRed(image,GetPixelRed(image,n),q);
6784                              SetPixelGreen(image,GetPixelGreen(image,n),q);
6785                              SetPixelBlue(image,GetPixelBlue(image,n),q);
6786                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6787                           }
6788
6789                           if (magn_methx == 5)
6790                             {
6791                               /* Interpolate */
6792                               SetPixelAlpha(image,
6793                                  (QM) ((2*i*( GetPixelAlpha(image,n)
6794                                  -GetPixelAlpha(image,pixels))+m)/
6795                                  ((ssize_t) (m*2))
6796                                  +GetPixelAlpha(image,pixels)),q);
6797                             }
6798                         }
6799                       q+=GetPixelChannels(image);
6800                     }
6801                     n+=GetPixelChannels(image);
6802                     p+=GetPixelChannels(image);
6803                   }
6804
6805                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
6806                     break;
6807                 }
6808 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6809               if (magn_methx != 1 || magn_methy != 1)
6810                 {
6811                 /*
6812                    Rescale pixels to Quantum
6813                 */
6814                    for (y=0; y < (ssize_t) image->rows; y++)
6815                    {
6816                      q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6817
6818                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
6819                      {
6820                         SetPixelRed(image,ScaleShortToQuantum(
6821                           GetPixelRed(image,q)),q);
6822                         SetPixelGreen(image,ScaleShortToQuantum(
6823                           GetPixelGreen(image,q)),q);
6824                         SetPixelBlue(image,ScaleShortToQuantum(
6825                           GetPixelBlue(image,q)),q);
6826                         SetPixelAlpha(image,ScaleShortToQuantum(
6827                           GetPixelAlpha(image,q)),q);
6828                         q+=GetPixelChannels(image);
6829                      }
6830
6831                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
6832                        break;
6833                    }
6834                 }
6835 #endif
6836                 if (logging != MagickFalse)
6837                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6838                     "  Finished MAGN processing");
6839               }
6840           }
6841
6842         /*
6843           Crop_box is with respect to the upper left corner of the MNG.
6844         */
6845         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6846         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6847         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6848         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6849         crop_box=mng_minimum_box(crop_box,mng_info->clip);
6850         crop_box=mng_minimum_box(crop_box,mng_info->frame);
6851         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6852         if ((crop_box.left != (mng_info->image_box.left
6853             +mng_info->x_off[object_id])) ||
6854             (crop_box.right != (mng_info->image_box.right
6855             +mng_info->x_off[object_id])) ||
6856             (crop_box.top != (mng_info->image_box.top
6857             +mng_info->y_off[object_id])) ||
6858             (crop_box.bottom != (mng_info->image_box.bottom
6859             +mng_info->y_off[object_id])))
6860           {
6861             if (logging != MagickFalse)
6862               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6863                 "  Crop the PNG image");
6864
6865             if ((crop_box.left < crop_box.right) &&
6866                 (crop_box.top < crop_box.bottom))
6867               {
6868                 Image
6869                   *im;
6870
6871                 RectangleInfo
6872                   crop_info;
6873
6874                 /*
6875                   Crop_info is with respect to the upper left corner of
6876                   the image.
6877                 */
6878                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6879                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6880                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6881                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6882                 image->page.width=image->columns;
6883                 image->page.height=image->rows;
6884                 image->page.x=0;
6885                 image->page.y=0;
6886                 im=CropImage(image,&crop_info,exception);
6887
6888                 if (im != (Image *) NULL)
6889                   {
6890                     image->columns=im->columns;
6891                     image->rows=im->rows;
6892                     im=DestroyImage(im);
6893                     image->page.width=image->columns;
6894                     image->page.height=image->rows;
6895                     image->page.x=crop_box.left;
6896                     image->page.y=crop_box.top;
6897                   }
6898               }
6899
6900             else
6901               {
6902                 /*
6903                   No pixels in crop area.  The MNG spec still requires
6904                   a layer, though, so make a single transparent pixel in
6905                   the top left corner.
6906                 */
6907                 image->columns=1;
6908                 image->rows=1;
6909                 image->colors=2;
6910                 (void) SetImageBackgroundColor(image,exception);
6911                 image->page.width=1;
6912                 image->page.height=1;
6913                 image->page.x=0;
6914                 image->page.y=0;
6915               }
6916           }
6917 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6918         image=mng_info->image;
6919 #endif
6920       }
6921
6922 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6923       /* PNG does not handle depths greater than 16 so reduce it even
6924        * if lossy.
6925        */
6926       if (image->depth > 16)
6927          image->depth=16;
6928 #endif
6929
6930 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
6931       if (image->depth > 8)
6932         {
6933           /* To do: fill low byte properly */
6934           image->depth=16;
6935         }
6936
6937       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
6938          image->depth = 8;
6939 #endif
6940
6941       if (image_info->number_scenes != 0)
6942         {
6943           if (mng_info->scenes_found >
6944              (ssize_t) (image_info->first_scene+image_info->number_scenes))
6945             break;
6946         }
6947
6948       if (logging != MagickFalse)
6949         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6950           "  Finished reading image datastream.");
6951
6952   } while (LocaleCompare(image_info->magick,"MNG") == 0);
6953
6954   (void) CloseBlob(image);
6955
6956   if (logging != MagickFalse)
6957     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6958       "  Finished reading all image datastreams.");
6959
6960 #if defined(MNG_INSERT_LAYERS)
6961   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6962        (mng_info->mng_height))
6963     {
6964       /*
6965         Insert a background layer if nothing else was found.
6966       */
6967       if (logging != MagickFalse)
6968         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6969           "  No images found.  Inserting a background layer.");
6970
6971       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6972         {
6973           /*
6974             Allocate next image structure.
6975           */
6976           AcquireNextImage(image_info,image,exception);
6977           if (GetNextImageInList(image) == (Image *) NULL)
6978             {
6979               image=DestroyImageList(image);
6980               MngInfoFreeStruct(mng_info,&have_mng_structure);
6981
6982               if (logging != MagickFalse)
6983                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6984                   "  Allocation failed, returning NULL.");
6985
6986               return((Image *) NULL);
6987             }
6988           image=SyncNextImageInList(image);
6989         }
6990       image->columns=mng_info->mng_width;
6991       image->rows=mng_info->mng_height;
6992       image->page.width=mng_info->mng_width;
6993       image->page.height=mng_info->mng_height;
6994       image->page.x=0;
6995       image->page.y=0;
6996       image->background_color=mng_background_color;
6997       image->alpha_trait=UndefinedPixelTrait;
6998
6999       if (image_info->ping == MagickFalse)
7000         (void) SetImageBackgroundColor(image,exception);
7001
7002       mng_info->image_found++;
7003     }
7004 #endif
7005   image->iterations=mng_iterations;
7006
7007   if (mng_iterations == 1)
7008     image->start_loop=MagickTrue;
7009
7010   while (GetPreviousImageInList(image) != (Image *) NULL)
7011   {
7012     image_count++;
7013     if (image_count > 10*mng_info->image_found)
7014       {
7015         if (logging != MagickFalse)
7016           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7017
7018         (void) ThrowMagickException(exception,GetMagickModule(),
7019           CoderError,"Linked list is corrupted, beginning of list not found",
7020           "`%s'",image_info->filename);
7021
7022         return((Image *) NULL);
7023       }
7024
7025     image=GetPreviousImageInList(image);
7026
7027     if (GetNextImageInList(image) == (Image *) NULL)
7028       {
7029         if (logging != MagickFalse)
7030           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7031
7032         (void) ThrowMagickException(exception,GetMagickModule(),
7033           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7034           image_info->filename);
7035       }
7036   }
7037
7038   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7039              GetNextImageInList(image) ==
7040      (Image *) NULL)
7041     {
7042       if (logging != MagickFalse)
7043         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7044             "  First image null");
7045
7046       (void) ThrowMagickException(exception,GetMagickModule(),
7047         CoderError,"image->next for first image is NULL but shouldn't be.",
7048         "`%s'",image_info->filename);
7049     }
7050
7051   if (mng_info->image_found == 0)
7052     {
7053       if (logging != MagickFalse)
7054         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7055           "  No visible images found.");
7056
7057       (void) ThrowMagickException(exception,GetMagickModule(),
7058         CoderError,"No visible images in file","`%s'",image_info->filename);
7059
7060       if (image != (Image *) NULL)
7061         image=DestroyImageList(image);
7062
7063       MngInfoFreeStruct(mng_info,&have_mng_structure);
7064       return((Image *) NULL);
7065     }
7066
7067   if (mng_info->ticks_per_second)
7068     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7069             final_delay/mng_info->ticks_per_second;
7070
7071   else
7072     image->start_loop=MagickTrue;
7073
7074   /* Find final nonzero image delay */
7075   final_image_delay=0;
7076
7077   while (GetNextImageInList(image) != (Image *) NULL)
7078     {
7079       if (image->delay)
7080         final_image_delay=image->delay;
7081
7082       image=GetNextImageInList(image);
7083     }
7084
7085   if (final_delay < final_image_delay)
7086     final_delay=final_image_delay;
7087
7088   image->delay=final_delay;
7089
7090   if (logging != MagickFalse)
7091       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7092         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7093         (double) final_delay);
7094
7095   if (logging != MagickFalse)
7096     {
7097       int
7098         scene;
7099
7100       scene=0;
7101       image=GetFirstImageInList(image);
7102
7103       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7104         "  Before coalesce:");
7105
7106       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7107         "    scene 0 delay=%.20g",(double) image->delay);
7108
7109       while (GetNextImageInList(image) != (Image *) NULL)
7110       {
7111         image=GetNextImageInList(image);
7112         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7113           "    scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7114       }
7115     }
7116
7117   image=GetFirstImageInList(image);
7118 #ifdef MNG_COALESCE_LAYERS
7119   if (insert_layers)
7120     {
7121       Image
7122         *next_image,
7123         *next;
7124
7125       size_t
7126         scene;
7127
7128       if (logging != MagickFalse)
7129         (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Coalesce Images");
7130
7131       scene=image->scene;
7132       next_image=CoalesceImages(image,exception);
7133
7134       if (next_image == (Image *) NULL)
7135         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7136
7137       image=DestroyImageList(image);
7138       image=next_image;
7139
7140       for (next=image; next != (Image *) NULL; next=next_image)
7141       {
7142          next->page.width=mng_info->mng_width;
7143          next->page.height=mng_info->mng_height;
7144          next->page.x=0;
7145          next->page.y=0;
7146          next->scene=scene++;
7147          next_image=GetNextImageInList(next);
7148
7149          if (next_image == (Image *) NULL)
7150            break;
7151
7152          if (next->delay == 0)
7153            {
7154              scene--;
7155              next_image->previous=GetPreviousImageInList(next);
7156              if (GetPreviousImageInList(next) == (Image *) NULL)
7157                image=next_image;
7158              else
7159                next->previous->next=next_image;
7160              next=DestroyImage(next);
7161            }
7162       }
7163     }
7164 #endif
7165
7166   while (GetNextImageInList(image) != (Image *) NULL)
7167       image=GetNextImageInList(image);
7168
7169   image->dispose=BackgroundDispose;
7170
7171   if (logging != MagickFalse)
7172     {
7173       int
7174         scene;
7175
7176       scene=0;
7177       image=GetFirstImageInList(image);
7178
7179       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7180         "  After coalesce:");
7181
7182       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7183         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7184         (double) image->dispose);
7185
7186       while (GetNextImageInList(image) != (Image *) NULL)
7187       {
7188         image=GetNextImageInList(image);
7189
7190         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7191           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7192           (double) image->delay,(double) image->dispose);
7193       }
7194    }
7195
7196   image=GetFirstImageInList(image);
7197   MngInfoFreeStruct(mng_info,&have_mng_structure);
7198   have_mng_structure=MagickFalse;
7199
7200   if (logging != MagickFalse)
7201     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7202
7203   return(GetFirstImageInList(image));
7204 }
7205 #else /* PNG_LIBPNG_VER > 10011 */
7206 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7207 {
7208   printf("Your PNG library is too old: You have libpng-%s\n",
7209      PNG_LIBPNG_VER_STRING);
7210
7211   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7212     "PNG library is too old","`%s'",image_info->filename);
7213
7214   return(Image *) NULL;
7215 }
7216
7217 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7218 {
7219   return(ReadPNGImage(image_info,exception));
7220 }
7221 #endif /* PNG_LIBPNG_VER > 10011 */
7222 #endif
7223 \f
7224 /*
7225 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7226 %                                                                             %
7227 %                                                                             %
7228 %                                                                             %
7229 %   R e g i s t e r P N G I m a g e                                           %
7230 %                                                                             %
7231 %                                                                             %
7232 %                                                                             %
7233 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7234 %
7235 %  RegisterPNGImage() adds properties for the PNG image format to
7236 %  the list of supported formats.  The properties include the image format
7237 %  tag, a method to read and/or write the format, whether the format
7238 %  supports the saving of more than one frame to the same file or blob,
7239 %  whether the format supports native in-memory I/O, and a brief
7240 %  description of the format.
7241 %
7242 %  The format of the RegisterPNGImage method is:
7243 %
7244 %      size_t RegisterPNGImage(void)
7245 %
7246 */
7247 ModuleExport size_t RegisterPNGImage(void)
7248 {
7249   char
7250     version[MaxTextExtent];
7251
7252   MagickInfo
7253     *entry;
7254
7255   static const char
7256     *PNGNote=
7257     {
7258       "See http://www.libpng.org/ for details about the PNG format."
7259     },
7260
7261     *JNGNote=
7262     {
7263       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7264       "format."
7265     },
7266
7267     *MNGNote=
7268     {
7269       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7270       "format."
7271     };
7272
7273   *version='\0';
7274
7275 #if defined(PNG_LIBPNG_VER_STRING)
7276   (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7277   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7278
7279   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7280     {
7281       (void) ConcatenateMagickString(version,",",MaxTextExtent);
7282       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7283             MaxTextExtent);
7284     }
7285 #endif
7286
7287   entry=SetMagickInfo("MNG");
7288   entry->seekable_stream=MagickTrue;  /* To do: eliminate this. */
7289
7290 #if defined(MAGICKCORE_PNG_DELEGATE)
7291   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7292   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7293 #endif
7294
7295   entry->magick=(IsImageFormatHandler *) IsMNG;
7296   entry->description=ConstantString("Multiple-image Network Graphics");
7297
7298   if (*version != '\0')
7299     entry->version=ConstantString(version);
7300
7301   entry->module=ConstantString("PNG");
7302   entry->note=ConstantString(MNGNote);
7303   (void) RegisterMagickInfo(entry);
7304
7305   entry=SetMagickInfo("PNG");
7306
7307 #if defined(MAGICKCORE_PNG_DELEGATE)
7308   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7309   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7310 #endif
7311
7312   entry->magick=(IsImageFormatHandler *) IsPNG;
7313   entry->adjoin=MagickFalse;
7314   entry->description=ConstantString("Portable Network Graphics");
7315   entry->module=ConstantString("PNG");
7316
7317   if (*version != '\0')
7318     entry->version=ConstantString(version);
7319
7320   entry->note=ConstantString(PNGNote);
7321   (void) RegisterMagickInfo(entry);
7322
7323   entry=SetMagickInfo("PNG8");
7324
7325 #if defined(MAGICKCORE_PNG_DELEGATE)
7326   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7327   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7328 #endif
7329
7330   entry->magick=(IsImageFormatHandler *) IsPNG;
7331   entry->adjoin=MagickFalse;
7332   entry->description=ConstantString(
7333             "8-bit indexed with optional binary transparency");
7334   entry->module=ConstantString("PNG");
7335   (void) RegisterMagickInfo(entry);
7336
7337   entry=SetMagickInfo("PNG24");
7338   *version='\0';
7339
7340 #if defined(ZLIB_VERSION)
7341   (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7342   (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7343
7344   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7345     {
7346       (void) ConcatenateMagickString(version,",",MaxTextExtent);
7347       (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7348     }
7349 #endif
7350
7351   if (*version != '\0')
7352     entry->version=ConstantString(version);
7353
7354 #if defined(MAGICKCORE_PNG_DELEGATE)
7355   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7356   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7357 #endif
7358
7359   entry->magick=(IsImageFormatHandler *) IsPNG;
7360   entry->adjoin=MagickFalse;
7361   entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
7362   entry->module=ConstantString("PNG");
7363   (void) RegisterMagickInfo(entry);
7364
7365   entry=SetMagickInfo("PNG32");
7366
7367 #if defined(MAGICKCORE_PNG_DELEGATE)
7368   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7369   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7370 #endif
7371
7372   entry->magick=(IsImageFormatHandler *) IsPNG;
7373   entry->adjoin=MagickFalse;
7374   entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7375   entry->module=ConstantString("PNG");
7376   (void) RegisterMagickInfo(entry);
7377
7378   entry=SetMagickInfo("PNG48");
7379
7380 #if defined(MAGICKCORE_PNG_DELEGATE)
7381   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7382   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7383 #endif
7384
7385   entry->magick=(IsImageFormatHandler *) IsPNG;
7386   entry->adjoin=MagickFalse;
7387   entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7388   entry->module=ConstantString("PNG");
7389   (void) RegisterMagickInfo(entry);
7390
7391   entry=SetMagickInfo("PNG64");
7392
7393 #if defined(MAGICKCORE_PNG_DELEGATE)
7394   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7395   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7396 #endif
7397
7398   entry->magick=(IsImageFormatHandler *) IsPNG;
7399   entry->adjoin=MagickFalse;
7400   entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7401   entry->module=ConstantString("PNG");
7402   (void) RegisterMagickInfo(entry);
7403
7404   entry=SetMagickInfo("PNG00");
7405
7406 #if defined(MAGICKCORE_PNG_DELEGATE)
7407   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7408   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7409 #endif
7410
7411   entry->magick=(IsImageFormatHandler *) IsPNG;
7412   entry->adjoin=MagickFalse;
7413   entry->description=ConstantString(
7414     "PNG inheriting bit-depth and color-type from original");
7415   entry->module=ConstantString("PNG");
7416   (void) RegisterMagickInfo(entry);
7417
7418   entry=SetMagickInfo("JNG");
7419
7420 #if defined(JNG_SUPPORTED)
7421 #if defined(MAGICKCORE_PNG_DELEGATE)
7422   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7423   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7424 #endif
7425 #endif
7426
7427   entry->magick=(IsImageFormatHandler *) IsJNG;
7428   entry->adjoin=MagickFalse;
7429   entry->description=ConstantString("JPEG Network Graphics");
7430   entry->module=ConstantString("PNG");
7431   entry->note=ConstantString(JNGNote);
7432   (void) RegisterMagickInfo(entry);
7433
7434 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7435   ping_semaphore=AllocateSemaphoreInfo();
7436 #endif
7437
7438   return(MagickImageCoderSignature);
7439 }
7440 \f
7441 /*
7442 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7443 %                                                                             %
7444 %                                                                             %
7445 %                                                                             %
7446 %   U n r e g i s t e r P N G I m a g e                                       %
7447 %                                                                             %
7448 %                                                                             %
7449 %                                                                             %
7450 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7451 %
7452 %  UnregisterPNGImage() removes format registrations made by the
7453 %  PNG module from the list of supported formats.
7454 %
7455 %  The format of the UnregisterPNGImage method is:
7456 %
7457 %      UnregisterPNGImage(void)
7458 %
7459 */
7460 ModuleExport void UnregisterPNGImage(void)
7461 {
7462   (void) UnregisterMagickInfo("MNG");
7463   (void) UnregisterMagickInfo("PNG");
7464   (void) UnregisterMagickInfo("PNG8");
7465   (void) UnregisterMagickInfo("PNG24");
7466   (void) UnregisterMagickInfo("PNG32");
7467   (void) UnregisterMagickInfo("PNG48");
7468   (void) UnregisterMagickInfo("PNG64");
7469   (void) UnregisterMagickInfo("PNG00");
7470   (void) UnregisterMagickInfo("JNG");
7471
7472 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
7473   if (ping_semaphore != (SemaphoreInfo *) NULL)
7474     DestroySemaphoreInfo(&ping_semaphore);
7475 #endif
7476 }
7477 \f
7478 #if defined(MAGICKCORE_PNG_DELEGATE)
7479 #if PNG_LIBPNG_VER > 10011
7480 /*
7481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7482 %                                                                             %
7483 %                                                                             %
7484 %                                                                             %
7485 %   W r i t e M N G I m a g e                                                 %
7486 %                                                                             %
7487 %                                                                             %
7488 %                                                                             %
7489 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7490 %
7491 %  WriteMNGImage() writes an image in the Portable Network Graphics
7492 %  Group's "Multiple-image Network Graphics" encoded image format.
7493 %
7494 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
7495 %
7496 %  The format of the WriteMNGImage method is:
7497 %
7498 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7499 %        Image *image,ExceptionInfo *exception)
7500 %
7501 %  A description of each parameter follows.
7502 %
7503 %    o image_info: the image info.
7504 %
7505 %    o image:  The image.
7506 %
7507 %    o exception: return any errors or warnings in this structure.
7508 %
7509 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7510 %    "To do" under ReadPNGImage):
7511 %
7512 %    Preserve all unknown and not-yet-handled known chunks found in input
7513 %    PNG file and copy them  into output PNG files according to the PNG
7514 %    copying rules.
7515 %
7516 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
7517 %
7518 %    Improve selection of color type (use indexed-colour or indexed-colour
7519 %    with tRNS when 256 or fewer unique RGBA values are present).
7520 %
7521 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7522 %    This will be complicated if we limit ourselves to generating MNG-LC
7523 %    files.  For now we ignore disposal method 3 and simply overlay the next
7524 %    image on it.
7525 %
7526 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
7527 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
7528 %    [mostly done 15 June 1999 but still need to take care of tRNS]
7529 %
7530 %    Check for identical sRGB and replace with a global sRGB (and remove
7531 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7532 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7533 %    local gAMA/cHRM with local sRGB if appropriate).
7534 %
7535 %    Check for identical sBIT chunks and write global ones.
7536 %
7537 %    Provide option to skip writing the signature tEXt chunks.
7538 %
7539 %    Use signatures to detect identical objects and reuse the first
7540 %    instance of such objects instead of writing duplicate objects.
7541 %
7542 %    Use a smaller-than-32k value of compression window size when
7543 %    appropriate.
7544 %
7545 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
7546 %    ancillary text chunks and save profiles.
7547 %
7548 %    Provide an option to force LC files (to ensure exact framing rate)
7549 %    instead of VLC.
7550 %
7551 %    Provide an option to force VLC files instead of LC, even when offsets
7552 %    are present.  This will involve expanding the embedded images with a
7553 %    transparent region at the top and/or left.
7554 */
7555
7556 static void
7557 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7558    png_info *ping_info, unsigned char *profile_type, unsigned char
7559    *profile_description, unsigned char *profile_data, png_uint_32 length)
7560 {
7561    png_textp
7562      text;
7563
7564    register ssize_t
7565      i;
7566
7567    unsigned char
7568      *sp;
7569
7570    png_charp
7571      dp;
7572
7573    png_uint_32
7574      allocated_length,
7575      description_length;
7576
7577    unsigned char
7578      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7579
7580    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7581       return;
7582
7583    if (image_info->verbose)
7584      {
7585        (void) printf("writing raw profile: type=%s, length=%.20g\n",
7586          (char *) profile_type, (double) length);
7587      }
7588
7589 #if PNG_LIBPNG_VER >= 14000
7590    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7591 #else
7592    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7593 #endif
7594    description_length=(png_uint_32) strlen((const char *) profile_description);
7595    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7596       + description_length);
7597 #if PNG_LIBPNG_VER >= 14000
7598    text[0].text=(png_charp) png_malloc(ping,
7599       (png_alloc_size_t) allocated_length);
7600    text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7601 #else
7602    text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7603    text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7604 #endif
7605    text[0].key[0]='\0';
7606    (void) ConcatenateMagickString(text[0].key,
7607       "Raw profile type ",MaxTextExtent);
7608    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7609    sp=profile_data;
7610    dp=text[0].text;
7611    *dp++='\n';
7612    (void) CopyMagickString(dp,(const char *) profile_description,
7613      allocated_length);
7614    dp+=description_length;
7615    *dp++='\n';
7616    (void) FormatLocaleString(dp,allocated_length-
7617      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7618    dp+=8;
7619
7620    for (i=0; i < (ssize_t) length; i++)
7621    {
7622      if (i%36 == 0)
7623        *dp++='\n';
7624      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7625      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7626    }
7627
7628    *dp++='\n';
7629    *dp='\0';
7630    text[0].text_length=(png_size_t) (dp-text[0].text);
7631    text[0].compression=image_info->compression == NoCompression ||
7632      (image_info->compression == UndefinedCompression &&
7633      text[0].text_length < 128) ? -1 : 0;
7634
7635    if (text[0].text_length <= allocated_length)
7636      png_set_text(ping,ping_info,text,1);
7637
7638    png_free(ping,text[0].text);
7639    png_free(ping,text[0].key);
7640    png_free(ping,text);
7641 }
7642
7643 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7644   const char *string, MagickBooleanType logging)
7645 {
7646   char
7647     *name;
7648
7649   const StringInfo
7650     *profile;
7651
7652   unsigned char
7653     *data;
7654
7655   png_uint_32 length;
7656
7657   ResetImageProfileIterator(image);
7658
7659   for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7660   {
7661     profile=GetImageProfile(image,name);
7662
7663     if (profile != (const StringInfo *) NULL)
7664       {
7665         StringInfo
7666           *ping_profile;
7667
7668         if (LocaleNCompare(name,string,11) == 0)
7669           {
7670             if (logging != MagickFalse)
7671                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7672                    "  Found %s profile",name);
7673
7674             ping_profile=CloneStringInfo(profile);
7675             data=GetStringInfoDatum(ping_profile),
7676             length=(png_uint_32) GetStringInfoLength(ping_profile);
7677             data[4]=data[3];
7678             data[3]=data[2];
7679             data[2]=data[1];
7680             data[1]=data[0];
7681             (void) WriteBlobMSBULong(image,length-5);  /* data length */
7682             (void) WriteBlob(image,length-1,data+1);
7683             (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7684             ping_profile=DestroyStringInfo(ping_profile);
7685           }
7686       }
7687
7688       name=GetNextImageProfile(image);
7689    }
7690
7691    return(MagickTrue);
7692 }
7693
7694
7695 /* Write one PNG image */
7696 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7697   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7698 {
7699   Image
7700     *image;
7701
7702   ImageInfo
7703     *image_info;
7704
7705   char
7706     s[2];
7707
7708   const char
7709     *name,
7710     *property,
7711     *value;
7712
7713   const StringInfo
7714     *profile;
7715
7716   int
7717     num_passes,
7718     pass;
7719
7720   png_byte
7721      ping_trans_alpha[256];
7722
7723   png_color
7724      palette[257];
7725
7726   png_color_16
7727     ping_background,
7728     ping_trans_color;
7729
7730   png_info
7731     *ping_info;
7732
7733   png_struct
7734     *ping;
7735
7736   png_uint_32
7737     ping_height,
7738     ping_width;
7739
7740   ssize_t
7741     y;
7742
7743   MagickBooleanType
7744     image_matte,
7745     logging,
7746     matte,
7747
7748     ping_have_blob,
7749     ping_have_cheap_transparency,
7750     ping_have_color,
7751     ping_have_non_bw,
7752     ping_have_PLTE,
7753     ping_have_bKGD,
7754     ping_have_pHYs,
7755     ping_have_tRNS,
7756
7757     ping_exclude_bKGD,
7758     ping_exclude_cHRM,
7759     ping_exclude_date,
7760     /* ping_exclude_EXIF, */
7761     ping_exclude_gAMA,
7762     ping_exclude_iCCP,
7763     /* ping_exclude_iTXt, */
7764     ping_exclude_oFFs,
7765     ping_exclude_pHYs,
7766     ping_exclude_sRGB,
7767     ping_exclude_tEXt,
7768     /* ping_exclude_tRNS, */
7769     ping_exclude_vpAg,
7770     ping_exclude_zCCP, /* hex-encoded iCCP */
7771     ping_exclude_zTXt,
7772
7773     ping_preserve_colormap,
7774     ping_need_colortype_warning,
7775
7776     status,
7777     tried_332,
7778     tried_333,
7779     tried_444;
7780
7781   QuantumInfo
7782     *quantum_info;
7783
7784   PNGErrorInfo
7785     error_info;
7786
7787   register ssize_t
7788     i,
7789     x;
7790
7791   unsigned char
7792     *volatile ping_pixels;
7793
7794   volatile int
7795     image_colors,
7796     ping_bit_depth,
7797     ping_color_type,
7798     ping_interlace_method,
7799     ping_compression_method,
7800     ping_filter_method,
7801     ping_num_trans;
7802
7803   volatile size_t
7804     image_depth,
7805     old_bit_depth;
7806
7807   size_t
7808     quality,
7809     rowbytes,
7810     save_image_depth;
7811
7812   int
7813     j,
7814     number_colors,
7815     number_opaque,
7816     number_semitransparent,
7817     number_transparent,
7818     ping_pHYs_unit_type;
7819
7820   png_uint_32
7821     ping_pHYs_x_resolution,
7822     ping_pHYs_y_resolution;
7823
7824   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7825     "  Enter WriteOnePNGImage()");
7826
7827   image = CloneImage(IMimage,0,0,MagickFalse,exception);
7828   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7829   if (image_info == (ImageInfo *) NULL)
7830      ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7831
7832   /* Initialize some stuff */
7833   ping_bit_depth=0,
7834   ping_color_type=0,
7835   ping_interlace_method=0,
7836   ping_compression_method=0,
7837   ping_filter_method=0,
7838   ping_num_trans = 0;
7839
7840   ping_background.red = 0;
7841   ping_background.green = 0;
7842   ping_background.blue = 0;
7843   ping_background.gray = 0;
7844   ping_background.index = 0;
7845
7846   ping_trans_color.red=0;
7847   ping_trans_color.green=0;
7848   ping_trans_color.blue=0;
7849   ping_trans_color.gray=0;
7850
7851   ping_pHYs_unit_type = 0;
7852   ping_pHYs_x_resolution = 0;
7853   ping_pHYs_y_resolution = 0;
7854
7855   ping_have_blob=MagickFalse;
7856   ping_have_color=MagickTrue;
7857   ping_have_non_bw=MagickTrue;
7858   ping_have_PLTE=MagickFalse;
7859   ping_have_bKGD=MagickFalse;
7860   ping_have_pHYs=MagickFalse;
7861   ping_have_tRNS=MagickFalse;
7862
7863   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7864   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
7865   ping_exclude_date=mng_info->ping_exclude_date;
7866   /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
7867   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
7868   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7869   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7870   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7871   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7872   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7873   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
7874   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
7875   ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7876   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7877   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7878
7879   ping_preserve_colormap = mng_info->ping_preserve_colormap;
7880   ping_need_colortype_warning = MagickFalse;
7881
7882   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7883    * i.e., eliminate the ICC profile and set image->rendering_intent.
7884    * Note that this will not involve any changes to the actual pixels
7885    * but merely passes information to applications that read the resulting
7886    * PNG image.
7887    */
7888    if (ping_exclude_sRGB == MagickFalse)
7889    {
7890       char
7891         *name;
7892
7893       const StringInfo
7894         *profile;
7895
7896       ResetImageProfileIterator(image);
7897       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7898       {
7899         profile=GetImageProfile(image,name);
7900
7901         if (profile != (StringInfo *) NULL)
7902           {
7903             if ((LocaleCompare(name,"ICC") == 0) ||
7904                (LocaleCompare(name,"ICM") == 0))
7905               {
7906                  int
7907                    icheck;
7908
7909                  /* 0: not a known sRGB profile
7910                   * 1: HP-Microsoft sRGB v2
7911                   * 2: ICC sRGB v4 perceptual
7912                   * 3: ICC sRGB v2 perceptual no black-compensation
7913                   */
7914                  png_uint_32
7915                    check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7916                    check_len[4] = {0, 3144, 60960, 3052};
7917
7918                  png_uint_32
7919                    length,
7920                    profile_crc;
7921
7922                  unsigned char
7923                    *data;
7924
7925                  length=(png_uint_32) GetStringInfoLength(profile);
7926
7927                  for (icheck=3; icheck > 0; icheck--)
7928                  {
7929                    if (length == check_len[icheck])
7930                    {
7931                      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7932                          "    Got a %lu-byte ICC profile (potentially sRGB)",
7933                          (unsigned long) length);
7934
7935                      data=GetStringInfoDatum(profile);
7936                      profile_crc=crc32(0,data,length);
7937
7938                      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7939                          "      with crc=%8x",(unsigned int) profile_crc);
7940
7941                      if (profile_crc == check_crc[icheck])
7942                      {
7943                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7944                             "      It is sRGB.");
7945                         if (image->rendering_intent==UndefinedIntent)
7946                           image->rendering_intent=PerceptualIntent;
7947                         break;
7948                      }
7949                    }
7950                  }
7951                  if (icheck == 0)
7952                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7953                         "    Got a %lu-byte ICC profile",
7954                         (unsigned long) length);
7955               }
7956           }
7957         name=GetNextImageProfile(image);
7958       }
7959   }
7960
7961   number_opaque = 0;
7962   number_semitransparent = 0;
7963   number_transparent = 0;
7964
7965   if (logging != MagickFalse)
7966     {
7967       if (image->storage_class == UndefinedClass)
7968           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7969           "    storage_class=UndefinedClass");
7970       if (image->storage_class == DirectClass)
7971           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7972           "    storage_class=DirectClass");
7973       if (image->storage_class == PseudoClass)
7974           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7975           "    storage_class=PseudoClass");
7976     }
7977
7978   if (image->storage_class == PseudoClass &&
7979      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7980      mng_info->write_png48 || mng_info->write_png64 ||
7981      (mng_info->write_png_colortype != 1 &&
7982      mng_info->write_png_colortype != 5)))
7983     {
7984       (void) SyncImage(image,exception);
7985       image->storage_class = DirectClass;
7986     }
7987
7988   if (ping_preserve_colormap == MagickFalse)
7989     {
7990       if (image->storage_class != PseudoClass && image->colormap != NULL)
7991         {
7992           /* Free the bogus colormap; it can cause trouble later */
7993            if (logging != MagickFalse)
7994               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7995               "    Freeing bogus colormap");
7996            (void) RelinquishMagickMemory(image->colormap);
7997            image->colormap=NULL;
7998         }
7999     }
8000
8001   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8002     (void) TransformImageColorspace(image,sRGBColorspace,exception);
8003
8004   /*
8005     Sometimes we get PseudoClass images whose RGB values don't match
8006     the colors in the colormap.  This code syncs the RGB values.
8007   */
8008   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8009      (void) SyncImage(image,exception);
8010
8011 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8012   if (image->depth > 8)
8013     {
8014       if (logging != MagickFalse)
8015         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8016           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8017
8018       image->depth=8;
8019     }
8020 #endif
8021
8022   /* Respect the -depth option */
8023   if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
8024     {
8025        register Quantum
8026          *r;
8027
8028        if (image->depth > 8)
8029          {
8030 #if MAGICKCORE_QUANTUM_DEPTH > 16
8031            /* Scale to 16-bit */
8032            LBR16PacketRGBO(image->background_color);
8033
8034            for (y=0; y < (ssize_t) image->rows; y++)
8035            {
8036              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8037
8038              if (r == (Quantum *) NULL)
8039                break;
8040
8041              for (x=0; x < (ssize_t) image->columns; x++)
8042              {
8043                 LBR16PixelRGBA(r);
8044                 r+=GetPixelChannels(image);
8045              }
8046
8047              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8048                 break;
8049            }
8050
8051            if (image->storage_class == PseudoClass && image->colormap != NULL)
8052            {
8053              for (i=0; i < (ssize_t) image->colors; i++)
8054              {
8055                LBR16PacketRGBO(image->colormap[i]);
8056              }
8057            }
8058 #endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
8059          }
8060
8061        else if (image->depth > 4)
8062          {
8063 #if MAGICKCORE_QUANTUM_DEPTH > 8
8064            /* Scale to 8-bit */
8065            LBR08PacketRGBO(image->background_color);
8066
8067            for (y=0; y < (ssize_t) image->rows; y++)
8068            {
8069              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8070
8071              if (r == (Quantum *) NULL)
8072                break;
8073
8074              for (x=0; x < (ssize_t) image->columns; x++)
8075              {
8076                 LBR08PixelRGBA(r);
8077                 r+=GetPixelChannels(image);
8078              }
8079
8080              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8081                 break;
8082            }
8083
8084            if (image->storage_class == PseudoClass && image->colormap != NULL)
8085            {
8086              for (i=0; i < (ssize_t) image->colors; i++)
8087              {
8088                LBR08PacketRGBO(image->colormap[i]);
8089              }
8090            }
8091 #endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
8092          }
8093        else
8094          if (image->depth > 2)
8095          {
8096            /* Scale to 4-bit */
8097            LBR04PacketRGBO(image->background_color);
8098
8099            for (y=0; y < (ssize_t) image->rows; y++)
8100            {
8101              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8102
8103              if (r == (Quantum *) NULL)
8104                break;
8105
8106              for (x=0; x < (ssize_t) image->columns; x++)
8107              {
8108                 LBR04PixelRGBA(r);
8109                 r+=GetPixelChannels(image);
8110              }
8111
8112              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8113                 break;
8114            }
8115
8116            if (image->storage_class == PseudoClass && image->colormap != NULL)
8117            {
8118              for (i=0; i < (ssize_t) image->colors; i++)
8119              {
8120                LBR04PacketRGBO(image->colormap[i]);
8121              }
8122            }
8123          }
8124
8125        else if (image->depth > 1)
8126          {
8127            /* Scale to 2-bit */
8128            LBR02PacketRGBO(image->background_color);
8129
8130            for (y=0; y < (ssize_t) image->rows; y++)
8131            {
8132              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8133
8134              if (r == (Quantum *) NULL)
8135                break;
8136
8137              for (x=0; x < (ssize_t) image->columns; x++)
8138              {
8139                 LBR02PixelRGBA(r);
8140                 r+=GetPixelChannels(image);
8141              }
8142
8143              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8144                 break;
8145            }
8146
8147            if (image->storage_class == PseudoClass && image->colormap != NULL)
8148            {
8149              for (i=0; i < (ssize_t) image->colors; i++)
8150              {
8151                LBR02PacketRGBO(image->colormap[i]);
8152              }
8153            }
8154          }
8155        else
8156          {
8157            /* Scale to 1-bit */
8158            LBR01PacketRGBO(image->background_color);
8159
8160            for (y=0; y < (ssize_t) image->rows; y++)
8161            {
8162              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8163
8164              if (r == (Quantum *) NULL)
8165                break;
8166
8167              for (x=0; x < (ssize_t) image->columns; x++)
8168              {
8169                 LBR01PixelRGBA(r);
8170                 r+=GetPixelChannels(image);
8171              }
8172
8173              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8174                 break;
8175            }
8176
8177            if (image->storage_class == PseudoClass && image->colormap != NULL)
8178            {
8179              for (i=0; i < (ssize_t) image->colors; i++)
8180              {
8181                LBR01PacketRGBO(image->colormap[i]);
8182              }
8183            }
8184          }
8185     }
8186
8187   /* To do: set to next higher multiple of 8 */
8188   if (image->depth < 8)
8189      image->depth=8;
8190
8191 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8192   /* PNG does not handle depths greater than 16 so reduce it even
8193    * if lossy
8194    */
8195   if (image->depth > 8)
8196       image->depth=16;
8197 #endif
8198
8199 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8200   if (image->depth > 8)
8201     {
8202       /* To do: fill low byte properly */
8203       image->depth=16;
8204     }
8205
8206   if (image->depth == 16 && mng_info->write_png_depth != 16)
8207     if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8208       image->depth = 8;
8209 #endif
8210
8211   if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
8212      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8213      mng_info->write_png_colortype < 4 &&
8214      image->alpha_trait != BlendPixelTrait)))
8215   {
8216      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8217       * are not going to need the result.
8218       */
8219      image_colors = (int) image->colors;
8220      number_opaque = (int) image->colors;
8221      if (mng_info->write_png_colortype == 1 ||
8222         mng_info->write_png_colortype == 5)
8223        ping_have_color=MagickFalse;
8224      else
8225        ping_have_color=MagickTrue;
8226      ping_have_non_bw=MagickFalse;
8227
8228      if (image->alpha_trait == BlendPixelTrait)
8229        {
8230          number_transparent = 2;
8231          number_semitransparent = 1;
8232        }
8233
8234      else
8235        {
8236          number_transparent = 0;
8237          number_semitransparent = 0;
8238        }
8239   }
8240
8241   else
8242   {
8243   /* BUILD_PALETTE
8244    *
8245    * Normally we run this just once, but in the case of writing PNG8
8246    * we reduce the transparency to binary and run again, then if there
8247    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8248    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8249    * palette.  Then (To do) we take care of a final reduction that is only
8250    * needed if there are still 256 colors present and one of them has both
8251    * transparent and opaque instances.
8252    */
8253
8254   tried_332 = MagickFalse;
8255   tried_333 = MagickFalse;
8256   tried_444 = MagickFalse;
8257
8258   for (j=0; j<6; j++)
8259   {
8260     /*
8261      * Sometimes we get DirectClass images that have 256 colors or fewer.
8262      * This code will build a colormap.
8263      *
8264      * Also, sometimes we get PseudoClass images with an out-of-date
8265      * colormap.  This code will replace the colormap with a new one.
8266      * Sometimes we get PseudoClass images that have more than 256 colors.
8267      * This code will delete the colormap and change the image to
8268      * DirectClass.
8269      *
8270      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8271      * even though it sometimes contains left-over non-opaque values.
8272      *
8273      * Also we gather some information (number of opaque, transparent,
8274      * and semitransparent pixels, and whether the image has any non-gray
8275      * pixels or only black-and-white pixels) that we might need later.
8276      *
8277      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8278      * we need to check for bogus non-opaque values, at least.
8279      */
8280
8281    int
8282      n;
8283
8284    PixelInfo
8285      opaque[260],
8286      semitransparent[260],
8287      transparent[260];
8288
8289    register const Quantum
8290      *s;
8291
8292    register Quantum
8293      *q,
8294      *r;
8295
8296    if (logging != MagickFalse)
8297      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8298          "    Enter BUILD_PALETTE:");
8299
8300    if (logging != MagickFalse)
8301      {
8302        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8303              "      image->columns=%.20g",(double) image->columns);
8304        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8305              "      image->rows=%.20g",(double) image->rows);
8306        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8307              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
8308        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8309              "      image->depth=%.20g",(double) image->depth);
8310
8311        if (image->storage_class == PseudoClass && image->colormap != NULL)
8312        {
8313          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8314              "      Original colormap:");
8315          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8316              "        i    (red,green,blue,alpha)");
8317
8318          for (i=0; i < 256; i++)
8319          {
8320                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8321                    "        %d    (%d,%d,%d,%d)",
8322                     (int) i,
8323                     (int) image->colormap[i].red,
8324                     (int) image->colormap[i].green,
8325                     (int) image->colormap[i].blue,
8326                     (int) image->colormap[i].alpha);
8327          }
8328
8329          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8330          {
8331            if (i > 255)
8332              {
8333                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8334                    "        %d    (%d,%d,%d,%d)",
8335                     (int) i,
8336                     (int) image->colormap[i].red,
8337                     (int) image->colormap[i].green,
8338                     (int) image->colormap[i].blue,
8339                     (int) image->colormap[i].alpha);
8340              }
8341          }
8342        }
8343
8344        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8345            "      image->colors=%d",(int) image->colors);
8346
8347        if (image->colors == 0)
8348          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8349              "        (zero means unknown)");
8350
8351        if (ping_preserve_colormap == MagickFalse)
8352          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8353               "      Regenerate the colormap");
8354      }
8355
8356      image_colors=0;
8357      number_opaque = 0;
8358      number_semitransparent = 0;
8359      number_transparent = 0;
8360
8361      for (y=0; y < (ssize_t) image->rows; y++)
8362      {
8363        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8364
8365        if (q == (Quantum *) NULL)
8366          break;
8367
8368        for (x=0; x < (ssize_t) image->columns; x++)
8369        {
8370            if (image->alpha_trait != BlendPixelTrait ||
8371               GetPixelAlpha(image,q) == OpaqueAlpha)
8372              {
8373                if (number_opaque < 259)
8374                  {
8375                    if (number_opaque == 0)
8376                      {
8377                        GetPixelInfoPixel(image, q, opaque);
8378                        opaque[0].alpha=OpaqueAlpha;
8379                        number_opaque=1;
8380                      }
8381
8382                    for (i=0; i< (ssize_t) number_opaque; i++)
8383                      {
8384                        if (IsPixelEquivalent(image,q, opaque+i))
8385                          break;
8386                      }
8387
8388                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
8389                      {
8390                        number_opaque++;
8391                        GetPixelInfoPixel(image, q, opaque+i);
8392                        opaque[i].alpha=OpaqueAlpha;
8393                      }
8394                  }
8395              }
8396            else if (GetPixelAlpha(image,q) == TransparentAlpha)
8397              {
8398                if (number_transparent < 259)
8399                  {
8400                    if (number_transparent == 0)
8401                      {
8402                        GetPixelInfoPixel(image, q, transparent);
8403                        ping_trans_color.red=(unsigned short)
8404                          GetPixelRed(image,q);
8405                        ping_trans_color.green=(unsigned short)
8406                          GetPixelGreen(image,q);
8407                        ping_trans_color.blue=(unsigned short)
8408                          GetPixelBlue(image,q);
8409                        ping_trans_color.gray=(unsigned short)
8410                          GetPixelGray(image,q);
8411                        number_transparent = 1;
8412                      }
8413
8414                    for (i=0; i< (ssize_t) number_transparent; i++)
8415                      {
8416                        if (IsPixelEquivalent(image,q, transparent+i))
8417                          break;
8418                      }
8419
8420                    if (i ==  (ssize_t) number_transparent &&
8421                        number_transparent < 259)
8422                      {
8423                        number_transparent++;
8424                        GetPixelInfoPixel(image,q,transparent+i);
8425                      }
8426                  }
8427              }
8428            else
8429              {
8430                if (number_semitransparent < 259)
8431                  {
8432                    if (number_semitransparent == 0)
8433                      {
8434                        GetPixelInfoPixel(image,q,semitransparent);
8435                        number_semitransparent = 1;
8436                      }
8437
8438                    for (i=0; i< (ssize_t) number_semitransparent; i++)
8439                      {
8440                        if (IsPixelEquivalent(image,q, semitransparent+i)
8441                            && GetPixelAlpha(image,q) ==
8442                            semitransparent[i].alpha)
8443                          break;
8444                      }
8445
8446                    if (i ==  (ssize_t) number_semitransparent &&
8447                        number_semitransparent < 259)
8448                      {
8449                        number_semitransparent++;
8450                        GetPixelInfoPixel(image, q, semitransparent+i);
8451                      }
8452                  }
8453              }
8454            q+=GetPixelChannels(image);
8455         }
8456      }
8457
8458      if (mng_info->write_png8 == MagickFalse &&
8459          ping_exclude_bKGD == MagickFalse)
8460        {
8461          /* Add the background color to the palette, if it
8462           * isn't already there.
8463           */
8464           if (logging != MagickFalse)
8465             {
8466               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8467                   "      Check colormap for background (%d,%d,%d)",
8468                   (int) image->background_color.red,
8469                   (int) image->background_color.green,
8470                   (int) image->background_color.blue);
8471             }
8472           for (i=0; i<number_opaque; i++)
8473           {
8474              if (opaque[i].red == image->background_color.red &&
8475                  opaque[i].green == image->background_color.green &&
8476                  opaque[i].blue == image->background_color.blue)
8477                break;
8478           }
8479           if (number_opaque < 259 && i == number_opaque)
8480             {
8481                opaque[i] = image->background_color;
8482                ping_background.index = i;
8483                number_opaque++;
8484                if (logging != MagickFalse)
8485                  {
8486                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8487                        "      background_color index is %d",(int) i);
8488                  }
8489
8490             }
8491           else if (logging != MagickFalse)
8492               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8493                   "      No room in the colormap to add background color");
8494        }
8495
8496      image_colors=number_opaque+number_transparent+number_semitransparent;
8497
8498      if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8499        {
8500          /* No room for the background color; remove it. */
8501          number_opaque--;
8502          image_colors--;
8503        }
8504
8505      if (logging != MagickFalse)
8506        {
8507          if (image_colors > 256)
8508             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8509                   "      image has more than 256 colors");
8510
8511          else
8512             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8513                   "      image has %d colors",image_colors);
8514        }
8515
8516      if (ping_preserve_colormap != MagickFalse)
8517        break;
8518
8519      if (mng_info->write_png_colortype != 7) /* We won't need this info */
8520        {
8521          ping_have_color=MagickFalse;
8522          ping_have_non_bw=MagickFalse;
8523
8524          if ((IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) ||
8525              (IssRGBColorspace(image->colorspace) != MagickFalse))
8526          {
8527            ping_have_color=MagickTrue;
8528            ping_have_non_bw=MagickTrue;
8529          }
8530
8531          if(image_colors > 256)
8532            {
8533              for (y=0; y < (ssize_t) image->rows; y++)
8534              {
8535                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8536
8537                if (q == (Quantum *) NULL)
8538                  break;
8539
8540                s=q;
8541                for (x=0; x < (ssize_t) image->columns; x++)
8542                {
8543                  if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8544                      GetPixelRed(image,s) != GetPixelBlue(image,s))
8545                    {
8546                       ping_have_color=MagickTrue;
8547                       ping_have_non_bw=MagickTrue;
8548                       break;
8549                    }
8550                  s+=GetPixelChannels(image);
8551                }
8552
8553                if (ping_have_color != MagickFalse)
8554                  break;
8555
8556                /* Worst case is black-and-white; we are looking at every
8557                 * pixel twice.
8558                 */
8559
8560                if (ping_have_non_bw == MagickFalse)
8561                  {
8562                    s=q;
8563                    for (x=0; x < (ssize_t) image->columns; x++)
8564                    {
8565                      if (GetPixelRed(image,s) != 0 &&
8566                          GetPixelRed(image,s) != QuantumRange)
8567                        {
8568                          ping_have_non_bw=MagickTrue;
8569                          break;
8570                        }
8571                      s+=GetPixelChannels(image);
8572                    }
8573                }
8574              }
8575            }
8576        }
8577
8578      if (image_colors < 257)
8579        {
8580          PixelInfo
8581            colormap[260];
8582
8583          /*
8584           * Initialize image colormap.
8585           */
8586
8587          if (logging != MagickFalse)
8588             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8589                   "      Sort the new colormap");
8590
8591         /* Sort palette, transparent first */;
8592
8593          n = 0;
8594
8595          for (i=0; i<number_transparent; i++)
8596             colormap[n++] = transparent[i];
8597
8598          for (i=0; i<number_semitransparent; i++)
8599             colormap[n++] = semitransparent[i];
8600
8601          for (i=0; i<number_opaque; i++)
8602             colormap[n++] = opaque[i];
8603
8604          ping_background.index +=
8605            (number_transparent + number_semitransparent);
8606
8607          /* image_colors < 257; search the colormap instead of the pixels
8608           * to get ping_have_color and ping_have_non_bw
8609           */
8610          for (i=0; i<n; i++)
8611          {
8612            if (ping_have_color == MagickFalse)
8613              {
8614                 if (colormap[i].red != colormap[i].green ||
8615                     colormap[i].red != colormap[i].blue)
8616                   {
8617                      ping_have_color=MagickTrue;
8618                      ping_have_non_bw=MagickTrue;
8619                      break;
8620                   }
8621               }
8622
8623            if (ping_have_non_bw == MagickFalse)
8624              {
8625                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8626                    ping_have_non_bw=MagickTrue;
8627              }
8628           }
8629
8630         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8631             (number_transparent == 0 && number_semitransparent == 0)) &&
8632             (((mng_info->write_png_colortype-1) ==
8633             PNG_COLOR_TYPE_PALETTE) ||
8634             (mng_info->write_png_colortype == 0)))
8635           {
8636             if (logging != MagickFalse)
8637               {
8638                 if (n !=  (ssize_t) image_colors)
8639                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8640                    "   image_colors (%d) and n (%d)  don't match",
8641                    image_colors, n);
8642
8643                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8644                    "      AcquireImageColormap");
8645               }
8646
8647             image->colors = image_colors;
8648
8649             if (AcquireImageColormap(image,image_colors,exception) ==
8650                 MagickFalse)
8651                ThrowWriterException(ResourceLimitError,
8652                    "MemoryAllocationFailed");
8653
8654             for (i=0; i< (ssize_t) image_colors; i++)
8655                image->colormap[i] = colormap[i];
8656
8657             if (logging != MagickFalse)
8658               {
8659                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8660                       "      image->colors=%d (%d)",
8661                       (int) image->colors, image_colors);
8662
8663                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8664                       "      Update the pixel indexes");
8665               }
8666
8667             /* Sync the pixel indices with the new colormap */
8668
8669             for (y=0; y < (ssize_t) image->rows; y++)
8670             {
8671               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8672
8673               if (q == (Quantum *) NULL)
8674                 break;
8675
8676               for (x=0; x < (ssize_t) image->columns; x++)
8677               {
8678                 for (i=0; i< (ssize_t) image_colors; i++)
8679                 {
8680                   if ((image->alpha_trait != BlendPixelTrait ||
8681                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8682                       image->colormap[i].red == GetPixelRed(image,q) &&
8683                       image->colormap[i].green == GetPixelGreen(image,q) &&
8684                       image->colormap[i].blue == GetPixelBlue(image,q))
8685                   {
8686                     SetPixelIndex(image,i,q);
8687                     break;
8688                   }
8689                 }
8690                 q+=GetPixelChannels(image);
8691               }
8692
8693               if (SyncAuthenticPixels(image,exception) == MagickFalse)
8694                  break;
8695             }
8696           }
8697        }
8698
8699      if (logging != MagickFalse)
8700        {
8701          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8702             "      image->colors=%d", (int) image->colors);
8703
8704          if (image->colormap != NULL)
8705            {
8706              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8707                  "       i     (red,green,blue,alpha)");
8708
8709              for (i=0; i < (ssize_t) image->colors; i++)
8710              {
8711                if (i < 300 || i >= (ssize_t) image->colors - 10)
8712                  {
8713                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8714                        "       %d     (%d,%d,%d,%d)",
8715                         (int) i,
8716                         (int) image->colormap[i].red,
8717                         (int) image->colormap[i].green,
8718                         (int) image->colormap[i].blue,
8719                         (int) image->colormap[i].alpha);
8720                  }
8721              }
8722            }
8723
8724            if (number_transparent < 257)
8725              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8726                    "      number_transparent     = %d",
8727                    number_transparent);
8728            else
8729
8730              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8731                    "      number_transparent     > 256");
8732
8733            if (number_opaque < 257)
8734              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8735                    "      number_opaque          = %d",
8736                    number_opaque);
8737
8738            else
8739              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8740                    "      number_opaque          > 256");
8741
8742            if (number_semitransparent < 257)
8743              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8744                    "      number_semitransparent = %d",
8745                    number_semitransparent);
8746
8747            else
8748              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8749                    "      number_semitransparent > 256");
8750
8751            if (ping_have_non_bw == MagickFalse)
8752               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8753                     "      All pixels and the background are black or white");
8754
8755            else if (ping_have_color == MagickFalse)
8756               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8757                     "      All pixels and the background are gray");
8758
8759            else
8760               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8761                     "      At least one pixel or the background is non-gray");
8762
8763            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8764                "    Exit BUILD_PALETTE:");
8765        }
8766
8767    if (mng_info->write_png8 == MagickFalse)
8768       break;
8769
8770    /* Make any reductions necessary for the PNG8 format */
8771     if (image_colors <= 256 &&
8772         image_colors != 0 && image->colormap != NULL &&
8773         number_semitransparent == 0 &&
8774         number_transparent <= 1)
8775       break;
8776
8777     /* PNG8 can't have semitransparent colors so we threshold the
8778      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8779      * transparent color so if more than one is transparent we merge
8780      * them into image->background_color.
8781      */
8782     if (number_semitransparent != 0 || number_transparent > 1)
8783       {
8784         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8785             "    Thresholding the alpha channel to binary");
8786
8787         for (y=0; y < (ssize_t) image->rows; y++)
8788         {
8789           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8790
8791           if (r == (Quantum *) NULL)
8792             break;
8793
8794           for (x=0; x < (ssize_t) image->columns; x++)
8795           {
8796               if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8797                 {
8798                   SetPixelInfoPixel(image,&image->background_color,r);
8799                   SetPixelAlpha(image,TransparentAlpha,r);
8800                 }
8801               else
8802                   SetPixelAlpha(image,OpaqueAlpha,r);
8803               r+=GetPixelChannels(image);
8804           }
8805
8806           if (SyncAuthenticPixels(image,exception) == MagickFalse)
8807              break;
8808
8809           if (image_colors != 0 && image_colors <= 256 &&
8810              image->colormap != NULL)
8811             for (i=0; i<image_colors; i++)
8812                 image->colormap[i].alpha =
8813                     (image->colormap[i].alpha > TransparentAlpha/2 ?
8814                     TransparentAlpha : OpaqueAlpha);
8815         }
8816       continue;
8817     }
8818
8819     /* PNG8 can't have more than 256 colors so we quantize the pixels and
8820      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
8821      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8822      * colors or less.
8823      */
8824     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8825       {
8826         if (logging != MagickFalse)
8827            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8828                "    Quantizing the background color to 4-4-4");
8829
8830         tried_444 = MagickTrue;
8831
8832         LBR04PacketRGB(image->background_color);
8833
8834         if (logging != MagickFalse)
8835           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8836               "    Quantizing the pixel colors to 4-4-4");
8837
8838         if (image->colormap == NULL)
8839         {
8840           for (y=0; y < (ssize_t) image->rows; y++)
8841           {
8842             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8843
8844             if (r == (Quantum *) NULL)
8845               break;
8846
8847             for (x=0; x < (ssize_t) image->columns; x++)
8848             {
8849               if (GetPixelAlpha(image,r) == OpaqueAlpha)
8850                   LBR04PixelRGB(r);
8851               r+=GetPixelChannels(image);
8852             }
8853
8854             if (SyncAuthenticPixels(image,exception) == MagickFalse)
8855                break;
8856           }
8857         }
8858
8859         else /* Should not reach this; colormap already exists and
8860                 must be <= 256 */
8861         {
8862           if (logging != MagickFalse)
8863               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8864               "    Quantizing the colormap to 4-4-4");
8865
8866           for (i=0; i<image_colors; i++)
8867           {
8868             LBR04PacketRGB(image->colormap[i]);
8869           }
8870         }
8871         continue;
8872       }
8873
8874     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8875       {
8876         if (logging != MagickFalse)
8877            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8878                "    Quantizing the background color to 3-3-3");
8879
8880         tried_333 = MagickTrue;
8881
8882         LBR03PacketRGB(image->background_color);
8883
8884         if (logging != MagickFalse)
8885           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8886               "    Quantizing the pixel colors to 3-3-3-1");
8887
8888         if (image->colormap == NULL)
8889         {
8890           for (y=0; y < (ssize_t) image->rows; y++)
8891           {
8892             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8893
8894             if (r == (Quantum *) NULL)
8895               break;
8896
8897             for (x=0; x < (ssize_t) image->columns; x++)
8898             {
8899               if (GetPixelAlpha(image,r) == OpaqueAlpha)
8900                   LBR03RGB(r);
8901               r+=GetPixelChannels(image);
8902             }
8903
8904             if (SyncAuthenticPixels(image,exception) == MagickFalse)
8905                break;
8906           }
8907         }
8908
8909         else /* Should not reach this; colormap already exists and
8910                 must be <= 256 */
8911         {
8912           if (logging != MagickFalse)
8913               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8914               "    Quantizing the colormap to 3-3-3-1");
8915           for (i=0; i<image_colors; i++)
8916           {
8917               LBR03PacketRGB(image->colormap[i]);
8918           }
8919         }
8920         continue;
8921       }
8922
8923     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8924       {
8925         if (logging != MagickFalse)
8926            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8927                "    Quantizing the background color to 3-3-2");
8928
8929         tried_332 = MagickTrue;
8930
8931         /* Red and green were already done so we only quantize the blue
8932          * channel
8933          */
8934
8935         LBR02PacketBlue(image->background_color);
8936
8937         if (logging != MagickFalse)
8938           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8939               "    Quantizing the pixel colors to 3-3-2-1");
8940
8941         if (image->colormap == NULL)
8942         {
8943           for (y=0; y < (ssize_t) image->rows; y++)
8944           {
8945             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8946
8947             if (r == (Quantum *) NULL)
8948               break;
8949
8950             for (x=0; x < (ssize_t) image->columns; x++)
8951             {
8952               if (GetPixelAlpha(image,r) == OpaqueAlpha)
8953                   LBR02PixelBlue(r);
8954               r+=GetPixelChannels(image);
8955             }
8956
8957             if (SyncAuthenticPixels(image,exception) == MagickFalse)
8958                break;
8959           }
8960         }
8961
8962         else /* Should not reach this; colormap already exists and
8963                 must be <= 256 */
8964         {
8965           if (logging != MagickFalse)
8966               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8967               "    Quantizing the colormap to 3-3-2-1");
8968           for (i=0; i<image_colors; i++)
8969           {
8970               LBR02PacketBlue(image->colormap[i]);
8971           }
8972       }
8973       continue;
8974     }
8975     break;
8976
8977     if (image_colors == 0 || image_colors > 256)
8978     {
8979       /* Take care of special case with 256 colors + 1 transparent
8980        * color.  We don't need to quantize to 2-3-2-1; we only need to
8981        * eliminate one color, so we'll merge the two darkest red
8982        * colors (0x49, 0, 0) -> (0x24, 0, 0).
8983        */
8984       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8985           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8986           ScaleQuantumToChar(image->background_color.blue) == 0x00)
8987       {
8988          image->background_color.red=ScaleCharToQuantum(0x24);
8989       }
8990
8991       if (image->colormap == NULL)
8992       {
8993         for (y=0; y < (ssize_t) image->rows; y++)
8994         {
8995           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8996
8997           if (r == (Quantum *) NULL)
8998             break;
8999
9000           for (x=0; x < (ssize_t) image->columns; x++)
9001           {
9002             if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9003                 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9004                 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9005                 GetPixelAlpha(image,r) == OpaqueAlpha)
9006               {
9007                 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9008               }
9009             r+=GetPixelChannels(image);
9010           }
9011
9012           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9013              break;
9014
9015         }
9016       }
9017
9018       else
9019       {
9020          for (i=0; i<image_colors; i++)
9021          {
9022             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9023                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9024                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9025             {
9026                image->colormap[i].red=ScaleCharToQuantum(0x24);
9027             }
9028          }
9029       }
9030     }
9031   }
9032   }
9033   /* END OF BUILD_PALETTE */
9034
9035   /* If we are excluding the tRNS chunk and there is transparency,
9036    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9037    * PNG.
9038    */
9039   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9040      (number_transparent != 0 || number_semitransparent != 0))
9041     {
9042       unsigned int colortype=mng_info->write_png_colortype;
9043
9044       if (ping_have_color == MagickFalse)
9045         mng_info->write_png_colortype = 5;
9046
9047       else
9048         mng_info->write_png_colortype = 7;
9049
9050       if (colortype != 0 &&
9051          mng_info->write_png_colortype != colortype)
9052         ping_need_colortype_warning=MagickTrue;
9053
9054     }
9055
9056   /* See if cheap transparency is possible.  It is only possible
9057    * when there is a single transparent color, no semitransparent
9058    * color, and no opaque color that has the same RGB components
9059    * as the transparent color.  We only need this information if
9060    * we are writing a PNG with colortype 0 or 2, and we have not
9061    * excluded the tRNS chunk.
9062    */
9063   if (number_transparent == 1 &&
9064       mng_info->write_png_colortype < 4)
9065     {
9066        ping_have_cheap_transparency = MagickTrue;
9067
9068        if (number_semitransparent != 0)
9069          ping_have_cheap_transparency = MagickFalse;
9070
9071        else if (image_colors == 0 || image_colors > 256 ||
9072            image->colormap == NULL)
9073          {
9074            register const Quantum
9075              *q;
9076
9077            for (y=0; y < (ssize_t) image->rows; y++)
9078            {
9079              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9080
9081              if (q == (Quantum *) NULL)
9082                break;
9083
9084              for (x=0; x < (ssize_t) image->columns; x++)
9085              {
9086                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9087                      (unsigned short) GetPixelRed(image,q) ==
9088                                      ping_trans_color.red &&
9089                      (unsigned short) GetPixelGreen(image,q) ==
9090                                      ping_trans_color.green &&
9091                      (unsigned short) GetPixelBlue(image,q) ==
9092                                      ping_trans_color.blue)
9093                    {
9094                      ping_have_cheap_transparency = MagickFalse;
9095                      break;
9096                    }
9097
9098                  q+=GetPixelChannels(image);
9099              }
9100
9101              if (ping_have_cheap_transparency == MagickFalse)
9102                 break;
9103            }
9104          }
9105        else
9106          {
9107             /* Assuming that image->colormap[0] is the one transparent color
9108              * and that all others are opaque.
9109              */
9110             if (image_colors > 1)
9111               for (i=1; i<image_colors; i++)
9112                 if (image->colormap[i].red == image->colormap[0].red &&
9113                     image->colormap[i].green == image->colormap[0].green &&
9114                     image->colormap[i].blue == image->colormap[0].blue)
9115                   {
9116                      ping_have_cheap_transparency = MagickFalse;
9117                      break;
9118                   }
9119          }
9120
9121        if (logging != MagickFalse)
9122          {
9123            if (ping_have_cheap_transparency == MagickFalse)
9124              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9125                  "   Cheap transparency is not possible.");
9126
9127            else
9128              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9129                  "   Cheap transparency is possible.");
9130          }
9131      }
9132   else
9133     ping_have_cheap_transparency = MagickFalse;
9134
9135   image_depth=image->depth;
9136
9137   quantum_info = (QuantumInfo *) NULL;
9138   number_colors=0;
9139   image_colors=(int) image->colors;
9140   image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
9141
9142   mng_info->IsPalette=image->storage_class == PseudoClass &&
9143     image_colors <= 256 && image->colormap != NULL;
9144
9145   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9146      (image->colors == 0 || image->colormap == NULL))
9147     {
9148       image_info=DestroyImageInfo(image_info);
9149       image=DestroyImage(image);
9150       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9151           "Cannot write PNG8 or color-type 3; colormap is NULL",
9152           "`%s'",IMimage->filename);
9153       return(MagickFalse);
9154     }
9155
9156   /*
9157     Allocate the PNG structures
9158   */
9159 #ifdef PNG_USER_MEM_SUPPORTED
9160  error_info.image=image;
9161  error_info.exception=exception;
9162   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9163     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9164     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9165
9166 #else
9167   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9168     MagickPNGErrorHandler,MagickPNGWarningHandler);
9169
9170 #endif
9171   if (ping == (png_struct *) NULL)
9172     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9173
9174   ping_info=png_create_info_struct(ping);
9175
9176   if (ping_info == (png_info *) NULL)
9177     {
9178       png_destroy_write_struct(&ping,(png_info **) NULL);
9179       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9180     }
9181
9182   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9183   ping_pixels=(unsigned char *) NULL;
9184
9185   if (setjmp(png_jmpbuf(ping)))
9186     {
9187       /*
9188         PNG write failed.
9189       */
9190 #ifdef PNG_DEBUG
9191      if (image_info->verbose)
9192         (void) printf("PNG write has failed.\n");
9193 #endif
9194       png_destroy_write_struct(&ping,&ping_info);
9195 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9196       UnlockSemaphoreInfo(ping_semaphore);
9197 #endif
9198
9199       if (ping_pixels != (unsigned char *) NULL)
9200         ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9201
9202       if (quantum_info != (QuantumInfo *) NULL)
9203         quantum_info=DestroyQuantumInfo(quantum_info);
9204
9205       if (ping_have_blob != MagickFalse)
9206           (void) CloseBlob(image);
9207       image_info=DestroyImageInfo(image_info);
9208       image=DestroyImage(image);
9209       return(MagickFalse);
9210     }
9211
9212   /* {  For navigation to end of SETJMP-protected block.  Within this
9213    *    block, use png_error() instead of Throwing an Exception, to ensure
9214    *    that libpng is able to clean up, and that the semaphore is unlocked.
9215    */
9216
9217 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
9218   LockSemaphoreInfo(ping_semaphore);
9219 #endif
9220
9221   /*
9222     Prepare PNG for writing.
9223   */
9224
9225 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9226   if (mng_info->write_mng)
9227   {
9228      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9229 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9230      /* Disable new libpng-1.5.10 feature when writing a MNG because
9231       * zero-length PLTE is OK
9232       */
9233      png_set_check_for_invalid_index (ping, 0);
9234 # endif
9235   }
9236
9237 #else
9238 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9239   if (mng_info->write_mng)
9240      png_permit_empty_plte(ping,MagickTrue);
9241
9242 # endif
9243 #endif
9244
9245   x=0;
9246
9247   ping_width=(png_uint_32) image->columns;
9248   ping_height=(png_uint_32) image->rows;
9249
9250   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9251      image_depth=8;
9252
9253   if (mng_info->write_png48 || mng_info->write_png64)
9254      image_depth=16;
9255
9256   if (mng_info->write_png_depth != 0)
9257      image_depth=mng_info->write_png_depth;
9258
9259   /* Adjust requested depth to next higher valid depth if necessary */
9260   if (image_depth > 8)
9261      image_depth=16;
9262
9263   if ((image_depth > 4) && (image_depth < 8))
9264      image_depth=8;
9265
9266   if (image_depth == 3)
9267      image_depth=4;
9268
9269   if (logging != MagickFalse)
9270     {
9271      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9272         "    width=%.20g",(double) ping_width);
9273      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9274         "    height=%.20g",(double) ping_height);
9275      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9276         "    image_matte=%.20g",(double) image->alpha_trait);
9277      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9278         "    image->depth=%.20g",(double) image->depth);
9279      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9280         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
9281     }
9282
9283   save_image_depth=image_depth;
9284   ping_bit_depth=(png_byte) save_image_depth;
9285
9286
9287 #if defined(PNG_pHYs_SUPPORTED)
9288   if (ping_exclude_pHYs == MagickFalse)
9289   {
9290   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9291       (!mng_info->write_mng || !mng_info->equal_physs))
9292     {
9293       if (logging != MagickFalse)
9294         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9295             "    Setting up pHYs chunk");
9296
9297       if (image->units == PixelsPerInchResolution)
9298         {
9299           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9300           ping_pHYs_x_resolution=
9301              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9302           ping_pHYs_y_resolution=
9303              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9304         }
9305
9306       else if (image->units == PixelsPerCentimeterResolution)
9307         {
9308           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9309           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9310           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9311         }
9312
9313       else
9314         {
9315           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9316           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9317           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9318         }
9319
9320       if (logging != MagickFalse)
9321         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9322           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9323           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9324           (int) ping_pHYs_unit_type);
9325        ping_have_pHYs = MagickTrue;
9326     }
9327   }
9328 #endif
9329
9330   if (ping_exclude_bKGD == MagickFalse)
9331   {
9332   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9333     {
9334        unsigned int
9335          mask;
9336
9337        mask=0xffff;
9338        if (ping_bit_depth == 8)
9339           mask=0x00ff;
9340
9341        if (ping_bit_depth == 4)
9342           mask=0x000f;
9343
9344        if (ping_bit_depth == 2)
9345           mask=0x0003;
9346
9347        if (ping_bit_depth == 1)
9348           mask=0x0001;
9349
9350        ping_background.red=(png_uint_16)
9351          (ScaleQuantumToShort(image->background_color.red) & mask);
9352
9353        ping_background.green=(png_uint_16)
9354          (ScaleQuantumToShort(image->background_color.green) & mask);
9355
9356        ping_background.blue=(png_uint_16)
9357          (ScaleQuantumToShort(image->background_color.blue) & mask);
9358
9359        ping_background.gray=(png_uint_16) ping_background.green;
9360     }
9361
9362   if (logging != MagickFalse)
9363     {
9364       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9365           "    Setting up bKGD chunk (1)");
9366       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9367           "      background_color index is %d",
9368           (int) ping_background.index);
9369
9370       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9371           "    ping_bit_depth=%d",ping_bit_depth);
9372     }
9373
9374   ping_have_bKGD = MagickTrue;
9375   }
9376
9377   /*
9378     Select the color type.
9379   */
9380   matte=image_matte;
9381   old_bit_depth=0;
9382
9383   if (mng_info->IsPalette && mng_info->write_png8)
9384     {
9385
9386       /* To do: make this a function cause it's used twice, except
9387          for reducing the sample depth from 8. */
9388
9389       number_colors=image_colors;
9390
9391       ping_have_tRNS=MagickFalse;
9392
9393       /*
9394         Set image palette.
9395       */
9396       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9397
9398       if (logging != MagickFalse)
9399         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9400             "  Setting up PLTE chunk with %d colors (%d)",
9401             number_colors, image_colors);
9402
9403       for (i=0; i < (ssize_t) number_colors; i++)
9404       {
9405         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9406         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9407         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9408         if (logging != MagickFalse)
9409           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9410 #if MAGICKCORE_QUANTUM_DEPTH == 8
9411             "    %3ld (%3d,%3d,%3d)",
9412 #else
9413             "    %5ld (%5d,%5d,%5d)",
9414 #endif
9415             (long) i,palette[i].red,palette[i].green,palette[i].blue);
9416
9417       }
9418
9419       ping_have_PLTE=MagickTrue;
9420       image_depth=ping_bit_depth;
9421       ping_num_trans=0;
9422
9423       if (matte != MagickFalse)
9424       {
9425           /*
9426             Identify which colormap entry is transparent.
9427           */
9428           assert(number_colors <= 256);
9429           assert(image->colormap != NULL);
9430
9431           for (i=0; i < (ssize_t) number_transparent; i++)
9432              ping_trans_alpha[i]=0;
9433
9434
9435           ping_num_trans=(unsigned short) (number_transparent +
9436              number_semitransparent);
9437
9438           if (ping_num_trans == 0)
9439              ping_have_tRNS=MagickFalse;
9440
9441           else
9442              ping_have_tRNS=MagickTrue;
9443       }
9444
9445       if (ping_exclude_bKGD == MagickFalse)
9446       {
9447        /*
9448         * Identify which colormap entry is the background color.
9449         */
9450
9451         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9452           if (IsPNGColorEqual(ping_background,image->colormap[i]))
9453             break;
9454
9455         ping_background.index=(png_byte) i;
9456
9457         if (logging != MagickFalse)
9458           {
9459             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9460                  "      background_color index is %d",
9461                  (int) ping_background.index);
9462           }
9463       }
9464     } /* end of write_png8 */
9465
9466   else if (mng_info->write_png_colortype == 1)
9467     {
9468       image_matte=MagickFalse;
9469       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9470     }
9471
9472   else if (mng_info->write_png24 || mng_info->write_png48 ||
9473       mng_info->write_png_colortype == 3)
9474     {
9475       image_matte=MagickFalse;
9476       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9477     }
9478
9479   else if (mng_info->write_png32 || mng_info->write_png64 ||
9480       mng_info->write_png_colortype == 7)
9481     {
9482       image_matte=MagickTrue;
9483       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9484     }
9485
9486   else /* mng_info->write_pngNN not specified */
9487     {
9488       image_depth=ping_bit_depth;
9489
9490       if (mng_info->write_png_colortype != 0)
9491         {
9492           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9493
9494           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9495               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9496             image_matte=MagickTrue;
9497
9498           else
9499             image_matte=MagickFalse;
9500
9501           if (logging != MagickFalse)
9502              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9503              "   PNG colortype %d was specified:",(int) ping_color_type);
9504         }
9505
9506       else /* write_png_colortype not specified */
9507         {
9508           if (logging != MagickFalse)
9509              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9510              "  Selecting PNG colortype:");
9511
9512           ping_color_type=(png_byte) ((matte != MagickFalse)?
9513             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9514
9515           if (image_info->type == TrueColorType)
9516             {
9517               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9518               image_matte=MagickFalse;
9519             }
9520
9521           if (image_info->type == TrueColorMatteType)
9522             {
9523               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9524               image_matte=MagickTrue;
9525             }
9526
9527           if (image_info->type == PaletteType ||
9528               image_info->type == PaletteMatteType)
9529             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9530
9531           if (mng_info->write_png_colortype == 0 &&
9532              (image_info->type == UndefinedType ||
9533              image_info->type == OptimizeType))
9534             {
9535               if (ping_have_color == MagickFalse)
9536                 {
9537                   if (image_matte == MagickFalse)
9538                     {
9539                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9540                       image_matte=MagickFalse;
9541                     }
9542
9543                   else
9544                     {
9545                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9546                       image_matte=MagickTrue;
9547                     }
9548                 }
9549               else
9550                 {
9551                   if (image_matte == MagickFalse)
9552                     {
9553                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9554                       image_matte=MagickFalse;
9555                     }
9556
9557                   else
9558                     {
9559                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9560                       image_matte=MagickTrue;
9561                     }
9562                  }
9563             }
9564
9565         }
9566
9567       if (logging != MagickFalse)
9568          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9569          "    Selected PNG colortype=%d",ping_color_type);
9570
9571       if (ping_bit_depth < 8)
9572         {
9573           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9574               ping_color_type == PNG_COLOR_TYPE_RGB ||
9575               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9576             ping_bit_depth=8;
9577         }
9578
9579       old_bit_depth=ping_bit_depth;
9580
9581       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9582         {
9583           if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
9584              ping_bit_depth=1;
9585         }
9586
9587       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9588         {
9589            size_t one = 1;
9590            ping_bit_depth=1;
9591
9592            if (image->colors == 0)
9593            {
9594               /* DO SOMETHING */
9595                 png_error(ping,"image has 0 colors");
9596            }
9597
9598            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9599              ping_bit_depth <<= 1;
9600         }
9601
9602       if (logging != MagickFalse)
9603          {
9604            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9605             "    Number of colors: %.20g",(double) image_colors);
9606
9607            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9608             "    Tentative PNG bit depth: %d",ping_bit_depth);
9609          }
9610
9611       if (ping_bit_depth < (int) mng_info->write_png_depth)
9612          ping_bit_depth = mng_info->write_png_depth;
9613     }
9614
9615   image_depth=ping_bit_depth;
9616
9617   if (logging != MagickFalse)
9618     {
9619       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9620         "    Tentative PNG color type: %s (%.20g)",
9621         PngColorTypeToString(ping_color_type),
9622         (double) ping_color_type);
9623
9624       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9625         "    image_info->type: %.20g",(double) image_info->type);
9626
9627       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9628         "    image_depth: %.20g",(double) image_depth);
9629
9630       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631
9632         "    image->depth: %.20g",(double) image->depth);
9633
9634       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9635         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
9636     }
9637
9638   if (matte != MagickFalse)
9639     {
9640       if (mng_info->IsPalette)
9641         {
9642           if (mng_info->write_png_colortype == 0)
9643             {
9644               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9645
9646               if (ping_have_color != MagickFalse)
9647                  ping_color_type=PNG_COLOR_TYPE_RGBA;
9648             }
9649
9650           /*
9651            * Determine if there is any transparent color.
9652           */
9653           if (number_transparent + number_semitransparent == 0)
9654             {
9655               /*
9656                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
9657               */
9658
9659               image_matte=MagickFalse;
9660
9661               if (mng_info->write_png_colortype == 0)
9662                 ping_color_type&=0x03;
9663             }
9664
9665           else
9666             {
9667               unsigned int
9668                 mask;
9669
9670               mask=0xffff;
9671
9672               if (ping_bit_depth == 8)
9673                  mask=0x00ff;
9674
9675               if (ping_bit_depth == 4)
9676                  mask=0x000f;
9677
9678               if (ping_bit_depth == 2)
9679                  mask=0x0003;
9680
9681               if (ping_bit_depth == 1)
9682                  mask=0x0001;
9683
9684               ping_trans_color.red=(png_uint_16)
9685                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9686
9687               ping_trans_color.green=(png_uint_16)
9688                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9689
9690               ping_trans_color.blue=(png_uint_16)
9691                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9692
9693               ping_trans_color.gray=(png_uint_16)
9694                 (ScaleQuantumToShort(GetPixelInfoIntensity(
9695                    image->colormap)) & mask);
9696
9697               ping_trans_color.index=(png_byte) 0;
9698
9699               ping_have_tRNS=MagickTrue;
9700             }
9701
9702           if (ping_have_tRNS != MagickFalse)
9703             {
9704               /*
9705                * Determine if there is one and only one transparent color
9706                * and if so if it is fully transparent.
9707                */
9708               if (ping_have_cheap_transparency == MagickFalse)
9709                 ping_have_tRNS=MagickFalse;
9710             }
9711
9712           if (ping_have_tRNS != MagickFalse)
9713             {
9714               if (mng_info->write_png_colortype == 0)
9715                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
9716
9717               if (image_depth == 8)
9718                 {
9719                   ping_trans_color.red&=0xff;
9720                   ping_trans_color.green&=0xff;
9721                   ping_trans_color.blue&=0xff;
9722                   ping_trans_color.gray&=0xff;
9723                 }
9724             }
9725         }
9726       else
9727         {
9728           if (image_depth == 8)
9729             {
9730               ping_trans_color.red&=0xff;
9731               ping_trans_color.green&=0xff;
9732               ping_trans_color.blue&=0xff;
9733               ping_trans_color.gray&=0xff;
9734             }
9735         }
9736     }
9737
9738     matte=image_matte;
9739
9740     if (ping_have_tRNS != MagickFalse)
9741       image_matte=MagickFalse;
9742
9743     if ((mng_info->IsPalette) &&
9744         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9745         ping_have_color == MagickFalse &&
9746         (image_matte == MagickFalse || image_depth >= 8))
9747       {
9748         size_t one=1;
9749
9750         if (image_matte != MagickFalse)
9751           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9752
9753         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9754           {
9755             ping_color_type=PNG_COLOR_TYPE_GRAY;
9756
9757             if (save_image_depth == 16 && image_depth == 8)
9758               {
9759                 if (logging != MagickFalse)
9760                   {
9761                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9762                         "  Scaling ping_trans_color (0)");
9763                   }
9764                     ping_trans_color.gray*=0x0101;
9765               }
9766           }
9767
9768         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9769           image_depth=MAGICKCORE_QUANTUM_DEPTH;
9770
9771         if ((image_colors == 0) ||
9772              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9773           image_colors=(int) (one << image_depth);
9774
9775         if (image_depth > 8)
9776           ping_bit_depth=16;
9777
9778         else
9779           {
9780             ping_bit_depth=8;
9781             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9782               {
9783                 if(!mng_info->write_png_depth)
9784                   {
9785                     ping_bit_depth=1;
9786
9787                     while ((int) (one << ping_bit_depth)
9788                         < (ssize_t) image_colors)
9789                       ping_bit_depth <<= 1;
9790                   }
9791               }
9792
9793             else if (ping_color_type ==
9794                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9795                 mng_info->IsPalette)
9796               {
9797               /* Check if grayscale is reducible */
9798
9799                 int
9800                   depth_4_ok=MagickTrue,
9801                   depth_2_ok=MagickTrue,
9802                   depth_1_ok=MagickTrue;
9803
9804                 for (i=0; i < (ssize_t) image_colors; i++)
9805                 {
9806                    unsigned char
9807                      intensity;
9808
9809                    intensity=ScaleQuantumToChar(image->colormap[i].red);
9810
9811                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9812                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9813                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9814                      depth_2_ok=depth_1_ok=MagickFalse;
9815                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9816                      depth_1_ok=MagickFalse;
9817                 }
9818
9819                 if (depth_1_ok && mng_info->write_png_depth <= 1)
9820                   ping_bit_depth=1;
9821
9822                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9823                   ping_bit_depth=2;
9824
9825                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9826                   ping_bit_depth=4;
9827               }
9828           }
9829
9830           image_depth=ping_bit_depth;
9831       }
9832
9833     else
9834
9835       if (mng_info->IsPalette)
9836       {
9837         number_colors=image_colors;
9838
9839         if (image_depth <= 8)
9840           {
9841             /*
9842               Set image palette.
9843             */
9844             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9845
9846             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
9847               {
9848                 for (i=0; i < (ssize_t) number_colors; i++)
9849                 {
9850                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9851                   palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9852                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9853                 }
9854
9855                 if (logging != MagickFalse)
9856                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9857                     "  Setting up PLTE chunk with %d colors",
9858                     number_colors);
9859
9860                 ping_have_PLTE=MagickTrue;
9861               }
9862
9863             /* color_type is PNG_COLOR_TYPE_PALETTE */
9864             if (mng_info->write_png_depth == 0)
9865               {
9866                 size_t
9867                   one;
9868
9869                 ping_bit_depth=1;
9870                 one=1;
9871
9872                 while ((one << ping_bit_depth) < (size_t) number_colors)
9873                   ping_bit_depth <<= 1;
9874               }
9875
9876             ping_num_trans=0;
9877
9878             if (matte != MagickFalse)
9879               {
9880                 /*
9881                  * Set up trans_colors array.
9882                  */
9883                 assert(number_colors <= 256);
9884
9885                 ping_num_trans=(unsigned short) (number_transparent +
9886                   number_semitransparent);
9887
9888                 if (ping_num_trans == 0)
9889                   ping_have_tRNS=MagickFalse;
9890
9891                 else
9892                   {
9893                     if (logging != MagickFalse)
9894                       {
9895                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9896                           "  Scaling ping_trans_color (1)");
9897                       }
9898                     ping_have_tRNS=MagickTrue;
9899
9900                     for (i=0; i < ping_num_trans; i++)
9901                     {
9902                        ping_trans_alpha[i]= (png_byte)
9903                          ScaleQuantumToChar(image->colormap[i].alpha);
9904                     }
9905                   }
9906               }
9907           }
9908       }
9909
9910     else
9911       {
9912
9913         if (image_depth < 8)
9914           image_depth=8;
9915
9916         if ((save_image_depth == 16) && (image_depth == 8))
9917           {
9918             if (logging != MagickFalse)
9919               {
9920                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9921                   "    Scaling ping_trans_color from (%d,%d,%d)",
9922                   (int) ping_trans_color.red,
9923                   (int) ping_trans_color.green,
9924                   (int) ping_trans_color.blue);
9925               }
9926
9927             ping_trans_color.red*=0x0101;
9928             ping_trans_color.green*=0x0101;
9929             ping_trans_color.blue*=0x0101;
9930             ping_trans_color.gray*=0x0101;
9931
9932             if (logging != MagickFalse)
9933               {
9934                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9935                   "    to (%d,%d,%d)",
9936                   (int) ping_trans_color.red,
9937                   (int) ping_trans_color.green,
9938                   (int) ping_trans_color.blue);
9939               }
9940           }
9941       }
9942
9943     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
9944          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
9945
9946     /*
9947       Adjust background and transparency samples in sub-8-bit grayscale files.
9948     */
9949     if (ping_bit_depth < 8 && ping_color_type ==
9950         PNG_COLOR_TYPE_GRAY)
9951       {
9952          png_uint_16
9953            maxval;
9954
9955          size_t
9956            one=1;
9957
9958          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9959
9960          if (ping_exclude_bKGD == MagickFalse)
9961          {
9962
9963          ping_background.gray=(png_uint_16) ((maxval/65535.)*
9964            (ScaleQuantumToShort(((GetPixelInfoIntensity(
9965            &image->background_color))) +.5)));
9966
9967          if (logging != MagickFalse)
9968            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9969              "  Setting up bKGD chunk (2)");
9970          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9971              "      background_color index is %d",
9972              (int) ping_background.index);
9973
9974          ping_have_bKGD = MagickTrue;
9975          }
9976
9977          if (logging != MagickFalse)
9978            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9979              "  Scaling ping_trans_color.gray from %d",
9980              (int)ping_trans_color.gray);
9981
9982          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9983            ping_trans_color.gray)+.5);
9984
9985          if (logging != MagickFalse)
9986            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9987              "      to %d", (int)ping_trans_color.gray);
9988       }
9989
9990   if (ping_exclude_bKGD == MagickFalse)
9991   {
9992     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9993       {
9994         /*
9995            Identify which colormap entry is the background color.
9996         */
9997
9998         number_colors=image_colors;
9999
10000         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10001           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10002             break;
10003
10004         ping_background.index=(png_byte) i;
10005
10006         if (logging != MagickFalse)
10007           {
10008             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10009               "  Setting up bKGD chunk with index=%d",(int) i);
10010           }
10011
10012         if (i < (ssize_t) number_colors)
10013           {
10014             ping_have_bKGD = MagickTrue;
10015
10016             if (logging != MagickFalse)
10017               {
10018                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10019                   "     background   =(%d,%d,%d)",
10020                         (int) ping_background.red,
10021                         (int) ping_background.green,
10022                         (int) ping_background.blue);
10023               }
10024           }
10025
10026         else  /* Can't happen */
10027           {
10028             if (logging != MagickFalse)
10029               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10030                   "      No room in PLTE to add bKGD color");
10031             ping_have_bKGD = MagickFalse;
10032           }
10033       }
10034   }
10035
10036   if (logging != MagickFalse)
10037     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10038       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10039       ping_color_type);
10040   /*
10041     Initialize compression level and filtering.
10042   */
10043   if (logging != MagickFalse)
10044     {
10045       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10046         "  Setting up deflate compression");
10047
10048       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10049         "    Compression buffer size: 32768");
10050     }
10051
10052   png_set_compression_buffer_size(ping,32768L);
10053
10054   if (logging != MagickFalse)
10055     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10056       "    Compression mem level: 9");
10057
10058   png_set_compression_mem_level(ping, 9);
10059
10060   /* Untangle the "-quality" setting:
10061
10062      Undefined is 0; the default is used.
10063      Default is 75
10064
10065      10's digit:
10066
10067         0: Use Z_HUFFMAN_ONLY strategy with the
10068            zlib default compression level
10069
10070         1-9: the zlib compression level
10071
10072      1's digit:
10073
10074         0-4: the PNG filter method
10075
10076         5:   libpng adaptive filtering if compression level > 5
10077              libpng filter type "none" if compression level <= 5
10078                 or if image is grayscale or palette
10079
10080         6:   libpng adaptive filtering
10081
10082         7:   "LOCO" filtering (intrapixel differing) if writing
10083              a MNG, othewise "none".  Did not work in IM-6.7.0-9
10084              and earlier because of a missing "else".
10085
10086         8:   Z_RLE strategy, all filters
10087              Unused prior to IM-6.7.0-10, was same as 6
10088
10089         9:   Z_RLE strategy, no PNG filters
10090              Unused prior to IM-6.7.0-10, was same as 6
10091
10092     Note that using the -quality option, not all combinations of
10093     PNG filter type, zlib compression level, and zlib compression
10094     strategy are possible.  This will be addressed soon in a
10095     release that accomodates "-define png:compression-strategy", etc.
10096
10097    */
10098
10099   quality=image->quality == UndefinedCompressionQuality ? 75UL :
10100      image->quality;
10101
10102   if (quality <= 9)
10103     {
10104       if (mng_info->write_png_compression_strategy == 0)
10105         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10106     }
10107
10108   else if (mng_info->write_png_compression_level == 0)
10109     {
10110       int
10111         level;
10112
10113       level=(int) MagickMin((ssize_t) quality/10,9);
10114
10115       mng_info->write_png_compression_level = level+1;
10116     }
10117
10118   if (mng_info->write_png_compression_strategy == 0)
10119     {
10120         if ((quality %10) == 8 || (quality %10) == 9)
10121 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10122           mng_info->write_png_compression_strategy=Z_RLE+1;
10123 #else
10124           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10125 #endif
10126     }
10127
10128   if (mng_info->write_png_compression_filter == 0)
10129         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10130
10131   if (logging != MagickFalse)
10132     {
10133      if (mng_info->write_png_compression_level)
10134         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10135           "    Compression level:    %d",
10136             (int) mng_info->write_png_compression_level-1);
10137
10138      if (mng_info->write_png_compression_strategy)
10139         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10140           "    Compression strategy: %d",
10141             (int) mng_info->write_png_compression_strategy-1);
10142
10143         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10144           "  Setting up filtering");
10145
10146         if (mng_info->write_png_compression_filter == 6)
10147           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10148             "    Base filter method: ADAPTIVE");
10149         else if (mng_info->write_png_compression_filter == 0 ||
10150                  mng_info->write_png_compression_filter == 1)
10151           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10152             "    Base filter method: NONE");
10153         else
10154           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10155             "    Base filter method: %d",
10156             (int) mng_info->write_png_compression_filter-1);
10157     }
10158
10159   if (mng_info->write_png_compression_level != 0)
10160     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10161
10162   if (mng_info->write_png_compression_filter == 6)
10163     {
10164       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10165          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10166          (quality < 50))
10167         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10168       else
10169         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10170      }
10171   else if (mng_info->write_png_compression_filter == 7 ||
10172       mng_info->write_png_compression_filter == 10)
10173     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10174
10175   else if (mng_info->write_png_compression_filter == 8)
10176     {
10177 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10178       if (mng_info->write_mng)
10179       {
10180          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10181              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10182         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10183       }
10184 #endif
10185       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10186     }
10187
10188   else if (mng_info->write_png_compression_filter == 9)
10189     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10190
10191   else if (mng_info->write_png_compression_filter != 0)
10192     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10193        mng_info->write_png_compression_filter-1);
10194
10195   if (mng_info->write_png_compression_strategy != 0)
10196     png_set_compression_strategy(ping,
10197        mng_info->write_png_compression_strategy-1);
10198
10199   ping_interlace_method=image_info->interlace != NoInterlace;
10200
10201   if (mng_info->write_mng)
10202     png_set_sig_bytes(ping,8);
10203
10204   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10205
10206   if (mng_info->write_png_colortype != 0)
10207     {
10208      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10209        if (ping_have_color != MagickFalse)
10210          {
10211            ping_color_type = PNG_COLOR_TYPE_RGB;
10212
10213            if (ping_bit_depth < 8)
10214              ping_bit_depth=8;
10215          }
10216
10217      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10218        if (ping_have_color != MagickFalse)
10219          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10220     }
10221
10222   if (ping_need_colortype_warning != MagickFalse ||
10223      ((mng_info->write_png_depth &&
10224      (int) mng_info->write_png_depth != ping_bit_depth) ||
10225      (mng_info->write_png_colortype &&
10226      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10227       mng_info->write_png_colortype != 7 &&
10228       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10229     {
10230       if (logging != MagickFalse)
10231         {
10232           if (ping_need_colortype_warning != MagickFalse)
10233             {
10234               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10235                  "  Image has transparency but tRNS chunk was excluded");
10236             }
10237
10238           if (mng_info->write_png_depth)
10239             {
10240               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10241                   "  Defined png:bit-depth=%u, Computed depth=%u",
10242                   mng_info->write_png_depth,
10243                   ping_bit_depth);
10244             }
10245
10246           if (mng_info->write_png_colortype)
10247             {
10248               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10249                   "  Defined png:color-type=%u, Computed color type=%u",
10250                   mng_info->write_png_colortype-1,
10251                   ping_color_type);
10252             }
10253         }
10254
10255       png_warning(ping,
10256         "Cannot write image with defined png:bit-depth or png:color-type.");
10257     }
10258
10259   if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10260     {
10261       /* Add an opaque matte channel */
10262       image->alpha_trait = BlendPixelTrait;
10263       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10264
10265       if (logging != MagickFalse)
10266         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10267           "  Added an opaque matte channel");
10268     }
10269
10270   if (number_transparent != 0 || number_semitransparent != 0)
10271     {
10272       if (ping_color_type < 4)
10273         {
10274            ping_have_tRNS=MagickTrue;
10275            if (logging != MagickFalse)
10276              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10277                "  Setting ping_have_tRNS=MagickTrue.");
10278         }
10279     }
10280
10281   if (logging != MagickFalse)
10282     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10283       "  Writing PNG header chunks");
10284
10285   png_set_IHDR(ping,ping_info,ping_width,ping_height,
10286                ping_bit_depth,ping_color_type,
10287                ping_interlace_method,ping_compression_method,
10288                ping_filter_method);
10289
10290   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10291     {
10292       png_set_PLTE(ping,ping_info,palette,number_colors);
10293
10294       if (logging != MagickFalse)
10295         {
10296           for (i=0; i< (ssize_t) number_colors; i++)
10297           {
10298             if (i < ping_num_trans)
10299               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10300                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10301                       (int) i,
10302                       (int) palette[i].red,
10303                       (int) palette[i].green,
10304                       (int) palette[i].blue,
10305                       (int) i,
10306                       (int) ping_trans_alpha[i]);
10307              else
10308               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10309                 "     PLTE[%d] = (%d,%d,%d)",
10310                       (int) i,
10311                       (int) palette[i].red,
10312                       (int) palette[i].green,
10313                       (int) palette[i].blue);
10314            }
10315          }
10316     }
10317
10318   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10319   if (ping_exclude_sRGB != MagickFalse ||
10320      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10321   {
10322     if ((ping_exclude_tEXt == MagickFalse ||
10323        ping_exclude_zTXt == MagickFalse) &&
10324        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10325     {
10326       ResetImageProfileIterator(image);
10327       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10328       {
10329         profile=GetImageProfile(image,name);
10330
10331         if (profile != (StringInfo *) NULL)
10332           {
10333 #ifdef PNG_WRITE_iCCP_SUPPORTED
10334             if ((LocaleCompare(name,"ICC") == 0) ||
10335                 (LocaleCompare(name,"ICM") == 0))
10336              {
10337
10338                if (ping_exclude_iCCP == MagickFalse)
10339                  {
10340                        png_set_iCCP(ping,ping_info,(png_charp) name,0,
10341 #if (PNG_LIBPNG_VER < 10500)
10342                          (png_charp) GetStringInfoDatum(profile),
10343 #else
10344                          (png_const_bytep) GetStringInfoDatum(profile),
10345 #endif
10346                          (png_uint_32) GetStringInfoLength(profile));
10347                  }
10348              }
10349
10350             else
10351 #endif
10352               if (ping_exclude_zCCP == MagickFalse)
10353                 {
10354                   Magick_png_write_raw_profile(image_info,ping,ping_info,
10355                     (unsigned char *) name,(unsigned char *) name,
10356                     GetStringInfoDatum(profile),
10357                     (png_uint_32) GetStringInfoLength(profile));
10358                 }
10359           }
10360
10361           if (logging != MagickFalse)
10362             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10363               "  Setting up text chunk with %s profile",name);
10364
10365         name=GetNextImageProfile(image);
10366       }
10367     }
10368   }
10369
10370 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10371   if ((mng_info->have_write_global_srgb == 0) &&
10372       (png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10373     {
10374       if (ping_exclude_sRGB == MagickFalse)
10375         {
10376           /*
10377             Note image rendering intent.
10378           */
10379           if (logging != MagickFalse)
10380             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10381                 "  Setting up sRGB chunk");
10382
10383           (void) png_set_sRGB(ping,ping_info,(
10384             Magick_RenderingIntent_to_PNG_RenderingIntent(
10385               image->rendering_intent)));
10386         }
10387     }
10388
10389   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10390 #endif
10391     {
10392       if (ping_exclude_gAMA == MagickFalse &&
10393           (ping_exclude_sRGB == MagickFalse ||
10394           (image->gamma < .45 || image->gamma > .46)))
10395       {
10396       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10397         {
10398           /*
10399             Note image gamma.
10400             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10401           */
10402           if (logging != MagickFalse)
10403             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10404               "  Setting up gAMA chunk");
10405
10406           png_set_gAMA(ping,ping_info,image->gamma);
10407         }
10408       }
10409
10410       if (ping_exclude_cHRM == MagickFalse)
10411         {
10412           if ((mng_info->have_write_global_chrm == 0) &&
10413               (image->chromaticity.red_primary.x != 0.0))
10414             {
10415               /*
10416                 Note image chromaticity.
10417                 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10418               */
10419                PrimaryInfo
10420                  bp,
10421                  gp,
10422                  rp,
10423                  wp;
10424
10425                wp=image->chromaticity.white_point;
10426                rp=image->chromaticity.red_primary;
10427                gp=image->chromaticity.green_primary;
10428                bp=image->chromaticity.blue_primary;
10429
10430                if (logging != MagickFalse)
10431                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10432                    "  Setting up cHRM chunk");
10433
10434                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10435                    bp.x,bp.y);
10436            }
10437         }
10438     }
10439
10440   if (ping_exclude_bKGD == MagickFalse)
10441     {
10442       if (ping_have_bKGD != MagickFalse)
10443         {
10444           png_set_bKGD(ping,ping_info,&ping_background);
10445           if (logging)
10446             {
10447               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10448                    "    Setting up bKGD chunk");
10449               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10450                    "      background color = (%d,%d,%d)",
10451                         (int) ping_background.red,
10452                         (int) ping_background.green,
10453                         (int) ping_background.blue);
10454               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10455                    "      index = %d, gray=%d",
10456                         (int) ping_background.index,
10457                         (int) ping_background.gray);
10458             }
10459          }
10460     }
10461
10462   if (ping_exclude_pHYs == MagickFalse)
10463     {
10464       if (ping_have_pHYs != MagickFalse)
10465         {
10466           png_set_pHYs(ping,ping_info,
10467              ping_pHYs_x_resolution,
10468              ping_pHYs_y_resolution,
10469              ping_pHYs_unit_type);
10470
10471           if (logging)
10472             {
10473               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10474                    "    Setting up pHYs chunk");
10475               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10476                    "      x_resolution=%lu",
10477                    (unsigned long) ping_pHYs_x_resolution);
10478               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10479                    "      y_resolution=%lu",
10480                    (unsigned long) ping_pHYs_y_resolution);
10481               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10482                    "      unit_type=%lu",
10483                    (unsigned long) ping_pHYs_unit_type);
10484             }
10485         }
10486     }
10487
10488 #if defined(PNG_oFFs_SUPPORTED)
10489   if (ping_exclude_oFFs == MagickFalse)
10490     {
10491       if (image->page.x || image->page.y)
10492         {
10493            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10494               (png_int_32) image->page.y, 0);
10495
10496            if (logging != MagickFalse)
10497              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10498                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
10499                  (int) image->page.x, (int) image->page.y);
10500         }
10501     }
10502 #endif
10503
10504   if (mng_info->need_blob != MagickFalse)
10505   {
10506     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10507        MagickFalse)
10508        png_error(ping,"WriteBlob Failed");
10509
10510      ping_have_blob=MagickTrue;
10511   }
10512
10513   png_write_info_before_PLTE(ping, ping_info);
10514
10515   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10516     {
10517       if (logging != MagickFalse)
10518         {
10519           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10520               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10521         }
10522
10523       if (ping_color_type == 3)
10524          (void) png_set_tRNS(ping, ping_info,
10525                 ping_trans_alpha,
10526                 ping_num_trans,
10527                 NULL);
10528
10529       else
10530         {
10531            (void) png_set_tRNS(ping, ping_info,
10532                   NULL,
10533                   0,
10534                   &ping_trans_color);
10535
10536            if (logging != MagickFalse)
10537              {
10538                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10539                  "     tRNS color   =(%d,%d,%d)",
10540                        (int) ping_trans_color.red,
10541                        (int) ping_trans_color.green,
10542                        (int) ping_trans_color.blue);
10543              }
10544          }
10545     }
10546
10547   /* write any png-chunk-b profiles */
10548   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10549
10550   png_write_info(ping,ping_info);
10551
10552   /* write any PNG-chunk-m profiles */
10553   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10554
10555   if (ping_exclude_vpAg == MagickFalse)
10556     {
10557       if ((image->page.width != 0 && image->page.width != image->columns) ||
10558           (image->page.height != 0 && image->page.height != image->rows))
10559         {
10560           unsigned char
10561             chunk[14];
10562
10563           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
10564           PNGType(chunk,mng_vpAg);
10565           LogPNGChunk(logging,mng_vpAg,9L);
10566           PNGLong(chunk+4,(png_uint_32) image->page.width);
10567           PNGLong(chunk+8,(png_uint_32) image->page.height);
10568           chunk[12]=0;   /* unit = pixels */
10569           (void) WriteBlob(image,13,chunk);
10570           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10571         }
10572     }
10573
10574 #if (PNG_LIBPNG_VER == 10206)
10575     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10576 #define PNG_HAVE_IDAT               0x04
10577     ping->mode |= PNG_HAVE_IDAT;
10578 #undef PNG_HAVE_IDAT
10579 #endif
10580
10581   png_set_packing(ping);
10582   /*
10583     Allocate memory.
10584   */
10585   rowbytes=image->columns;
10586   if (image_depth > 8)
10587     rowbytes*=2;
10588   switch (ping_color_type)
10589     {
10590       case PNG_COLOR_TYPE_RGB:
10591         rowbytes*=3;
10592         break;
10593
10594       case PNG_COLOR_TYPE_GRAY_ALPHA:
10595         rowbytes*=2;
10596         break;
10597
10598       case PNG_COLOR_TYPE_RGBA:
10599         rowbytes*=4;
10600         break;
10601
10602       default:
10603         break;
10604     }
10605
10606   if (logging != MagickFalse)
10607     {
10608       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10609         "  Writing PNG image data");
10610
10611       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10612         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10613     }
10614   ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10615     sizeof(*ping_pixels));
10616
10617   if (ping_pixels == (unsigned char *) NULL)
10618     png_error(ping,"Allocation of memory for pixels failed");
10619
10620   /*
10621     Initialize image scanlines.
10622   */
10623   quantum_info=AcquireQuantumInfo(image_info,image);
10624   if (quantum_info == (QuantumInfo *) NULL)
10625     png_error(ping,"Memory allocation for quantum_info failed");
10626   quantum_info->format=UndefinedQuantumFormat;
10627   quantum_info->depth=image_depth;
10628   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10629   num_passes=png_set_interlace_handling(ping);
10630
10631   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10632        !mng_info->write_png48 && !mng_info->write_png64 &&
10633        !mng_info->write_png32) &&
10634        (mng_info->IsPalette ||
10635        (image_info->type == BilevelType)) &&
10636        image_matte == MagickFalse &&
10637        ping_have_non_bw == MagickFalse)
10638     {
10639       /* Palette, Bilevel, or Opaque Monochrome */
10640       register const Quantum
10641         *p;
10642
10643       quantum_info->depth=8;
10644       for (pass=0; pass < num_passes; pass++)
10645       {
10646         /*
10647           Convert PseudoClass image to a PNG monochrome image.
10648         */
10649         for (y=0; y < (ssize_t) image->rows; y++)
10650         {
10651           if (logging != MagickFalse && y == 0)
10652              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10653                  "    Writing row of pixels (0)");
10654
10655           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10656
10657           if (p == (const Quantum *) NULL)
10658             break;
10659
10660           if (mng_info->IsPalette)
10661             {
10662               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10663                 quantum_info,GrayQuantum,ping_pixels,exception);
10664               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10665                   mng_info->write_png_depth &&
10666                   mng_info->write_png_depth != old_bit_depth)
10667                 {
10668                   /* Undo pixel scaling */
10669                   for (i=0; i < (ssize_t) image->columns; i++)
10670                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10671                      >> (8-old_bit_depth));
10672                 }
10673             }
10674
10675           else
10676             {
10677               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10678                 quantum_info,RedQuantum,ping_pixels,exception);
10679             }
10680
10681           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10682             for (i=0; i < (ssize_t) image->columns; i++)
10683                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10684                       255 : 0);
10685
10686           if (logging != MagickFalse && y == 0)
10687             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10688                 "    Writing row of pixels (1)");
10689
10690           png_write_row(ping,ping_pixels);
10691         }
10692         if (image->previous == (Image *) NULL)
10693           {
10694             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10695             if (status == MagickFalse)
10696               break;
10697           }
10698       }
10699     }
10700
10701   else   /* Not Palette, Bilevel, or Opaque Monochrome */
10702     {
10703       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10704           !mng_info->write_png48 && !mng_info->write_png64 &&
10705           !mng_info->write_png32) && (image_matte != MagickFalse ||
10706           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10707           (mng_info->IsPalette) && ping_have_color == MagickFalse)
10708         {
10709           register const Quantum
10710             *p;
10711
10712           for (pass=0; pass < num_passes; pass++)
10713           {
10714
10715           for (y=0; y < (ssize_t) image->rows; y++)
10716           {
10717             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10718
10719             if (p == (const Quantum *) NULL)
10720               break;
10721
10722             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10723               {
10724                 if (mng_info->IsPalette)
10725                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10726                     quantum_info,GrayQuantum,ping_pixels,exception);
10727
10728                 else
10729                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10730                     quantum_info,RedQuantum,ping_pixels,exception);
10731
10732                 if (logging != MagickFalse && y == 0)
10733                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10734                        "    Writing GRAY PNG pixels (2)");
10735               }
10736
10737             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10738               {
10739                 if (logging != MagickFalse && y == 0)
10740                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10741                          "    Writing GRAY_ALPHA PNG pixels (2)");
10742
10743                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10744                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10745               }
10746
10747             if (logging != MagickFalse && y == 0)
10748               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10749                   "    Writing row of pixels (2)");
10750
10751             png_write_row(ping,ping_pixels);
10752           }
10753
10754           if (image->previous == (Image *) NULL)
10755             {
10756               status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10757               if (status == MagickFalse)
10758                 break;
10759             }
10760           }
10761         }
10762
10763       else
10764         {
10765           register const Quantum
10766             *p;
10767
10768           for (pass=0; pass < num_passes; pass++)
10769           {
10770             if ((image_depth > 8) ||
10771                 mng_info->write_png24 ||
10772                 mng_info->write_png32 ||
10773                 mng_info->write_png48 ||
10774                 mng_info->write_png64 ||
10775                 (!mng_info->write_png8 && !mng_info->IsPalette))
10776             {
10777               for (y=0; y < (ssize_t) image->rows; y++)
10778               {
10779                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10780
10781                 if (p == (const Quantum *) NULL)
10782                   break;
10783
10784                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10785                   {
10786                     if (image->storage_class == DirectClass)
10787                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
10788                         quantum_info,RedQuantum,ping_pixels,exception);
10789
10790                     else
10791                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
10792                         quantum_info,GrayQuantum,ping_pixels,exception);
10793                   }
10794
10795                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10796                   {
10797                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
10798                       quantum_info,GrayAlphaQuantum,ping_pixels,
10799                       exception);
10800
10801                     if (logging != MagickFalse && y == 0)
10802                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10803                            "    Writing GRAY_ALPHA PNG pixels (3)");
10804                   }
10805
10806                 else if (image_matte != MagickFalse)
10807                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10808                     quantum_info,RGBAQuantum,ping_pixels,exception);
10809
10810                 else
10811                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10812                     quantum_info,RGBQuantum,ping_pixels,exception);
10813
10814                 if (logging != MagickFalse && y == 0)
10815                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10816                       "    Writing row of pixels (3)");
10817
10818                 png_write_row(ping,ping_pixels);
10819               }
10820             }
10821
10822           else
10823             /* not ((image_depth > 8) ||
10824                 mng_info->write_png24 || mng_info->write_png32 ||
10825                 mng_info->write_png48 || mng_info->write_png64 ||
10826                 (!mng_info->write_png8 && !mng_info->IsPalette))
10827              */
10828             {
10829               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10830                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10831                 {
10832                   if (logging != MagickFalse)
10833                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10834                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10835
10836                   quantum_info->depth=8;
10837                   image_depth=8;
10838                 }
10839
10840               for (y=0; y < (ssize_t) image->rows; y++)
10841               {
10842                 if (logging != MagickFalse && y == 0)
10843                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10844                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10845
10846                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10847
10848                 if (p == (const Quantum *) NULL)
10849                   break;
10850
10851                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10852                   {
10853                     quantum_info->depth=image->depth;
10854
10855                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
10856                        quantum_info,GrayQuantum,ping_pixels,exception);
10857                   }
10858
10859                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10860                   {
10861                     if (logging != MagickFalse && y == 0)
10862                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10863                            "  Writing GRAY_ALPHA PNG pixels (4)");
10864
10865                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
10866                          quantum_info,GrayAlphaQuantum,ping_pixels,
10867                          exception);
10868                   }
10869
10870                 else
10871                   {
10872                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
10873                       quantum_info,IndexQuantum,ping_pixels,exception);
10874
10875                     if (logging != MagickFalse && y <= 2)
10876                     {
10877                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10878                           "  Writing row of non-gray pixels (4)");
10879
10880                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10881                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
10882                           (int)ping_pixels[0],(int)ping_pixels[1]);
10883                     }
10884                   }
10885                 png_write_row(ping,ping_pixels);
10886               }
10887             }
10888
10889             if (image->previous == (Image *) NULL)
10890               {
10891                 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10892                 if (status == MagickFalse)
10893                   break;
10894               }
10895           }
10896         }
10897     }
10898
10899   if (quantum_info != (QuantumInfo *) NULL)
10900     quantum_info=DestroyQuantumInfo(quantum_info);
10901
10902   if (logging != MagickFalse)
10903     {
10904       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10905         "  Wrote PNG image data");
10906
10907       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10908         "    Width: %.20g",(double) ping_width);
10909
10910       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10911         "    Height: %.20g",(double) ping_height);
10912
10913       if (mng_info->write_png_depth)
10914         {
10915           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10916             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
10917         }
10918
10919       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10920         "    PNG bit-depth written: %d",ping_bit_depth);
10921
10922       if (mng_info->write_png_colortype)
10923         {
10924           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10925             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
10926         }
10927
10928       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10929         "    PNG color-type written: %d",ping_color_type);
10930
10931       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10932         "    PNG Interlace method: %d",ping_interlace_method);
10933     }
10934   /*
10935     Generate text chunks after IDAT.
10936   */
10937   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10938   {
10939     ResetImagePropertyIterator(image);
10940     property=GetNextImageProperty(image);
10941     while (property != (const char *) NULL)
10942     {
10943       png_textp
10944         text;
10945
10946       value=GetImageProperty(image,property,exception);
10947
10948       /* Don't write any "png:" properties; those are just for "identify" */
10949       if (LocaleNCompare(property,"png:",4) != 0 &&
10950
10951           /* Suppress density and units if we wrote a pHYs chunk */
10952           (ping_exclude_pHYs != MagickFalse      ||
10953           LocaleCompare(property,"density") != 0 ||
10954           LocaleCompare(property,"units") != 0) &&
10955
10956           /* Suppress the IM-generated Date:create and Date:modify */
10957           (ping_exclude_date == MagickFalse      ||
10958           LocaleNCompare(property, "Date:",5) != 0))
10959         {
10960         if (value != (const char *) NULL)
10961           {
10962
10963 #if PNG_LIBPNG_VER >= 14000
10964             text=(png_textp) png_malloc(ping,
10965                  (png_alloc_size_t) sizeof(png_text));
10966 #else
10967             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
10968 #endif
10969             text[0].key=(char *) property;
10970             text[0].text=(char *) value;
10971             text[0].text_length=strlen(value);
10972
10973             if (ping_exclude_tEXt != MagickFalse)
10974                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10975
10976             else if (ping_exclude_zTXt != MagickFalse)
10977                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10978
10979             else
10980             {
10981                text[0].compression=image_info->compression == NoCompression ||
10982                  (image_info->compression == UndefinedCompression &&
10983                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10984                  PNG_TEXT_COMPRESSION_zTXt ;
10985             }
10986
10987             if (logging != MagickFalse)
10988               {
10989                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10990                   "  Setting up text chunk");
10991
10992                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10993                   "    keyword: '%s'",text[0].key);
10994               }
10995
10996             png_set_text(ping,ping_info,text,1);
10997             png_free(ping,text);
10998           }
10999         }
11000       property=GetNextImageProperty(image);
11001     }
11002   }
11003
11004   /* write any PNG-chunk-e profiles */
11005   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11006
11007   if (logging != MagickFalse)
11008     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11009       "  Writing PNG end info");
11010
11011   png_write_end(ping,ping_info);
11012
11013   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11014     {
11015       if (mng_info->page.x || mng_info->page.y ||
11016           (ping_width != mng_info->page.width) ||
11017           (ping_height != mng_info->page.height))
11018         {
11019           unsigned char
11020             chunk[32];
11021
11022           /*
11023             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11024           */
11025           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11026           PNGType(chunk,mng_FRAM);
11027           LogPNGChunk(logging,mng_FRAM,27L);
11028           chunk[4]=4;
11029           chunk[5]=0;  /* frame name separator (no name) */
11030           chunk[6]=1;  /* flag for changing delay, for next frame only */
11031           chunk[7]=0;  /* flag for changing frame timeout */
11032           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11033           chunk[9]=0;  /* flag for changing frame sync_id */
11034           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11035           chunk[14]=0; /* clipping boundaries delta type */
11036           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11037           PNGLong(chunk+19,
11038              (png_uint_32) (mng_info->page.x + ping_width));
11039           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11040           PNGLong(chunk+27,
11041              (png_uint_32) (mng_info->page.y + ping_height));
11042           (void) WriteBlob(image,31,chunk);
11043           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11044           mng_info->old_framing_mode=4;
11045           mng_info->framing_mode=1;
11046         }
11047
11048       else
11049         mng_info->framing_mode=3;
11050     }
11051   if (mng_info->write_mng && !mng_info->need_fram &&
11052       ((int) image->dispose == 3))
11053      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11054
11055   /*
11056     Free PNG resources.
11057   */
11058
11059   png_destroy_write_struct(&ping,&ping_info);
11060
11061   ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
11062
11063   if (ping_have_blob != MagickFalse)
11064      (void) CloseBlob(image);
11065
11066   image_info=DestroyImageInfo(image_info);
11067   image=DestroyImage(image);
11068
11069   /* Store bit depth actually written */
11070   s[0]=(char) ping_bit_depth;
11071   s[1]='\0';
11072
11073   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11074
11075   if (logging != MagickFalse)
11076     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11077       "  exit WriteOnePNGImage()");
11078
11079 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
11080   UnlockSemaphoreInfo(ping_semaphore);
11081 #endif
11082
11083    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11084     *    Throwing an Exception when an error occurs.
11085     */
11086
11087   return(MagickTrue);
11088 /*  End write one PNG image */
11089
11090 }
11091
11092 /*
11093 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11094 %                                                                             %
11095 %                                                                             %
11096 %                                                                             %
11097 %   W r i t e P N G I m a g e                                                 %
11098 %                                                                             %
11099 %                                                                             %
11100 %                                                                             %
11101 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11102 %
11103 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11104 %  Multiple-image Network Graphics (MNG) image file.
11105 %
11106 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11107 %
11108 %  The format of the WritePNGImage method is:
11109 %
11110 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11111 %        Image *image,ExceptionInfo *exception)
11112 %
11113 %  A description of each parameter follows:
11114 %
11115 %    o image_info: the image info.
11116 %
11117 %    o image:  The image.
11118 %
11119 %    o exception: return any errors or warnings in this structure.
11120 %
11121 %  Returns MagickTrue on success, MagickFalse on failure.
11122 %
11123 %  Communicating with the PNG encoder:
11124 %
11125 %  While the datastream written is always in PNG format and normally would
11126 %  be given the "png" file extension, this method also writes the following
11127 %  pseudo-formats which are subsets of png:
11128 %
11129 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11130 %               a depth greater than 8, the depth is reduced. If transparency
11131 %               is present, the tRNS chunk must only have values 0 and 255
11132 %               (i.e., transparency is binary: fully opaque or fully
11133 %               transparent).  If other values are present they will be
11134 %               50%-thresholded to binary transparency.  If more than 256
11135 %               colors are present, they will be quantized to the 4-4-4-1,
11136 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11137 %               of any resulting fully-transparent pixels is changed to
11138 %               the image's background color.
11139 %
11140 %               If you want better quantization or dithering of the colors
11141 %               or alpha than that, you need to do it before calling the
11142 %               PNG encoder. The pixels contain 8-bit indices even if
11143 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11144 %               images will be written as indexed PNG files even though the
11145 %               PNG grayscale type might be slightly more efficient.  Please
11146 %               note that writing to the PNG8 format may result in loss
11147 %               of color and alpha data.
11148 %
11149 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11150 %               chunk can be present to convey binary transparency by naming
11151 %               one of the colors as transparent.  The only loss incurred
11152 %               is reduction of sample depth to 8.  If the image has more
11153 %               than one transparent color, has semitransparent pixels, or
11154 %               has an opaque pixel with the same RGB components as the
11155 %               transparent color, an image is not written.
11156 %
11157 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
11158 %               transparency is permitted, i.e., the alpha sample for
11159 %               each pixel can have any value from 0 to 255. The alpha
11160 %               channel is present even if the image is fully opaque.
11161 %               The only loss in data is the reduction of the sample depth
11162 %               to 8.
11163 %
11164 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
11165 %               chunk can be present to convey binary transparency by naming
11166 %               one of the colors as transparent.  If the image has more
11167 %               than one transparent color, has semitransparent pixels, or
11168 %               has an opaque pixel with the same RGB components as the
11169 %               transparent color, an image is not written.
11170 %
11171 %    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
11172 %               transparency is permitted, i.e., the alpha sample for
11173 %               each pixel can have any value from 0 to 65535. The alpha
11174 %               channel is present even if the image is fully opaque.
11175 %
11176 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
11177 %               image, if the input was a PNG, is written.  If these values
11178 %               cannot be found, then "PNG00" falls back to the regular "PNG"
11179 %               format.
11180 %
11181 %    o -define: For more precise control of the PNG output, you can use the
11182 %               Image options "png:bit-depth" and "png:color-type".  These
11183 %               can be set from the commandline with "-define" and also
11184 %               from the application programming interfaces.  The options
11185 %               are case-independent and are converted to lowercase before
11186 %               being passed to this encoder.
11187 %
11188 %               png:color-type can be 0, 2, 3, 4, or 6.
11189 %
11190 %               When png:color-type is 0 (Grayscale), png:bit-depth can
11191 %               be 1, 2, 4, 8, or 16.
11192 %
11193 %               When png:color-type is 2 (RGB), png:bit-depth can
11194 %               be 8 or 16.
11195 %
11196 %               When png:color-type is 3 (Indexed), png:bit-depth can
11197 %               be 1, 2, 4, or 8.  This refers to the number of bits
11198 %               used to store the index.  The color samples always have
11199 %               bit-depth 8 in indexed PNG files.
11200 %
11201 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11202 %               png:bit-depth can be 8 or 16.
11203 %
11204 %               If the image cannot be written without loss with the
11205 %               requested bit-depth and color-type, a PNG file will not
11206 %               be written, a warning will be issued, and the encoder will
11207 %               return MagickFalse.
11208 %
11209 %  Since image encoders should not be responsible for the "heavy lifting",
11210 %  the user should make sure that ImageMagick has already reduced the
11211 %  image depth and number of colors and limit transparency to binary
11212 %  transparency prior to attempting to write the image with depth, color,
11213 %  or transparency limitations.
11214 %
11215 %  Note that another definition, "png:bit-depth-written" exists, but it
11216 %  is not intended for external use.  It is only used internally by the
11217 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11218 %
11219 %  It is possible to request that the PNG encoder write previously-formatted
11220 %  ancillary chunks in the output PNG file, using the "-profile" commandline
11221 %  option as shown below or by setting the profile via a programming
11222 %  interface:
11223 %
11224 %     -profile PNG-chunk-x:<file>
11225 %
11226 %  where x is a location flag and <file> is a file containing the chunk
11227 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11228 %  This encoder will compute the chunk length and CRC, so those must not
11229 %  be included in the file.
11230 %
11231 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11232 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
11233 %  of the same type, then add a short unique string after the "x" to prevent
11234 %  subsequent profiles from overwriting the preceding ones, e.g.,
11235 %
11236 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11237 %
11238 %  As of version 6.6.6 the following optimizations are always done:
11239 %
11240 %   o  32-bit depth is reduced to 16.
11241 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
11242 %      high byte and low byte are identical.
11243 %   o  Palette is sorted to remove unused entries and to put a
11244 %      transparent color first, if BUILD_PNG_PALETTE is defined.
11245 %   o  Opaque matte channel is removed when writing an indexed PNG.
11246 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
11247 %      this can be done without loss and a larger bit depth N was not
11248 %      requested via the "-define png:bit-depth=N" option.
11249 %   o  If matte channel is present but only one transparent color is
11250 %      present, RGB+tRNS is written instead of RGBA
11251 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
11252 %      was requested when converting an opaque image).
11253 %
11254 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11255 */
11256 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11257   Image *image,ExceptionInfo *exception)
11258 {
11259   MagickBooleanType
11260     excluding,
11261     logging,
11262     have_mng_structure,
11263     status;
11264
11265   MngInfo
11266     *mng_info;
11267
11268   const char
11269     *value;
11270
11271   int
11272     i,
11273     source;
11274
11275   /*
11276     Open image file.
11277   */
11278   assert(image_info != (const ImageInfo *) NULL);
11279   assert(image_info->signature == MagickSignature);
11280   assert(image != (Image *) NULL);
11281   assert(image->signature == MagickSignature);
11282   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11283   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11284   /*
11285     Allocate a MngInfo structure.
11286   */
11287   have_mng_structure=MagickFalse;
11288   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11289
11290   if (mng_info == (MngInfo *) NULL)
11291     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11292
11293   /*
11294     Initialize members of the MngInfo structure.
11295   */
11296   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11297   mng_info->image=image;
11298   mng_info->equal_backgrounds=MagickTrue;
11299   have_mng_structure=MagickTrue;
11300
11301   /* See if user has requested a specific PNG subformat */
11302
11303   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11304   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11305   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11306   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11307   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11308
11309   value=GetImageOption(image_info,"png:format");
11310
11311   if (value != (char *) NULL)
11312     {
11313       mng_info->write_png8 = MagickFalse;
11314       mng_info->write_png24 = MagickFalse;
11315       mng_info->write_png32 = MagickFalse;
11316       mng_info->write_png48 = MagickFalse;
11317       mng_info->write_png64 = MagickFalse;
11318
11319       if (LocaleCompare(value,"png8") == 0)
11320         mng_info->write_png8 = MagickTrue;
11321
11322       else if (LocaleCompare(value,"png24") == 0)
11323         mng_info->write_png24 = MagickTrue;
11324
11325       else if (LocaleCompare(value,"png32") == 0)
11326         mng_info->write_png32 = MagickTrue;
11327
11328       else if (LocaleCompare(value,"png48") == 0)
11329         mng_info->write_png48 = MagickTrue;
11330
11331       else if (LocaleCompare(value,"png64") == 0)
11332         mng_info->write_png64 = MagickTrue;
11333     }
11334
11335   if (LocaleCompare(value,"png00") == 0)
11336     {
11337       /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig
11338          Note that whitespace at the end of the property names must match
11339          that in the corresponding SetImageProperty() calls.
11340        */
11341       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11342          "  Format=%s",value);
11343
11344       value=GetImageProperty(image,"png:IHDR.bit-depth-orig  ",exception);
11345
11346       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11347          "  png00 inherited bit depth=%s",value);
11348
11349       if (value != (char *) NULL)
11350         {
11351           if (LocaleCompare(value,"1") == 0)
11352             mng_info->write_png_depth = 1;
11353
11354           else if (LocaleCompare(value,"1") == 0)
11355             mng_info->write_png_depth = 2;
11356
11357           else if (LocaleCompare(value,"2") == 0)
11358             mng_info->write_png_depth = 4;
11359
11360           else if (LocaleCompare(value,"8") == 0)
11361             mng_info->write_png_depth = 8;
11362
11363           else if (LocaleCompare(value,"16") == 0)
11364             mng_info->write_png_depth = 16;
11365         }
11366
11367       value=GetImageProperty(image,"png:IHDR.color-type-orig ",exception);
11368
11369       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11370          "  png00 inherited color type=%s",value);
11371
11372       if (value != (char *) NULL)
11373         {
11374           if (LocaleCompare(value,"0") == 0)
11375             mng_info->write_png_colortype = 1;
11376
11377           else if (LocaleCompare(value,"2") == 0)
11378             mng_info->write_png_colortype = 3;
11379
11380           else if (LocaleCompare(value,"3") == 0)
11381             mng_info->write_png_colortype = 4;
11382
11383           else if (LocaleCompare(value,"4") == 0)
11384             mng_info->write_png_colortype = 5;
11385
11386           else if (LocaleCompare(value,"6") == 0)
11387             mng_info->write_png_colortype = 7;
11388         }
11389     }
11390
11391   if (mng_info->write_png8)
11392     {
11393       mng_info->write_png_colortype = /* 3 */ 4;
11394       mng_info->write_png_depth = 8;
11395       image->depth = 8;
11396     }
11397
11398   if (mng_info->write_png24)
11399     {
11400       mng_info->write_png_colortype = /* 2 */ 3;
11401       mng_info->write_png_depth = 8;
11402       image->depth = 8;
11403
11404       if (image->alpha_trait == BlendPixelTrait)
11405         (void) SetImageType(image,TrueColorMatteType,exception);
11406
11407       else
11408         (void) SetImageType(image,TrueColorType,exception);
11409
11410       (void) SyncImage(image,exception);
11411     }
11412
11413   if (mng_info->write_png32)
11414     {
11415       mng_info->write_png_colortype = /* 6 */  7;
11416       mng_info->write_png_depth = 8;
11417       image->depth = 8;
11418
11419       if (image->alpha_trait == BlendPixelTrait)
11420         (void) SetImageType(image,TrueColorMatteType,exception);
11421
11422       else
11423         (void) SetImageType(image,TrueColorType,exception);
11424
11425       (void) SyncImage(image,exception);
11426     }
11427
11428   if (mng_info->write_png48)
11429     {
11430       mng_info->write_png_colortype = /* 2 */ 3;
11431       mng_info->write_png_depth = 16;
11432       image->depth = 16;
11433
11434       if (image->alpha_trait == BlendPixelTrait)
11435         (void) SetImageType(image,TrueColorMatteType,exception);
11436
11437       else
11438         (void) SetImageType(image,TrueColorType,exception);
11439
11440       (void) SyncImage(image,exception);
11441     }
11442
11443   if (mng_info->write_png64)
11444     {
11445       mng_info->write_png_colortype = /* 6 */  7;
11446       mng_info->write_png_depth = 16;
11447       image->depth = 16;
11448
11449       if (image->alpha_trait == BlendPixelTrait)
11450         (void) SetImageType(image,TrueColorMatteType,exception);
11451
11452       else
11453         (void) SetImageType(image,TrueColorType,exception);
11454
11455       (void) SyncImage(image,exception);
11456     }
11457
11458   value=GetImageOption(image_info,"png:bit-depth");
11459
11460   if (value != (char *) NULL)
11461     {
11462       if (LocaleCompare(value,"1") == 0)
11463         mng_info->write_png_depth = 1;
11464
11465       else if (LocaleCompare(value,"2") == 0)
11466         mng_info->write_png_depth = 2;
11467
11468       else if (LocaleCompare(value,"4") == 0)
11469         mng_info->write_png_depth = 4;
11470
11471       else if (LocaleCompare(value,"8") == 0)
11472         mng_info->write_png_depth = 8;
11473
11474       else if (LocaleCompare(value,"16") == 0)
11475         mng_info->write_png_depth = 16;
11476
11477       else
11478         (void) ThrowMagickException(exception,
11479              GetMagickModule(),CoderWarning,
11480              "ignoring invalid defined png:bit-depth",
11481              "=%s",value);
11482
11483       if (logging != MagickFalse)
11484         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11485           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11486     }
11487
11488   value=GetImageOption(image_info,"png:color-type");
11489
11490   if (value != (char *) NULL)
11491     {
11492       /* We must store colortype+1 because 0 is a valid colortype */
11493       if (LocaleCompare(value,"0") == 0)
11494         mng_info->write_png_colortype = 1;
11495
11496       else if (LocaleCompare(value,"1") == 0)
11497         mng_info->write_png_colortype = 2;
11498
11499       else if (LocaleCompare(value,"2") == 0)
11500         mng_info->write_png_colortype = 3;
11501
11502       else if (LocaleCompare(value,"3") == 0)
11503         mng_info->write_png_colortype = 4;
11504
11505       else if (LocaleCompare(value,"4") == 0)
11506         mng_info->write_png_colortype = 5;
11507
11508       else if (LocaleCompare(value,"6") == 0)
11509         mng_info->write_png_colortype = 7;
11510
11511       else
11512         (void) ThrowMagickException(exception,
11513              GetMagickModule(),CoderWarning,
11514              "ignoring invalid defined png:color-type",
11515              "=%s",value);
11516
11517       if (logging != MagickFalse)
11518         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11519           "  png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11520     }
11521
11522   /* Check for chunks to be excluded:
11523    *
11524    * The default is to not exclude any known chunks except for any
11525    * listed in the "unused_chunks" array, above.
11526    *
11527    * Chunks can be listed for exclusion via a "png:exclude-chunk"
11528    * define (in the image properties or in the image artifacts)
11529    * or via a mng_info member.  For convenience, in addition
11530    * to or instead of a comma-separated list of chunks, the
11531    * "exclude-chunk" string can be simply "all" or "none".
11532    *
11533    * The exclude-chunk define takes priority over the mng_info.
11534    *
11535    * A "png:include-chunk" define takes  priority over both the
11536    * mng_info and the "png:exclude-chunk" define.  Like the
11537    * "exclude-chunk" string, it can define "all" or "none" as
11538    * well as a comma-separated list.  Chunks that are unknown to
11539    * ImageMagick are always excluded, regardless of their "copy-safe"
11540    * status according to the PNG specification, and even if they
11541    * appear in the "include-chunk" list. Such defines appearing among
11542    * the image options take priority over those found among the image
11543    * artifacts.
11544    *
11545    * Finally, all chunks listed in the "unused_chunks" array are
11546    * automatically excluded, regardless of the other instructions
11547    * or lack thereof.
11548    *
11549    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11550    * will not be written and the gAMA chunk will only be written if it
11551    * is not between .45 and .46, or approximately (1.0/2.2).
11552    *
11553    * If you exclude tRNS and the image has transparency, the colortype
11554    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11555    *
11556    * The -strip option causes StripImage() to set the png:include-chunk
11557    * artifact to "none,trns,gama".
11558    */
11559
11560   mng_info->ping_exclude_bKGD=MagickFalse;
11561   mng_info->ping_exclude_cHRM=MagickFalse;
11562   mng_info->ping_exclude_date=MagickFalse;
11563   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11564   mng_info->ping_exclude_gAMA=MagickFalse;
11565   mng_info->ping_exclude_iCCP=MagickFalse;
11566   /* mng_info->ping_exclude_iTXt=MagickFalse; */
11567   mng_info->ping_exclude_oFFs=MagickFalse;
11568   mng_info->ping_exclude_pHYs=MagickFalse;
11569   mng_info->ping_exclude_sRGB=MagickFalse;
11570   mng_info->ping_exclude_tEXt=MagickFalse;
11571   mng_info->ping_exclude_tRNS=MagickFalse;
11572   mng_info->ping_exclude_vpAg=MagickFalse;
11573   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11574   mng_info->ping_exclude_zTXt=MagickFalse;
11575
11576   mng_info->ping_preserve_colormap=MagickFalse;
11577
11578   value=GetImageArtifact(image,"png:preserve-colormap");
11579   if (value == NULL)
11580      value=GetImageOption(image_info,"png:preserve-colormap");
11581   if (value != NULL)
11582      mng_info->ping_preserve_colormap=MagickTrue;
11583
11584   /* Thes compression-level, compression-strategy, and compression-filter
11585    * defines take precedence over values from the -quality option.
11586    */
11587   value=GetImageArtifact(image,"png:compression-level");
11588   if (value == NULL)
11589      value=GetImageOption(image_info,"png:compression-level");
11590   if (value != NULL)
11591   {
11592       /* We have to add 1 to everything because 0 is a valid input,
11593        * and we want to use 0 (the default) to mean undefined.
11594        */
11595       if (LocaleCompare(value,"0") == 0)
11596         mng_info->write_png_compression_level = 1;
11597
11598       else if (LocaleCompare(value,"1") == 0)
11599         mng_info->write_png_compression_level = 2;
11600
11601       else if (LocaleCompare(value,"2") == 0)
11602         mng_info->write_png_compression_level = 3;
11603
11604       else if (LocaleCompare(value,"3") == 0)
11605         mng_info->write_png_compression_level = 4;
11606
11607       else if (LocaleCompare(value,"4") == 0)
11608         mng_info->write_png_compression_level = 5;
11609
11610       else if (LocaleCompare(value,"5") == 0)
11611         mng_info->write_png_compression_level = 6;
11612
11613       else if (LocaleCompare(value,"6") == 0)
11614         mng_info->write_png_compression_level = 7;
11615
11616       else if (LocaleCompare(value,"7") == 0)
11617         mng_info->write_png_compression_level = 8;
11618
11619       else if (LocaleCompare(value,"8") == 0)
11620         mng_info->write_png_compression_level = 9;
11621
11622       else if (LocaleCompare(value,"9") == 0)
11623         mng_info->write_png_compression_level = 10;
11624
11625       else
11626         (void) ThrowMagickException(exception,
11627              GetMagickModule(),CoderWarning,
11628              "ignoring invalid defined png:compression-level",
11629              "=%s",value);
11630     }
11631
11632   value=GetImageArtifact(image,"png:compression-strategy");
11633   if (value == NULL)
11634      value=GetImageOption(image_info,"png:compression-strategy");
11635   if (value != NULL)
11636   {
11637
11638       if (LocaleCompare(value,"0") == 0)
11639         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11640
11641       else if (LocaleCompare(value,"1") == 0)
11642         mng_info->write_png_compression_strategy = Z_FILTERED+1;
11643
11644       else if (LocaleCompare(value,"2") == 0)
11645         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11646
11647       else if (LocaleCompare(value,"3") == 0)
11648 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
11649         mng_info->write_png_compression_strategy = Z_RLE+1;
11650 #else
11651         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11652 #endif
11653
11654       else if (LocaleCompare(value,"4") == 0)
11655 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
11656         mng_info->write_png_compression_strategy = Z_FIXED+1;
11657 #else
11658         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11659 #endif
11660
11661       else
11662         (void) ThrowMagickException(exception,
11663              GetMagickModule(),CoderWarning,
11664              "ignoring invalid defined png:compression-strategy",
11665              "=%s",value);
11666     }
11667
11668   value=GetImageArtifact(image,"png:compression-filter");
11669   if (value == NULL)
11670      value=GetImageOption(image_info,"png:compression-filter");
11671   if (value != NULL)
11672   {
11673
11674       /* To do: combinations of filters allowed by libpng
11675        * masks 0x08 through 0xf8
11676        *
11677        * Implement this as a comma-separated list of 0,1,2,3,4,5
11678        * where 5 is a special case meaning PNG_ALL_FILTERS.
11679        */
11680
11681       if (LocaleCompare(value,"0") == 0)
11682         mng_info->write_png_compression_filter = 1;
11683
11684       else if (LocaleCompare(value,"1") == 0)
11685         mng_info->write_png_compression_filter = 2;
11686
11687       else if (LocaleCompare(value,"2") == 0)
11688         mng_info->write_png_compression_filter = 3;
11689
11690       else if (LocaleCompare(value,"3") == 0)
11691         mng_info->write_png_compression_filter = 4;
11692
11693       else if (LocaleCompare(value,"4") == 0)
11694         mng_info->write_png_compression_filter = 5;
11695
11696       else if (LocaleCompare(value,"5") == 0)
11697         mng_info->write_png_compression_filter = 6;
11698
11699       else
11700         (void) ThrowMagickException(exception,
11701              GetMagickModule(),CoderWarning,
11702              "ignoring invalid defined png:compression-filter",
11703              "=%s",value);
11704     }
11705
11706   excluding=MagickFalse;
11707
11708   for (source=0; source<1; source++)
11709   {
11710     if (source==0)
11711       {
11712        value=GetImageArtifact(image,"png:exclude-chunk");
11713
11714        if (value == NULL)
11715          value=GetImageArtifact(image,"png:exclude-chunks");
11716       }
11717     else
11718       {
11719        value=GetImageOption(image_info,"png:exclude-chunk");
11720
11721        if (value == NULL)
11722          value=GetImageOption(image_info,"png:exclude-chunks");
11723       }
11724
11725     if (value != NULL)
11726     {
11727
11728     size_t
11729       last;
11730
11731     excluding=MagickTrue;
11732
11733     if (logging != MagickFalse)
11734       {
11735         if (source == 0)
11736            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11737               "  png:exclude-chunk=%s found in image artifacts.\n", value);
11738         else
11739            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11740               "  png:exclude-chunk=%s found in image properties.\n", value);
11741       }
11742
11743     last=strlen(value);
11744
11745     for (i=0; i<(int) last; i+=5)
11746     {
11747
11748       if (LocaleNCompare(value+i,"all",3) == 0)
11749       {
11750         mng_info->ping_exclude_bKGD=MagickTrue;
11751         mng_info->ping_exclude_cHRM=MagickTrue;
11752         mng_info->ping_exclude_date=MagickTrue;
11753         mng_info->ping_exclude_EXIF=MagickTrue;
11754         mng_info->ping_exclude_gAMA=MagickTrue;
11755         mng_info->ping_exclude_iCCP=MagickTrue;
11756         /* mng_info->ping_exclude_iTXt=MagickTrue; */
11757         mng_info->ping_exclude_oFFs=MagickTrue;
11758         mng_info->ping_exclude_pHYs=MagickTrue;
11759         mng_info->ping_exclude_sRGB=MagickTrue;
11760         mng_info->ping_exclude_tEXt=MagickTrue;
11761         mng_info->ping_exclude_tRNS=MagickTrue;
11762         mng_info->ping_exclude_vpAg=MagickTrue;
11763         mng_info->ping_exclude_zCCP=MagickTrue;
11764         mng_info->ping_exclude_zTXt=MagickTrue;
11765         i--;
11766       }
11767
11768       if (LocaleNCompare(value+i,"none",4) == 0)
11769       {
11770         mng_info->ping_exclude_bKGD=MagickFalse;
11771         mng_info->ping_exclude_cHRM=MagickFalse;
11772         mng_info->ping_exclude_date=MagickFalse;
11773         mng_info->ping_exclude_EXIF=MagickFalse;
11774         mng_info->ping_exclude_gAMA=MagickFalse;
11775         mng_info->ping_exclude_iCCP=MagickFalse;
11776         /* mng_info->ping_exclude_iTXt=MagickFalse; */
11777         mng_info->ping_exclude_oFFs=MagickFalse;
11778         mng_info->ping_exclude_pHYs=MagickFalse;
11779         mng_info->ping_exclude_sRGB=MagickFalse;
11780         mng_info->ping_exclude_tEXt=MagickFalse;
11781         mng_info->ping_exclude_tRNS=MagickFalse;
11782         mng_info->ping_exclude_vpAg=MagickFalse;
11783         mng_info->ping_exclude_zCCP=MagickFalse;
11784         mng_info->ping_exclude_zTXt=MagickFalse;
11785       }
11786
11787       if (LocaleNCompare(value+i,"bkgd",4) == 0)
11788         mng_info->ping_exclude_bKGD=MagickTrue;
11789
11790       if (LocaleNCompare(value+i,"chrm",4) == 0)
11791         mng_info->ping_exclude_cHRM=MagickTrue;
11792
11793       if (LocaleNCompare(value+i,"date",4) == 0)
11794         mng_info->ping_exclude_date=MagickTrue;
11795
11796       if (LocaleNCompare(value+i,"exif",4) == 0)
11797         mng_info->ping_exclude_EXIF=MagickTrue;
11798
11799       if (LocaleNCompare(value+i,"gama",4) == 0)
11800         mng_info->ping_exclude_gAMA=MagickTrue;
11801
11802       if (LocaleNCompare(value+i,"iccp",4) == 0)
11803         mng_info->ping_exclude_iCCP=MagickTrue;
11804
11805     /*
11806       if (LocaleNCompare(value+i,"itxt",4) == 0)
11807         mng_info->ping_exclude_iTXt=MagickTrue;
11808      */
11809
11810       if (LocaleNCompare(value+i,"gama",4) == 0)
11811         mng_info->ping_exclude_gAMA=MagickTrue;
11812
11813       if (LocaleNCompare(value+i,"offs",4) == 0)
11814         mng_info->ping_exclude_oFFs=MagickTrue;
11815
11816       if (LocaleNCompare(value+i,"phys",4) == 0)
11817         mng_info->ping_exclude_pHYs=MagickTrue;
11818
11819       if (LocaleNCompare(value+i,"srgb",4) == 0)
11820         mng_info->ping_exclude_sRGB=MagickTrue;
11821
11822       if (LocaleNCompare(value+i,"text",4) == 0)
11823         mng_info->ping_exclude_tEXt=MagickTrue;
11824
11825       if (LocaleNCompare(value+i,"trns",4) == 0)
11826         mng_info->ping_exclude_tRNS=MagickTrue;
11827
11828       if (LocaleNCompare(value+i,"vpag",4) == 0)
11829         mng_info->ping_exclude_vpAg=MagickTrue;
11830
11831       if (LocaleNCompare(value+i,"zccp",4) == 0)
11832         mng_info->ping_exclude_zCCP=MagickTrue;
11833
11834       if (LocaleNCompare(value+i,"ztxt",4) == 0)
11835         mng_info->ping_exclude_zTXt=MagickTrue;
11836
11837       }
11838     }
11839   }
11840
11841   for (source=0; source<1; source++)
11842   {
11843     if (source==0)
11844       {
11845        value=GetImageArtifact(image,"png:include-chunk");
11846
11847        if (value == NULL)
11848          value=GetImageArtifact(image,"png:include-chunks");
11849       }
11850     else
11851       {
11852        value=GetImageOption(image_info,"png:include-chunk");
11853
11854        if (value == NULL)
11855          value=GetImageOption(image_info,"png:include-chunks");
11856       }
11857
11858     if (value != NULL)
11859     {
11860     size_t
11861       last;
11862
11863     excluding=MagickTrue;
11864
11865     if (logging != MagickFalse)
11866       {
11867         if (source == 0)
11868            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11869               "  png:include-chunk=%s found in image artifacts.\n", value);
11870         else
11871            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11872               "  png:include-chunk=%s found in image properties.\n", value);
11873       }
11874
11875     last=strlen(value);
11876
11877     for (i=0; i<(int) last; i+=5)
11878       {
11879       if (LocaleNCompare(value+i,"all",3) == 0)
11880         {
11881           mng_info->ping_exclude_bKGD=MagickFalse;
11882           mng_info->ping_exclude_cHRM=MagickFalse;
11883           mng_info->ping_exclude_date=MagickFalse;
11884           mng_info->ping_exclude_EXIF=MagickFalse;
11885           mng_info->ping_exclude_gAMA=MagickFalse;
11886           mng_info->ping_exclude_iCCP=MagickFalse;
11887           /* mng_info->ping_exclude_iTXt=MagickFalse; */
11888           mng_info->ping_exclude_oFFs=MagickFalse;
11889           mng_info->ping_exclude_pHYs=MagickFalse;
11890           mng_info->ping_exclude_sRGB=MagickFalse;
11891           mng_info->ping_exclude_tEXt=MagickFalse;
11892           mng_info->ping_exclude_tRNS=MagickFalse;
11893           mng_info->ping_exclude_vpAg=MagickFalse;
11894           mng_info->ping_exclude_zCCP=MagickFalse;
11895           mng_info->ping_exclude_zTXt=MagickFalse;
11896           i--;
11897         }
11898
11899       if (LocaleNCompare(value+i,"none",4) == 0)
11900         {
11901           mng_info->ping_exclude_bKGD=MagickTrue;
11902           mng_info->ping_exclude_cHRM=MagickTrue;
11903           mng_info->ping_exclude_date=MagickTrue;
11904           mng_info->ping_exclude_EXIF=MagickTrue;
11905           mng_info->ping_exclude_gAMA=MagickTrue;
11906           mng_info->ping_exclude_iCCP=MagickTrue;
11907           /* mng_info->ping_exclude_iTXt=MagickTrue; */
11908           mng_info->ping_exclude_oFFs=MagickTrue;
11909           mng_info->ping_exclude_pHYs=MagickTrue;
11910           mng_info->ping_exclude_sRGB=MagickTrue;
11911           mng_info->ping_exclude_tEXt=MagickTrue;
11912           mng_info->ping_exclude_tRNS=MagickTrue;
11913           mng_info->ping_exclude_vpAg=MagickTrue;
11914           mng_info->ping_exclude_zCCP=MagickTrue;
11915           mng_info->ping_exclude_zTXt=MagickTrue;
11916         }
11917
11918       if (LocaleNCompare(value+i,"bkgd",4) == 0)
11919         mng_info->ping_exclude_bKGD=MagickFalse;
11920
11921       if (LocaleNCompare(value+i,"chrm",4) == 0)
11922         mng_info->ping_exclude_cHRM=MagickFalse;
11923
11924       if (LocaleNCompare(value+i,"date",4) == 0)
11925         mng_info->ping_exclude_date=MagickFalse;
11926
11927       if (LocaleNCompare(value+i,"exif",4) == 0)
11928         mng_info->ping_exclude_EXIF=MagickFalse;
11929
11930       if (LocaleNCompare(value+i,"gama",4) == 0)
11931         mng_info->ping_exclude_gAMA=MagickFalse;
11932
11933       if (LocaleNCompare(value+i,"iccp",4) == 0)
11934         mng_info->ping_exclude_iCCP=MagickFalse;
11935
11936     /*
11937       if (LocaleNCompare(value+i,"itxt",4) == 0)
11938         mng_info->ping_exclude_iTXt=MagickFalse;
11939      */
11940
11941       if (LocaleNCompare(value+i,"gama",4) == 0)
11942         mng_info->ping_exclude_gAMA=MagickFalse;
11943
11944       if (LocaleNCompare(value+i,"offs",4) == 0)
11945         mng_info->ping_exclude_oFFs=MagickFalse;
11946
11947       if (LocaleNCompare(value+i,"phys",4) == 0)
11948         mng_info->ping_exclude_pHYs=MagickFalse;
11949
11950       if (LocaleNCompare(value+i,"srgb",4) == 0)
11951         mng_info->ping_exclude_sRGB=MagickFalse;
11952
11953       if (LocaleNCompare(value+i,"text",4) == 0)
11954         mng_info->ping_exclude_tEXt=MagickFalse;
11955
11956       if (LocaleNCompare(value+i,"trns",4) == 0)
11957         mng_info->ping_exclude_tRNS=MagickFalse;
11958
11959       if (LocaleNCompare(value+i,"vpag",4) == 0)
11960         mng_info->ping_exclude_vpAg=MagickFalse;
11961
11962       if (LocaleNCompare(value+i,"zccp",4) == 0)
11963         mng_info->ping_exclude_zCCP=MagickFalse;
11964
11965       if (LocaleNCompare(value+i,"ztxt",4) == 0)
11966         mng_info->ping_exclude_zTXt=MagickFalse;
11967
11968       }
11969     }
11970   }
11971
11972   if (excluding != MagickFalse && logging != MagickFalse)
11973   {
11974     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11975       "  Chunks to be excluded from the output png:");
11976     if (mng_info->ping_exclude_bKGD != MagickFalse)
11977       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11978           "    bKGD");
11979     if (mng_info->ping_exclude_cHRM != MagickFalse)
11980       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11981           "    cHRM");
11982     if (mng_info->ping_exclude_date != MagickFalse)
11983       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11984           "    date");
11985     if (mng_info->ping_exclude_EXIF != MagickFalse)
11986       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11987           "    EXIF");
11988     if (mng_info->ping_exclude_gAMA != MagickFalse)
11989       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11990           "    gAMA");
11991     if (mng_info->ping_exclude_iCCP != MagickFalse)
11992       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11993           "    iCCP");
11994 /*
11995     if (mng_info->ping_exclude_iTXt != MagickFalse)
11996       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11997           "    iTXt");
11998 */
11999     if (mng_info->ping_exclude_oFFs != MagickFalse)
12000       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12001           "    oFFs");
12002     if (mng_info->ping_exclude_pHYs != MagickFalse)
12003       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12004           "    pHYs");
12005     if (mng_info->ping_exclude_sRGB != MagickFalse)
12006       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12007           "    sRGB");
12008     if (mng_info->ping_exclude_tEXt != MagickFalse)
12009       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12010           "    tEXt");
12011     if (mng_info->ping_exclude_tRNS != MagickFalse)
12012       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12013           "    tRNS");
12014     if (mng_info->ping_exclude_vpAg != MagickFalse)
12015       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12016           "    vpAg");
12017     if (mng_info->ping_exclude_zCCP != MagickFalse)
12018       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12019           "    zCCP");
12020     if (mng_info->ping_exclude_zTXt != MagickFalse)
12021       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12022           "    zTXt");
12023   }
12024
12025   mng_info->need_blob = MagickTrue;
12026
12027   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12028
12029   MngInfoFreeStruct(mng_info,&have_mng_structure);
12030
12031   if (logging != MagickFalse)
12032     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12033
12034   return(status);
12035 }
12036
12037 #if defined(JNG_SUPPORTED)
12038
12039 /* Write one JNG image */
12040 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12041    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12042 {
12043   Image
12044     *jpeg_image;
12045
12046   ImageInfo
12047     *jpeg_image_info;
12048
12049   MagickBooleanType
12050     logging,
12051     status;
12052
12053   size_t
12054     length;
12055
12056   unsigned char
12057     *blob,
12058     chunk[80],
12059     *p;
12060
12061   unsigned int
12062     jng_alpha_compression_method,
12063     jng_alpha_sample_depth,
12064     jng_color_type,
12065     transparent;
12066
12067   size_t
12068     jng_alpha_quality,
12069     jng_quality;
12070
12071   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12072     "  Enter WriteOneJNGImage()");
12073
12074   blob=(unsigned char *) NULL;
12075   jpeg_image=(Image *) NULL;
12076   jpeg_image_info=(ImageInfo *) NULL;
12077
12078   status=MagickTrue;
12079   transparent=image_info->type==GrayscaleMatteType ||
12080      image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
12081
12082   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12083
12084   jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12085
12086   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12087       image_info->quality;
12088
12089   if (jng_alpha_quality >= 1000)
12090     jng_alpha_quality /= 1000;
12091
12092   if (transparent)
12093     {
12094       jng_color_type=14;
12095
12096       /* Create JPEG blob, image, and image_info */
12097       if (logging != MagickFalse)
12098         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12099           "  Creating jpeg_image_info for alpha.");
12100
12101       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12102
12103       if (jpeg_image_info == (ImageInfo *) NULL)
12104         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12105
12106       if (logging != MagickFalse)
12107         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12108           "  Creating jpeg_image.");
12109
12110       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12111       if (jpeg_image == (Image *) NULL)
12112         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12113       (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12114       jpeg_image->alpha_trait=UndefinedPixelTrait;
12115       jpeg_image->quality=jng_alpha_quality;
12116       jpeg_image_info->type=GrayscaleType;
12117       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12118       (void) AcquireUniqueFilename(jpeg_image->filename);
12119       (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
12120         "%s",jpeg_image->filename);
12121     }
12122   else
12123     {
12124       jng_alpha_compression_method=0;
12125       jng_color_type=10;
12126       jng_alpha_sample_depth=0;
12127     }
12128
12129   /* To do: check bit depth of PNG alpha channel */
12130
12131   /* Check if image is grayscale. */
12132   if (image_info->type != TrueColorMatteType && image_info->type !=
12133     TrueColorType && IsImageGray(image,exception))
12134     jng_color_type-=2;
12135
12136   if (logging != MagickFalse)
12137     {
12138         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12139           "    JNG Quality           = %d",(int) jng_quality);
12140         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12141           "    JNG Color Type        = %d",jng_color_type);
12142         if (transparent)
12143           {
12144             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12145               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12146             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12147               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12148             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12149               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12150           }
12151     }
12152
12153   if (transparent)
12154     {
12155       if (jng_alpha_compression_method==0)
12156         {
12157           const char
12158             *value;
12159
12160           /* Encode alpha as a grayscale PNG blob */
12161           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12162             exception);
12163           if (logging != MagickFalse)
12164             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12165               "  Creating PNG blob.");
12166           length=0;
12167
12168           (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12169           (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12170           jpeg_image_info->interlace=NoInterlace;
12171
12172           /* Exclude all ancillary chunks */
12173           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12174
12175           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12176             exception);
12177
12178           /* Retrieve sample depth used */
12179           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12180           if (value != (char *) NULL)
12181             jng_alpha_sample_depth= (unsigned int) value[0];
12182         }
12183       else
12184         {
12185           /* Encode alpha as a grayscale JPEG blob */
12186
12187           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12188             exception);
12189
12190           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12191           (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12192           jpeg_image_info->interlace=NoInterlace;
12193           if (logging != MagickFalse)
12194             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12195               "  Creating blob.");
12196           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12197            exception);
12198           jng_alpha_sample_depth=8;
12199
12200           if (logging != MagickFalse)
12201             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12202               "  Successfully read jpeg_image into a blob, length=%.20g.",
12203               (double) length);
12204
12205         }
12206       /* Destroy JPEG image and image_info */
12207       jpeg_image=DestroyImage(jpeg_image);
12208       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12209       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12210     }
12211
12212   /* Write JHDR chunk */
12213   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12214   PNGType(chunk,mng_JHDR);
12215   LogPNGChunk(logging,mng_JHDR,16L);
12216   PNGLong(chunk+4,(png_uint_32) image->columns);
12217   PNGLong(chunk+8,(png_uint_32) image->rows);
12218   chunk[12]=jng_color_type;
12219   chunk[13]=8;  /* sample depth */
12220   chunk[14]=8; /*jng_image_compression_method */
12221   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12222   chunk[16]=jng_alpha_sample_depth;
12223   chunk[17]=jng_alpha_compression_method;
12224   chunk[18]=0; /*jng_alpha_filter_method */
12225   chunk[19]=0; /*jng_alpha_interlace_method */
12226   (void) WriteBlob(image,20,chunk);
12227   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12228   if (logging != MagickFalse)
12229     {
12230       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12231         "    JNG width:%15lu",(unsigned long) image->columns);
12232
12233       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12234         "    JNG height:%14lu",(unsigned long) image->rows);
12235
12236       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12237         "    JNG color type:%10d",jng_color_type);
12238
12239       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12240         "    JNG sample depth:%8d",8);
12241
12242       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12243         "    JNG compression:%9d",8);
12244
12245       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12246         "    JNG interlace:%11d",0);
12247
12248       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12249         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
12250
12251       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12252         "    JNG alpha compression:%3d",jng_alpha_compression_method);
12253
12254       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12255         "    JNG alpha filter:%8d",0);
12256
12257       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12258         "    JNG alpha interlace:%5d",0);
12259     }
12260
12261   /* Write any JNG-chunk-b profiles */
12262   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12263
12264   /*
12265      Write leading ancillary chunks
12266   */
12267
12268   if (transparent)
12269   {
12270     /*
12271       Write JNG bKGD chunk
12272     */
12273
12274     unsigned char
12275       blue,
12276       green,
12277       red;
12278
12279     ssize_t
12280       num_bytes;
12281
12282     if (jng_color_type == 8 || jng_color_type == 12)
12283       num_bytes=6L;
12284     else
12285       num_bytes=10L;
12286     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12287     PNGType(chunk,mng_bKGD);
12288     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12289     red=ScaleQuantumToChar(image->background_color.red);
12290     green=ScaleQuantumToChar(image->background_color.green);
12291     blue=ScaleQuantumToChar(image->background_color.blue);
12292     *(chunk+4)=0;
12293     *(chunk+5)=red;
12294     *(chunk+6)=0;
12295     *(chunk+7)=green;
12296     *(chunk+8)=0;
12297     *(chunk+9)=blue;
12298     (void) WriteBlob(image,(size_t) num_bytes,chunk);
12299     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12300   }
12301
12302   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12303     {
12304       /*
12305         Write JNG sRGB chunk
12306       */
12307       (void) WriteBlobMSBULong(image,1L);
12308       PNGType(chunk,mng_sRGB);
12309       LogPNGChunk(logging,mng_sRGB,1L);
12310
12311       if (image->rendering_intent != UndefinedIntent)
12312         chunk[4]=(unsigned char)
12313           Magick_RenderingIntent_to_PNG_RenderingIntent(
12314           (image->rendering_intent));
12315
12316       else
12317         chunk[4]=(unsigned char)
12318           Magick_RenderingIntent_to_PNG_RenderingIntent(
12319           (PerceptualIntent));
12320
12321       (void) WriteBlob(image,5,chunk);
12322       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12323     }
12324   else
12325     {
12326       if (image->gamma != 0.0)
12327         {
12328           /*
12329              Write JNG gAMA chunk
12330           */
12331           (void) WriteBlobMSBULong(image,4L);
12332           PNGType(chunk,mng_gAMA);
12333           LogPNGChunk(logging,mng_gAMA,4L);
12334           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12335           (void) WriteBlob(image,8,chunk);
12336           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12337         }
12338
12339       if ((mng_info->equal_chrms == MagickFalse) &&
12340           (image->chromaticity.red_primary.x != 0.0))
12341         {
12342           PrimaryInfo
12343             primary;
12344
12345           /*
12346              Write JNG cHRM chunk
12347           */
12348           (void) WriteBlobMSBULong(image,32L);
12349           PNGType(chunk,mng_cHRM);
12350           LogPNGChunk(logging,mng_cHRM,32L);
12351           primary=image->chromaticity.white_point;
12352           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12353           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12354           primary=image->chromaticity.red_primary;
12355           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12356           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12357           primary=image->chromaticity.green_primary;
12358           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12359           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12360           primary=image->chromaticity.blue_primary;
12361           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12362           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12363           (void) WriteBlob(image,36,chunk);
12364           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12365         }
12366     }
12367
12368   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12369     {
12370       /*
12371          Write JNG pHYs chunk
12372       */
12373       (void) WriteBlobMSBULong(image,9L);
12374       PNGType(chunk,mng_pHYs);
12375       LogPNGChunk(logging,mng_pHYs,9L);
12376       if (image->units == PixelsPerInchResolution)
12377         {
12378           PNGLong(chunk+4,(png_uint_32)
12379             (image->resolution.x*100.0/2.54+0.5));
12380
12381           PNGLong(chunk+8,(png_uint_32)
12382             (image->resolution.y*100.0/2.54+0.5));
12383
12384           chunk[12]=1;
12385         }
12386
12387       else
12388         {
12389           if (image->units == PixelsPerCentimeterResolution)
12390             {
12391               PNGLong(chunk+4,(png_uint_32)
12392                 (image->resolution.x*100.0+0.5));
12393
12394               PNGLong(chunk+8,(png_uint_32)
12395                 (image->resolution.y*100.0+0.5));
12396
12397               chunk[12]=1;
12398             }
12399
12400           else
12401             {
12402               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12403               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12404               chunk[12]=0;
12405             }
12406         }
12407       (void) WriteBlob(image,13,chunk);
12408       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12409     }
12410
12411   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12412     {
12413       /*
12414          Write JNG oFFs chunk
12415       */
12416       (void) WriteBlobMSBULong(image,9L);
12417       PNGType(chunk,mng_oFFs);
12418       LogPNGChunk(logging,mng_oFFs,9L);
12419       PNGsLong(chunk+4,(ssize_t) (image->page.x));
12420       PNGsLong(chunk+8,(ssize_t) (image->page.y));
12421       chunk[12]=0;
12422       (void) WriteBlob(image,13,chunk);
12423       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12424     }
12425   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12426     {
12427        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
12428        PNGType(chunk,mng_vpAg);
12429        LogPNGChunk(logging,mng_vpAg,9L);
12430        PNGLong(chunk+4,(png_uint_32) image->page.width);
12431        PNGLong(chunk+8,(png_uint_32) image->page.height);
12432        chunk[12]=0;   /* unit = pixels */
12433        (void) WriteBlob(image,13,chunk);
12434        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12435     }
12436
12437
12438   if (transparent)
12439     {
12440       if (jng_alpha_compression_method==0)
12441         {
12442           register ssize_t
12443             i;
12444
12445           ssize_t
12446             len;
12447
12448           /* Write IDAT chunk header */
12449           if (logging != MagickFalse)
12450             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12451               "  Write IDAT chunks from blob, length=%.20g.",(double)
12452               length);
12453
12454           /* Copy IDAT chunks */
12455           len=0;
12456           p=blob+8;
12457           for (i=8; i<(ssize_t) length; i+=len+12)
12458           {
12459             len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12460             p+=4;
12461
12462             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12463               {
12464                 /* Found an IDAT chunk. */
12465                 (void) WriteBlobMSBULong(image,(size_t) len);
12466                 LogPNGChunk(logging,mng_IDAT,(size_t) len);
12467                 (void) WriteBlob(image,(size_t) len+4,p);
12468                 (void) WriteBlobMSBULong(image,
12469                     crc32(0,p,(uInt) len+4));
12470               }
12471
12472             else
12473               {
12474                 if (logging != MagickFalse)
12475                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12476                     "    Skipping %c%c%c%c chunk, length=%.20g.",
12477                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
12478               }
12479             p+=(8+len);
12480           }
12481         }
12482       else
12483         {
12484           /* Write JDAA chunk header */
12485           if (logging != MagickFalse)
12486             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12487               "  Write JDAA chunk, length=%.20g.",(double) length);
12488           (void) WriteBlobMSBULong(image,(size_t) length);
12489           PNGType(chunk,mng_JDAA);
12490           LogPNGChunk(logging,mng_JDAA,length);
12491           /* Write JDAT chunk(s) data */
12492           (void) WriteBlob(image,4,chunk);
12493           (void) WriteBlob(image,length,blob);
12494           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12495              (uInt) length));
12496         }
12497       blob=(unsigned char *) RelinquishMagickMemory(blob);
12498     }
12499
12500   /* Encode image as a JPEG blob */
12501   if (logging != MagickFalse)
12502     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12503       "  Creating jpeg_image_info.");
12504   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12505   if (jpeg_image_info == (ImageInfo *) NULL)
12506     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12507
12508   if (logging != MagickFalse)
12509     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12510       "  Creating jpeg_image.");
12511
12512   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12513   if (jpeg_image == (Image *) NULL)
12514     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12515   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12516
12517   (void) AcquireUniqueFilename(jpeg_image->filename);
12518   (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12519     jpeg_image->filename);
12520
12521   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12522     exception);
12523
12524   if (logging != MagickFalse)
12525     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12526       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12527       (double) jpeg_image->rows);
12528
12529   if (jng_color_type == 8 || jng_color_type == 12)
12530     jpeg_image_info->type=GrayscaleType;
12531
12532   jpeg_image_info->quality=jng_quality;
12533   jpeg_image->quality=jng_quality;
12534   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12535   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12536
12537   if (logging != MagickFalse)
12538     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12539       "  Creating blob.");
12540
12541   blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12542
12543   if (logging != MagickFalse)
12544     {
12545       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12546         "  Successfully read jpeg_image into a blob, length=%.20g.",
12547         (double) length);
12548
12549       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12550         "  Write JDAT chunk, length=%.20g.",(double) length);
12551     }
12552
12553   /* Write JDAT chunk(s) */
12554   (void) WriteBlobMSBULong(image,(size_t) length);
12555   PNGType(chunk,mng_JDAT);
12556   LogPNGChunk(logging,mng_JDAT,length);
12557   (void) WriteBlob(image,4,chunk);
12558   (void) WriteBlob(image,length,blob);
12559   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12560
12561   jpeg_image=DestroyImage(jpeg_image);
12562   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12563   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12564   blob=(unsigned char *) RelinquishMagickMemory(blob);
12565
12566   /* Write any JNG-chunk-e profiles */
12567   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12568
12569   /* Write IEND chunk */
12570   (void) WriteBlobMSBULong(image,0L);
12571   PNGType(chunk,mng_IEND);
12572   LogPNGChunk(logging,mng_IEND,0);
12573   (void) WriteBlob(image,4,chunk);
12574   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12575
12576   if (logging != MagickFalse)
12577     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12578       "  exit WriteOneJNGImage()");
12579
12580   return(status);
12581 }
12582
12583
12584 /*
12585 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12586 %                                                                             %
12587 %                                                                             %
12588 %                                                                             %
12589 %   W r i t e J N G I m a g e                                                 %
12590 %                                                                             %
12591 %                                                                             %
12592 %                                                                             %
12593 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12594 %
12595 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12596 %
12597 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
12598 %
12599 %  The format of the WriteJNGImage method is:
12600 %
12601 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12602 %        Image *image,ExceptionInfo *exception)
12603 %
12604 %  A description of each parameter follows:
12605 %
12606 %    o image_info: the image info.
12607 %
12608 %    o image:  The image.
12609 %
12610 %    o exception: return any errors or warnings in this structure.
12611 %
12612 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12613 */
12614 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12615   ExceptionInfo *exception)
12616 {
12617   MagickBooleanType
12618     have_mng_structure,
12619     logging,
12620     status;
12621
12622   MngInfo
12623     *mng_info;
12624
12625   /*
12626     Open image file.
12627   */
12628   assert(image_info != (const ImageInfo *) NULL);
12629   assert(image_info->signature == MagickSignature);
12630   assert(image != (Image *) NULL);
12631   assert(image->signature == MagickSignature);
12632   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12633   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12634   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12635   if (status == MagickFalse)
12636     return(status);
12637
12638   /*
12639     Allocate a MngInfo structure.
12640   */
12641   have_mng_structure=MagickFalse;
12642   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12643   if (mng_info == (MngInfo *) NULL)
12644     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12645   /*
12646     Initialize members of the MngInfo structure.
12647   */
12648   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12649   mng_info->image=image;
12650   have_mng_structure=MagickTrue;
12651
12652   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12653
12654   status=WriteOneJNGImage(mng_info,image_info,image,exception);
12655   (void) CloseBlob(image);
12656
12657   (void) CatchImageException(image);
12658   MngInfoFreeStruct(mng_info,&have_mng_structure);
12659   if (logging != MagickFalse)
12660     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12661   return(status);
12662 }
12663 #endif
12664
12665 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12666   ExceptionInfo *exception)
12667 {
12668   const char
12669     *option;
12670
12671   Image
12672     *next_image;
12673
12674   MagickBooleanType
12675     have_mng_structure,
12676     status;
12677
12678   volatile MagickBooleanType
12679     logging;
12680
12681   MngInfo
12682     *mng_info;
12683
12684   int
12685     image_count,
12686     need_iterations,
12687     need_matte;
12688
12689   volatile int
12690 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12691     defined(PNG_MNG_FEATURES_SUPPORTED)
12692     need_local_plte,
12693 #endif
12694     all_images_are_gray,
12695     need_defi,
12696     use_global_plte;
12697
12698   register ssize_t
12699     i;
12700
12701   unsigned char
12702     chunk[800];
12703
12704   volatile unsigned int
12705     write_jng,
12706     write_mng;
12707
12708   volatile size_t
12709     scene;
12710
12711   size_t
12712     final_delay=0,
12713     initial_delay;
12714
12715 #if (PNG_LIBPNG_VER < 10200)
12716     if (image_info->verbose)
12717       printf("Your PNG library (libpng-%s) is rather old.\n",
12718          PNG_LIBPNG_VER_STRING);
12719 #endif
12720
12721   /*
12722     Open image file.
12723   */
12724   assert(image_info != (const ImageInfo *) NULL);
12725   assert(image_info->signature == MagickSignature);
12726   assert(image != (Image *) NULL);
12727   assert(image->signature == MagickSignature);
12728   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12729   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12730   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12731   if (status == MagickFalse)
12732     return(status);
12733
12734   /*
12735     Allocate a MngInfo structure.
12736   */
12737   have_mng_structure=MagickFalse;
12738   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12739   if (mng_info == (MngInfo *) NULL)
12740     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12741   /*
12742     Initialize members of the MngInfo structure.
12743   */
12744   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12745   mng_info->image=image;
12746   have_mng_structure=MagickTrue;
12747   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12748
12749   /*
12750    * See if user has requested a specific PNG subformat to be used
12751    * for all of the PNGs in the MNG being written, e.g.,
12752    *
12753    *    convert *.png png8:animation.mng
12754    *
12755    * To do: check -define png:bit_depth and png:color_type as well,
12756    * or perhaps use mng:bit_depth and mng:color_type instead for
12757    * global settings.
12758    */
12759
12760   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12761   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12762   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12763
12764   write_jng=MagickFalse;
12765   if (image_info->compression == JPEGCompression)
12766     write_jng=MagickTrue;
12767
12768   mng_info->adjoin=image_info->adjoin &&
12769     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12770
12771   if (logging != MagickFalse)
12772     {
12773       /* Log some info about the input */
12774       Image
12775         *p;
12776
12777       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12778         "  Checking input image(s)");
12779
12780       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12781         "    Image_info depth: %.20g",(double) image_info->depth);
12782
12783       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12784         "    Type: %d",image_info->type);
12785
12786       scene=0;
12787       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12788       {
12789         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12790           "    Scene: %.20g",(double) scene++);
12791
12792         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12793           "      Image depth: %.20g",(double) p->depth);
12794
12795         if (p->alpha_trait == BlendPixelTrait)
12796           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12797             "      Matte: True");
12798
12799         else
12800           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12801             "      Matte: False");
12802
12803         if (p->storage_class == PseudoClass)
12804           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12805             "      Storage class: PseudoClass");
12806
12807         else
12808           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12809             "      Storage class: DirectClass");
12810
12811         if (p->colors)
12812           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12813             "      Number of colors: %.20g",(double) p->colors);
12814
12815         else
12816           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12817             "      Number of colors: unspecified");
12818
12819         if (mng_info->adjoin == MagickFalse)
12820           break;
12821       }
12822     }
12823
12824   use_global_plte=MagickFalse;
12825   all_images_are_gray=MagickFalse;
12826 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12827   need_local_plte=MagickTrue;
12828 #endif
12829   need_defi=MagickFalse;
12830   need_matte=MagickFalse;
12831   mng_info->framing_mode=1;
12832   mng_info->old_framing_mode=1;
12833
12834   if (write_mng)
12835       if (image_info->page != (char *) NULL)
12836         {
12837           /*
12838             Determine image bounding box.
12839           */
12840           SetGeometry(image,&mng_info->page);
12841           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12842             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12843         }
12844   if (write_mng)
12845     {
12846       unsigned int
12847         need_geom;
12848
12849       unsigned short
12850         red,
12851         green,
12852         blue;
12853
12854       mng_info->page=image->page;
12855       need_geom=MagickTrue;
12856       if (mng_info->page.width || mng_info->page.height)
12857          need_geom=MagickFalse;
12858       /*
12859         Check all the scenes.
12860       */
12861       initial_delay=image->delay;
12862       need_iterations=MagickFalse;
12863       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12864       mng_info->equal_physs=MagickTrue,
12865       mng_info->equal_gammas=MagickTrue;
12866       mng_info->equal_srgbs=MagickTrue;
12867       mng_info->equal_backgrounds=MagickTrue;
12868       image_count=0;
12869 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12870     defined(PNG_MNG_FEATURES_SUPPORTED)
12871       all_images_are_gray=MagickTrue;
12872       mng_info->equal_palettes=MagickFalse;
12873       need_local_plte=MagickFalse;
12874 #endif
12875       for (next_image=image; next_image != (Image *) NULL; )
12876       {
12877         if (need_geom)
12878           {
12879             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12880               mng_info->page.width=next_image->columns+next_image->page.x;
12881
12882             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12883               mng_info->page.height=next_image->rows+next_image->page.y;
12884           }
12885
12886         if (next_image->page.x || next_image->page.y)
12887           need_defi=MagickTrue;
12888
12889         if (next_image->alpha_trait == BlendPixelTrait)
12890           need_matte=MagickTrue;
12891
12892         if ((int) next_image->dispose >= BackgroundDispose)
12893           if ((next_image->alpha_trait == BlendPixelTrait) ||
12894                next_image->page.x || next_image->page.y ||
12895               ((next_image->columns < mng_info->page.width) &&
12896                (next_image->rows < mng_info->page.height)))
12897             mng_info->need_fram=MagickTrue;
12898
12899         if (next_image->iterations)
12900           need_iterations=MagickTrue;
12901
12902         final_delay=next_image->delay;
12903
12904         if (final_delay != initial_delay || final_delay > 1UL*
12905            next_image->ticks_per_second)
12906           mng_info->need_fram=1;
12907
12908 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12909     defined(PNG_MNG_FEATURES_SUPPORTED)
12910         /*
12911           check for global palette possibility.
12912         */
12913         if (image->alpha_trait == BlendPixelTrait)
12914            need_local_plte=MagickTrue;
12915
12916         if (need_local_plte == 0)
12917           {
12918             if (IsImageGray(image,exception) == MagickFalse)
12919               all_images_are_gray=MagickFalse;
12920             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12921             if (use_global_plte == 0)
12922               use_global_plte=mng_info->equal_palettes;
12923             need_local_plte=!mng_info->equal_palettes;
12924           }
12925 #endif
12926         if (GetNextImageInList(next_image) != (Image *) NULL)
12927           {
12928             if (next_image->background_color.red !=
12929                 next_image->next->background_color.red ||
12930                 next_image->background_color.green !=
12931                 next_image->next->background_color.green ||
12932                 next_image->background_color.blue !=
12933                 next_image->next->background_color.blue)
12934               mng_info->equal_backgrounds=MagickFalse;
12935
12936             if (next_image->gamma != next_image->next->gamma)
12937               mng_info->equal_gammas=MagickFalse;
12938
12939             if (next_image->rendering_intent !=
12940                 next_image->next->rendering_intent)
12941               mng_info->equal_srgbs=MagickFalse;
12942
12943             if ((next_image->units != next_image->next->units) ||
12944                 (next_image->resolution.x != next_image->next->resolution.x) ||
12945                 (next_image->resolution.y != next_image->next->resolution.y))
12946               mng_info->equal_physs=MagickFalse;
12947
12948             if (mng_info->equal_chrms)
12949               {
12950                 if (next_image->chromaticity.red_primary.x !=
12951                     next_image->next->chromaticity.red_primary.x ||
12952                     next_image->chromaticity.red_primary.y !=
12953                     next_image->next->chromaticity.red_primary.y ||
12954                     next_image->chromaticity.green_primary.x !=
12955                     next_image->next->chromaticity.green_primary.x ||
12956                     next_image->chromaticity.green_primary.y !=
12957                     next_image->next->chromaticity.green_primary.y ||
12958                     next_image->chromaticity.blue_primary.x !=
12959                     next_image->next->chromaticity.blue_primary.x ||
12960                     next_image->chromaticity.blue_primary.y !=
12961                     next_image->next->chromaticity.blue_primary.y ||
12962                     next_image->chromaticity.white_point.x !=
12963                     next_image->next->chromaticity.white_point.x ||
12964                     next_image->chromaticity.white_point.y !=
12965                     next_image->next->chromaticity.white_point.y)
12966                   mng_info->equal_chrms=MagickFalse;
12967               }
12968           }
12969         image_count++;
12970         next_image=GetNextImageInList(next_image);
12971       }
12972       if (image_count < 2)
12973         {
12974           mng_info->equal_backgrounds=MagickFalse;
12975           mng_info->equal_chrms=MagickFalse;
12976           mng_info->equal_gammas=MagickFalse;
12977           mng_info->equal_srgbs=MagickFalse;
12978           mng_info->equal_physs=MagickFalse;
12979           use_global_plte=MagickFalse;
12980 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12981           need_local_plte=MagickTrue;
12982 #endif
12983           need_iterations=MagickFalse;
12984         }
12985
12986      if (mng_info->need_fram == MagickFalse)
12987        {
12988          /*
12989            Only certain framing rates 100/n are exactly representable without
12990            the FRAM chunk but we'll allow some slop in VLC files
12991          */
12992          if (final_delay == 0)
12993            {
12994              if (need_iterations != MagickFalse)
12995                {
12996                  /*
12997                    It's probably a GIF with loop; don't run it *too* fast.
12998                  */
12999                  if (mng_info->adjoin)
13000                    {
13001                      final_delay=10;
13002                      (void) ThrowMagickException(exception,GetMagickModule(),
13003                        CoderWarning,
13004                        "input has zero delay between all frames; assuming",
13005                        " 10 cs `%s'","");
13006                    }
13007                }
13008              else
13009                mng_info->ticks_per_second=0;
13010            }
13011          if (final_delay != 0)
13012            mng_info->ticks_per_second=(png_uint_32)
13013               (image->ticks_per_second/final_delay);
13014          if (final_delay > 50)
13015            mng_info->ticks_per_second=2;
13016
13017          if (final_delay > 75)
13018            mng_info->ticks_per_second=1;
13019
13020          if (final_delay > 125)
13021            mng_info->need_fram=MagickTrue;
13022
13023          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13024             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13025             (final_delay != 25) && (final_delay != 50) && (final_delay !=
13026                1UL*image->ticks_per_second))
13027            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13028        }
13029
13030      if (mng_info->need_fram != MagickFalse)
13031         mng_info->ticks_per_second=1UL*image->ticks_per_second;
13032      /*
13033         If pseudocolor, we should also check to see if all the
13034         palettes are identical and write a global PLTE if they are.
13035         ../glennrp Feb 99.
13036      */
13037      /*
13038         Write the MNG version 1.0 signature and MHDR chunk.
13039      */
13040      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13041      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13042      PNGType(chunk,mng_MHDR);
13043      LogPNGChunk(logging,mng_MHDR,28L);
13044      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13045      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13046      PNGLong(chunk+12,mng_info->ticks_per_second);
13047      PNGLong(chunk+16,0L);  /* layer count=unknown */
13048      PNGLong(chunk+20,0L);  /* frame count=unknown */
13049      PNGLong(chunk+24,0L);  /* play time=unknown   */
13050      if (write_jng)
13051        {
13052          if (need_matte)
13053            {
13054              if (need_defi || mng_info->need_fram || use_global_plte)
13055                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13056
13057              else
13058                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13059            }
13060
13061          else
13062            {
13063              if (need_defi || mng_info->need_fram || use_global_plte)
13064                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13065
13066              else
13067                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13068            }
13069        }
13070
13071      else
13072        {
13073          if (need_matte)
13074            {
13075              if (need_defi || mng_info->need_fram || use_global_plte)
13076                PNGLong(chunk+28,11L);    /* simplicity=LC */
13077
13078              else
13079                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13080            }
13081
13082          else
13083            {
13084              if (need_defi || mng_info->need_fram || use_global_plte)
13085                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13086
13087              else
13088                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13089            }
13090        }
13091      (void) WriteBlob(image,32,chunk);
13092      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13093      option=GetImageOption(image_info,"mng:need-cacheoff");
13094      if (option != (const char *) NULL)
13095        {
13096          size_t
13097            length;
13098
13099          /*
13100            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13101          */
13102          PNGType(chunk,mng_nEED);
13103          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13104          (void) WriteBlobMSBULong(image,(size_t) length);
13105          LogPNGChunk(logging,mng_nEED,(size_t) length);
13106          length+=4;
13107          (void) WriteBlob(image,length,chunk);
13108          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13109        }
13110      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13111          (GetNextImageInList(image) != (Image *) NULL) &&
13112          (image->iterations != 1))
13113        {
13114          /*
13115            Write MNG TERM chunk
13116          */
13117          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13118          PNGType(chunk,mng_TERM);
13119          LogPNGChunk(logging,mng_TERM,10L);
13120          chunk[4]=3;  /* repeat animation */
13121          chunk[5]=0;  /* show last frame when done */
13122          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13123             final_delay/MagickMax(image->ticks_per_second,1)));
13124
13125          if (image->iterations == 0)
13126            PNGLong(chunk+10,PNG_UINT_31_MAX);
13127
13128          else
13129            PNGLong(chunk+10,(png_uint_32) image->iterations);
13130
13131          if (logging != MagickFalse)
13132            {
13133              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13134                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13135               final_delay/MagickMax(image->ticks_per_second,1)));
13136
13137              if (image->iterations == 0)
13138                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13139                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13140
13141              else
13142                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13143                  "     Image iterations: %.20g",(double) image->iterations);
13144            }
13145          (void) WriteBlob(image,14,chunk);
13146          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13147        }
13148      /*
13149        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13150      */
13151      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13152           mng_info->equal_srgbs)
13153        {
13154          /*
13155            Write MNG sRGB chunk
13156          */
13157          (void) WriteBlobMSBULong(image,1L);
13158          PNGType(chunk,mng_sRGB);
13159          LogPNGChunk(logging,mng_sRGB,1L);
13160
13161          if (image->rendering_intent != UndefinedIntent)
13162            chunk[4]=(unsigned char)
13163              Magick_RenderingIntent_to_PNG_RenderingIntent(
13164              (image->rendering_intent));
13165
13166          else
13167            chunk[4]=(unsigned char)
13168              Magick_RenderingIntent_to_PNG_RenderingIntent(
13169                (PerceptualIntent));
13170
13171          (void) WriteBlob(image,5,chunk);
13172          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13173          mng_info->have_write_global_srgb=MagickTrue;
13174        }
13175
13176      else
13177        {
13178          if (image->gamma && mng_info->equal_gammas)
13179            {
13180              /*
13181                 Write MNG gAMA chunk
13182              */
13183              (void) WriteBlobMSBULong(image,4L);
13184              PNGType(chunk,mng_gAMA);
13185              LogPNGChunk(logging,mng_gAMA,4L);
13186              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13187              (void) WriteBlob(image,8,chunk);
13188              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13189              mng_info->have_write_global_gama=MagickTrue;
13190            }
13191          if (mng_info->equal_chrms)
13192            {
13193              PrimaryInfo
13194                primary;
13195
13196              /*
13197                 Write MNG cHRM chunk
13198              */
13199              (void) WriteBlobMSBULong(image,32L);
13200              PNGType(chunk,mng_cHRM);
13201              LogPNGChunk(logging,mng_cHRM,32L);
13202              primary=image->chromaticity.white_point;
13203              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13204              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13205              primary=image->chromaticity.red_primary;
13206              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13207              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13208              primary=image->chromaticity.green_primary;
13209              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13210              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13211              primary=image->chromaticity.blue_primary;
13212              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13213              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13214              (void) WriteBlob(image,36,chunk);
13215              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13216              mng_info->have_write_global_chrm=MagickTrue;
13217            }
13218        }
13219      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13220        {
13221          /*
13222             Write MNG pHYs chunk
13223          */
13224          (void) WriteBlobMSBULong(image,9L);
13225          PNGType(chunk,mng_pHYs);
13226          LogPNGChunk(logging,mng_pHYs,9L);
13227
13228          if (image->units == PixelsPerInchResolution)
13229            {
13230              PNGLong(chunk+4,(png_uint_32)
13231                (image->resolution.x*100.0/2.54+0.5));
13232
13233              PNGLong(chunk+8,(png_uint_32)
13234                (image->resolution.y*100.0/2.54+0.5));
13235
13236              chunk[12]=1;
13237            }
13238
13239          else
13240            {
13241              if (image->units == PixelsPerCentimeterResolution)
13242                {
13243                  PNGLong(chunk+4,(png_uint_32)
13244                    (image->resolution.x*100.0+0.5));
13245
13246                  PNGLong(chunk+8,(png_uint_32)
13247                    (image->resolution.y*100.0+0.5));
13248
13249                  chunk[12]=1;
13250                }
13251
13252              else
13253                {
13254                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13255                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13256                  chunk[12]=0;
13257                }
13258            }
13259          (void) WriteBlob(image,13,chunk);
13260          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13261        }
13262      /*
13263        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13264        or does not cover the entire frame.
13265      */
13266      if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
13267          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13268          (image->page.width+image->page.x < mng_info->page.width))
13269          || (image->page.height && (image->page.height+image->page.y
13270          < mng_info->page.height))))
13271        {
13272          (void) WriteBlobMSBULong(image,6L);
13273          PNGType(chunk,mng_BACK);
13274          LogPNGChunk(logging,mng_BACK,6L);
13275          red=ScaleQuantumToShort(image->background_color.red);
13276          green=ScaleQuantumToShort(image->background_color.green);
13277          blue=ScaleQuantumToShort(image->background_color.blue);
13278          PNGShort(chunk+4,red);
13279          PNGShort(chunk+6,green);
13280          PNGShort(chunk+8,blue);
13281          (void) WriteBlob(image,10,chunk);
13282          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13283          if (mng_info->equal_backgrounds)
13284            {
13285              (void) WriteBlobMSBULong(image,6L);
13286              PNGType(chunk,mng_bKGD);
13287              LogPNGChunk(logging,mng_bKGD,6L);
13288              (void) WriteBlob(image,10,chunk);
13289              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13290            }
13291        }
13292
13293 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13294      if ((need_local_plte == MagickFalse) &&
13295          (image->storage_class == PseudoClass) &&
13296          (all_images_are_gray == MagickFalse))
13297        {
13298          size_t
13299            data_length;
13300
13301          /*
13302            Write MNG PLTE chunk
13303          */
13304          data_length=3*image->colors;
13305          (void) WriteBlobMSBULong(image,data_length);
13306          PNGType(chunk,mng_PLTE);
13307          LogPNGChunk(logging,mng_PLTE,data_length);
13308
13309          for (i=0; i < (ssize_t) image->colors; i++)
13310          {
13311            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13312              image->colormap[i].red) & 0xff);
13313            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13314              image->colormap[i].green) & 0xff);
13315            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13316              image->colormap[i].blue) & 0xff);
13317          }
13318
13319          (void) WriteBlob(image,data_length+4,chunk);
13320          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13321          mng_info->have_write_global_plte=MagickTrue;
13322        }
13323 #endif
13324     }
13325   scene=0;
13326   mng_info->delay=0;
13327 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13328     defined(PNG_MNG_FEATURES_SUPPORTED)
13329   mng_info->equal_palettes=MagickFalse;
13330 #endif
13331   do
13332   {
13333     if (mng_info->adjoin)
13334     {
13335 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13336     defined(PNG_MNG_FEATURES_SUPPORTED)
13337     /*
13338       If we aren't using a global palette for the entire MNG, check to
13339       see if we can use one for two or more consecutive images.
13340     */
13341     if (need_local_plte && use_global_plte && !all_images_are_gray)
13342       {
13343         if (mng_info->IsPalette)
13344           {
13345             /*
13346               When equal_palettes is true, this image has the same palette
13347               as the previous PseudoClass image
13348             */
13349             mng_info->have_write_global_plte=mng_info->equal_palettes;
13350             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13351             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13352               {
13353                 /*
13354                   Write MNG PLTE chunk
13355                 */
13356                 size_t
13357                   data_length;
13358
13359                 data_length=3*image->colors;
13360                 (void) WriteBlobMSBULong(image,data_length);
13361                 PNGType(chunk,mng_PLTE);
13362                 LogPNGChunk(logging,mng_PLTE,data_length);
13363
13364                 for (i=0; i < (ssize_t) image->colors; i++)
13365                 {
13366                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13367                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13368                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13369                 }
13370
13371                 (void) WriteBlob(image,data_length+4,chunk);
13372                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13373                    (uInt) (data_length+4)));
13374                 mng_info->have_write_global_plte=MagickTrue;
13375               }
13376           }
13377         else
13378           mng_info->have_write_global_plte=MagickFalse;
13379       }
13380 #endif
13381     if (need_defi)
13382       {
13383         ssize_t
13384           previous_x,
13385           previous_y;
13386
13387         if (scene)
13388           {
13389             previous_x=mng_info->page.x;
13390             previous_y=mng_info->page.y;
13391           }
13392         else
13393           {
13394             previous_x=0;
13395             previous_y=0;
13396           }
13397         mng_info->page=image->page;
13398         if ((mng_info->page.x !=  previous_x) ||
13399             (mng_info->page.y != previous_y))
13400           {
13401              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
13402              PNGType(chunk,mng_DEFI);
13403              LogPNGChunk(logging,mng_DEFI,12L);
13404              chunk[4]=0; /* object 0 MSB */
13405              chunk[5]=0; /* object 0 LSB */
13406              chunk[6]=0; /* visible  */
13407              chunk[7]=0; /* abstract */
13408              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13409              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13410              (void) WriteBlob(image,16,chunk);
13411              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13412           }
13413       }
13414     }
13415
13416    mng_info->write_mng=write_mng;
13417
13418    if ((int) image->dispose >= 3)
13419      mng_info->framing_mode=3;
13420
13421    if (mng_info->need_fram && mng_info->adjoin &&
13422        ((image->delay != mng_info->delay) ||
13423         (mng_info->framing_mode != mng_info->old_framing_mode)))
13424      {
13425        if (image->delay == mng_info->delay)
13426          {
13427            /*
13428              Write a MNG FRAM chunk with the new framing mode.
13429            */
13430            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
13431            PNGType(chunk,mng_FRAM);
13432            LogPNGChunk(logging,mng_FRAM,1L);
13433            chunk[4]=(unsigned char) mng_info->framing_mode;
13434            (void) WriteBlob(image,5,chunk);
13435            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13436          }
13437        else
13438          {
13439            /*
13440              Write a MNG FRAM chunk with the delay.
13441            */
13442            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13443            PNGType(chunk,mng_FRAM);
13444            LogPNGChunk(logging,mng_FRAM,10L);
13445            chunk[4]=(unsigned char) mng_info->framing_mode;
13446            chunk[5]=0;  /* frame name separator (no name) */
13447            chunk[6]=2;  /* flag for changing default delay */
13448            chunk[7]=0;  /* flag for changing frame timeout */
13449            chunk[8]=0;  /* flag for changing frame clipping */
13450            chunk[9]=0;  /* flag for changing frame sync_id */
13451            PNGLong(chunk+10,(png_uint_32)
13452              ((mng_info->ticks_per_second*
13453              image->delay)/MagickMax(image->ticks_per_second,1)));
13454            (void) WriteBlob(image,14,chunk);
13455            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13456            mng_info->delay=(png_uint_32) image->delay;
13457          }
13458        mng_info->old_framing_mode=mng_info->framing_mode;
13459      }
13460
13461 #if defined(JNG_SUPPORTED)
13462    if (image_info->compression == JPEGCompression)
13463      {
13464        ImageInfo
13465          *write_info;
13466
13467        if (logging != MagickFalse)
13468          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13469            "  Writing JNG object.");
13470        /* To do: specify the desired alpha compression method. */
13471        write_info=CloneImageInfo(image_info);
13472        write_info->compression=UndefinedCompression;
13473        status=WriteOneJNGImage(mng_info,write_info,image,exception);
13474        write_info=DestroyImageInfo(write_info);
13475      }
13476    else
13477 #endif
13478      {
13479        if (logging != MagickFalse)
13480          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13481            "  Writing PNG object.");
13482
13483        mng_info->need_blob = MagickFalse;
13484        mng_info->ping_preserve_colormap = MagickFalse;
13485
13486        /* We don't want any ancillary chunks written */
13487        mng_info->ping_exclude_bKGD=MagickTrue;
13488        mng_info->ping_exclude_cHRM=MagickTrue;
13489        mng_info->ping_exclude_date=MagickTrue;
13490        mng_info->ping_exclude_EXIF=MagickTrue;
13491        mng_info->ping_exclude_gAMA=MagickTrue;
13492        mng_info->ping_exclude_iCCP=MagickTrue;
13493        /* mng_info->ping_exclude_iTXt=MagickTrue; */
13494        mng_info->ping_exclude_oFFs=MagickTrue;
13495        mng_info->ping_exclude_pHYs=MagickTrue;
13496        mng_info->ping_exclude_sRGB=MagickTrue;
13497        mng_info->ping_exclude_tEXt=MagickTrue;
13498        mng_info->ping_exclude_tRNS=MagickTrue;
13499        mng_info->ping_exclude_vpAg=MagickTrue;
13500        mng_info->ping_exclude_zCCP=MagickTrue;
13501        mng_info->ping_exclude_zTXt=MagickTrue;
13502
13503        status=WriteOnePNGImage(mng_info,image_info,image,exception);
13504      }
13505
13506     if (status == MagickFalse)
13507       {
13508         MngInfoFreeStruct(mng_info,&have_mng_structure);
13509         (void) CloseBlob(image);
13510         return(MagickFalse);
13511       }
13512     (void) CatchImageException(image);
13513     if (GetNextImageInList(image) == (Image *) NULL)
13514       break;
13515     image=SyncNextImageInList(image);
13516     status=SetImageProgress(image,SaveImagesTag,scene++,
13517       GetImageListLength(image));
13518
13519     if (status == MagickFalse)
13520       break;
13521
13522   } while (mng_info->adjoin);
13523
13524   if (write_mng)
13525     {
13526       while (GetPreviousImageInList(image) != (Image *) NULL)
13527         image=GetPreviousImageInList(image);
13528       /*
13529         Write the MEND chunk.
13530       */
13531       (void) WriteBlobMSBULong(image,0x00000000L);
13532       PNGType(chunk,mng_MEND);
13533       LogPNGChunk(logging,mng_MEND,0L);
13534       (void) WriteBlob(image,4,chunk);
13535       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13536     }
13537   /*
13538     Relinquish resources.
13539   */
13540   (void) CloseBlob(image);
13541   MngInfoFreeStruct(mng_info,&have_mng_structure);
13542
13543   if (logging != MagickFalse)
13544     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13545
13546   return(MagickTrue);
13547 }
13548 #else /* PNG_LIBPNG_VER > 10011 */
13549
13550 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13551 {
13552   (void) image;
13553   printf("Your PNG library is too old: You have libpng-%s\n",
13554      PNG_LIBPNG_VER_STRING);
13555
13556   ThrowBinaryException(CoderError,"PNG library is too old",
13557      image_info->filename);
13558 }
13559
13560 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13561 {
13562   return(WritePNGImage(image_info,image));
13563 }
13564 #endif /* PNG_LIBPNG_VER > 10011 */
13565 #endif