]> granicus.if.org Git - imagemagick/blob - coders/png.c
Avoid using NULL alpha_image in the JNG decoder.
[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 %                                   Cristy                                    %
17 %                           Glenn Randers-Pehrson                             %
18 %                               November 1997                                 %
19 %                                                                             %
20 %                                                                             %
21 %  Copyright 1999-2015 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 /* Table of recognized sRGB ICC profiles */
133 struct sRGB_info_struct
134 {
135     png_uint_32 len;
136     png_uint_32 crc;
137     png_byte intent;
138 };
139
140 const struct sRGB_info_struct sRGB_info[] =
141 {
142     /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
143     { 3048, 0x3b8772b9UL, 0},
144
145     /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
146     { 3052, 0x427ebb21UL, 1},
147
148     /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
149     {60988, 0x306fd8aeUL, 0},
150
151     /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
152      {60960, 0xbbef7812UL, 0},
153
154     /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
155      { 3024, 0x5d5129ceUL, 1},
156
157      /* HP-Microsoft sRGB v2 perceptual */
158      { 3144, 0x182ea552UL, 0},
159
160      /* HP-Microsoft sRGB v2 media-relative */
161      { 3144, 0xf29e526dUL, 1},
162
163      /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
164      {  524, 0xd4938c39UL, 0},
165
166      /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
167      { 3212, 0x034af5a1UL, 0},
168
169      /* Not recognized */
170      {    0, 0x00000000UL, 0},
171 };
172
173 /* Macros for left-bit-replication to ensure that pixels
174  * and PixelInfos all have the same image->depth, and for use
175  * in PNG8 quantization.
176  */
177
178 /* LBR01: Replicate top bit */
179
180 #define LBR01PacketRed(pixelpacket) \
181      (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
182         0 : QuantumRange);
183
184 #define LBR01PacketGreen(pixelpacket) \
185      (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
186         0 : QuantumRange);
187
188 #define LBR01PacketBlue(pixelpacket) \
189      (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
190         0 : QuantumRange);
191
192 #define LBR01PacketAlpha(pixelpacket) \
193      (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
194         0 : QuantumRange);
195
196 #define LBR01PacketRGB(pixelpacket) \
197         { \
198         LBR01PacketRed((pixelpacket)); \
199         LBR01PacketGreen((pixelpacket)); \
200         LBR01PacketBlue((pixelpacket)); \
201         }
202
203 #define LBR01PacketRGBO(pixelpacket) \
204         { \
205         LBR01PacketRGB((pixelpacket)); \
206         LBR01PacketAlpha((pixelpacket)); \
207         }
208
209 #define LBR01PixelRed(pixel) \
210         (SetPixelRed(image, \
211         ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
212         0 : QuantumRange,(pixel)));
213
214 #define LBR01PixelGreen(pixel) \
215         (SetPixelGreen(image, \
216         ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
217         0 : QuantumRange,(pixel)));
218
219 #define LBR01PixelBlue(pixel) \
220         (SetPixelBlue(image, \
221         ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
222         0 : QuantumRange,(pixel)));
223
224 #define LBR01PixelAlpha(pixel) \
225         (SetPixelAlpha(image, \
226         ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
227         0 : QuantumRange,(pixel)));
228
229 #define LBR01PixelRGB(pixel) \
230         { \
231         LBR01PixelRed((pixel)); \
232         LBR01PixelGreen((pixel)); \
233         LBR01PixelBlue((pixel)); \
234         }
235
236 #define LBR01PixelRGBA(pixel) \
237         { \
238         LBR01PixelRGB((pixel)); \
239         LBR01PixelAlpha((pixel)); \
240         }
241
242 /* LBR02: Replicate top 2 bits */
243
244 #define LBR02PacketRed(pixelpacket) \
245    { \
246      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
247      (pixelpacket).red=ScaleCharToQuantum( \
248        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
249    }
250 #define LBR02PacketGreen(pixelpacket) \
251    { \
252      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
253      (pixelpacket).green=ScaleCharToQuantum( \
254        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
255    }
256 #define LBR02PacketBlue(pixelpacket) \
257    { \
258      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
259      (pixelpacket).blue=ScaleCharToQuantum( \
260        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
261    }
262 #define LBR02PacketAlpha(pixelpacket) \
263    { \
264      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
265      (pixelpacket).alpha=ScaleCharToQuantum( \
266        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
267    }
268
269 #define LBR02PacketRGB(pixelpacket) \
270         { \
271         LBR02PacketRed((pixelpacket)); \
272         LBR02PacketGreen((pixelpacket)); \
273         LBR02PacketBlue((pixelpacket)); \
274         }
275
276 #define LBR02PacketRGBO(pixelpacket) \
277         { \
278         LBR02PacketRGB((pixelpacket)); \
279         LBR02PacketAlpha((pixelpacket)); \
280         }
281
282 #define LBR02PixelRed(pixel) \
283    { \
284      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
285        & 0xc0; \
286      SetPixelRed(image, ScaleCharToQuantum( \
287        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
288        (pixel)); \
289    }
290 #define LBR02PixelGreen(pixel) \
291    { \
292      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
293        & 0xc0; \
294      SetPixelGreen(image, ScaleCharToQuantum( \
295        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
296        (pixel)); \
297    }
298 #define LBR02PixelBlue(pixel) \
299    { \
300      unsigned char lbr_bits= \
301        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
302      SetPixelBlue(image, ScaleCharToQuantum( \
303        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
304        (pixel)); \
305    }
306 #define LBR02PixelAlpha(pixel) \
307    { \
308      unsigned char lbr_bits= \
309        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
310      SetPixelAlpha(image, ScaleCharToQuantum( \
311        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
312        (pixel) ); \
313    }
314
315 #define LBR02PixelRGB(pixel) \
316         { \
317         LBR02PixelRed((pixel)); \
318         LBR02PixelGreen((pixel)); \
319         LBR02PixelBlue((pixel)); \
320         }
321
322 #define LBR02PixelRGBA(pixel) \
323         { \
324         LBR02PixelRGB((pixel)); \
325         LBR02PixelAlpha((pixel)); \
326         }
327
328 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
329    PNG8 quantization) */
330
331 #define LBR03PacketRed(pixelpacket) \
332    { \
333      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
334      (pixelpacket).red=ScaleCharToQuantum( \
335        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
336    }
337 #define LBR03PacketGreen(pixelpacket) \
338    { \
339      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
340      (pixelpacket).green=ScaleCharToQuantum( \
341        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
342    }
343 #define LBR03PacketBlue(pixelpacket) \
344    { \
345      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
346      (pixelpacket).blue=ScaleCharToQuantum( \
347        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
348    }
349
350 #define LBR03PacketRGB(pixelpacket) \
351         { \
352         LBR03PacketRed((pixelpacket)); \
353         LBR03PacketGreen((pixelpacket)); \
354         LBR03PacketBlue((pixelpacket)); \
355         }
356
357 #define LBR03PixelRed(pixel) \
358    { \
359      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
360        & 0xe0; \
361      SetPixelRed(image, ScaleCharToQuantum( \
362        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
363    }
364 #define LBR03Green(pixel) \
365    { \
366      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
367        & 0xe0; \
368      SetPixelGreen(image, ScaleCharToQuantum( \
369        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
370    }
371 #define LBR03Blue(pixel) \
372    { \
373      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
374        & 0xe0; \
375      SetPixelBlue(image, ScaleCharToQuantum( \
376        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
377    }
378
379 #define LBR03RGB(pixel) \
380         { \
381         LBR03PixelRed((pixel)); \
382         LBR03Green((pixel)); \
383         LBR03Blue((pixel)); \
384         }
385
386 /* LBR04: Replicate top 4 bits */
387
388 #define LBR04PacketRed(pixelpacket) \
389    { \
390      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
391      (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
392    }
393 #define LBR04PacketGreen(pixelpacket) \
394    { \
395      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
396      (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
397    }
398 #define LBR04PacketBlue(pixelpacket) \
399    { \
400      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
401      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
402    }
403 #define LBR04PacketAlpha(pixelpacket) \
404    { \
405      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
406      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
407    }
408
409 #define LBR04PacketRGB(pixelpacket) \
410         { \
411         LBR04PacketRed((pixelpacket)); \
412         LBR04PacketGreen((pixelpacket)); \
413         LBR04PacketBlue((pixelpacket)); \
414         }
415
416 #define LBR04PacketRGBO(pixelpacket) \
417         { \
418         LBR04PacketRGB((pixelpacket)); \
419         LBR04PacketAlpha((pixelpacket)); \
420         }
421
422 #define LBR04PixelRed(pixel) \
423    { \
424      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
425        & 0xf0; \
426      SetPixelRed(image,\
427        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
428    }
429 #define LBR04PixelGreen(pixel) \
430    { \
431      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
432        & 0xf0; \
433      SetPixelGreen(image,\
434        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
435    }
436 #define LBR04PixelBlue(pixel) \
437    { \
438      unsigned char lbr_bits= \
439        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
440      SetPixelBlue(image,\
441        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
442    }
443 #define LBR04PixelAlpha(pixel) \
444    { \
445      unsigned char lbr_bits= \
446        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
447      SetPixelAlpha(image,\
448        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
449    }
450
451 #define LBR04PixelRGB(pixel) \
452         { \
453         LBR04PixelRed((pixel)); \
454         LBR04PixelGreen((pixel)); \
455         LBR04PixelBlue((pixel)); \
456         }
457
458 #define LBR04PixelRGBA(pixel) \
459         { \
460         LBR04PixelRGB((pixel)); \
461         LBR04PixelAlpha((pixel)); \
462         }
463
464 /*
465   Establish thread safety.
466   setjmp/longjmp is claimed to be safe on these platforms:
467   setjmp/longjmp is alleged to be unsafe on these platforms:
468 */
469 #ifdef PNG_SETJMP_SUPPORTED
470 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
471 #   define IMPNG_SETJMP_NOT_THREAD_SAFE
472 # endif
473
474 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
475 static SemaphoreInfo
476   *ping_semaphore = (SemaphoreInfo *) NULL;
477 # endif
478 #endif
479
480 /*
481   This temporary until I set up malloc'ed object attributes array.
482   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
483   waste more memory.
484 */
485 #define MNG_MAX_OBJECTS 256
486
487 /*
488   If this not defined, spec is interpreted strictly.  If it is
489   defined, an attempt will be made to recover from some errors,
490   including
491       o global PLTE too short
492 */
493 #undef MNG_LOOSE
494
495 /*
496   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
497   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
498   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
499   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
500   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
501   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
502   will be enabled by default in libpng-1.2.0.
503 */
504 #ifdef PNG_MNG_FEATURES_SUPPORTED
505 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
506 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
507 #  endif
508 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
509 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
510 #  endif
511 #endif
512
513 /*
514   Maximum valid size_t in PNG/MNG chunks is (2^31)-1
515   This macro is only defined in libpng-1.0.3 and later.
516   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
517 */
518 #ifndef PNG_UINT_31_MAX
519 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
520 #endif
521
522 /*
523   Constant strings for known chunk types.  If you need to add a chunk,
524   add a string holding the name here.   To make the code more
525   portable, we use ASCII numbers like this, not characters.
526 */
527
528 static png_byte mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
529 static png_byte mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
530 static png_byte mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
531 static png_byte mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
532 static png_byte mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
533 static png_byte mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
534 static png_byte mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
535 static png_byte mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
536 static png_byte mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
537 static png_byte mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
538 static png_byte mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
539 static png_byte mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
540 static png_byte mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
541 static png_byte mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
542 static png_byte mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
543 static png_byte mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
544 static png_byte mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
545 static png_byte mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
546 static png_byte mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
547 static png_byte mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
548 static png_byte mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
549 static png_byte mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
550 static png_byte mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
551 static png_byte mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
552 static png_byte mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
553 static png_byte mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
554 static png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
555 static png_byte mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
556 static png_byte mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
557 static png_byte mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
558 static png_byte mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
559 static png_byte mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
560 static png_byte mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
561 static png_byte mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
562
563 #if defined(JNG_SUPPORTED)
564 static png_byte mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
565 static png_byte mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
566 static png_byte mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
567 static png_byte mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
568 static png_byte mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
569 static png_byte mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
570 #endif
571
572 #if 0
573 /* Other known chunks that are not yet supported by ImageMagick: */
574 static png_byte mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
575 static png_byte mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
576 static png_byte mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
577 static png_byte mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
578 static png_byte mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
579 static png_byte mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
580 static png_byte mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
581 #endif
582
583 typedef struct _MngBox
584 {
585   long
586     left,
587     right,
588     top,
589     bottom;
590 } MngBox;
591
592 typedef struct _MngPair
593 {
594   volatile long
595     a,
596     b;
597 } MngPair;
598
599 #ifdef MNG_OBJECT_BUFFERS
600 typedef struct _MngBuffer
601 {
602
603   size_t
604     height,
605     width;
606
607   Image
608     *image;
609
610   png_color
611     plte[256];
612
613   int
614     reference_count;
615
616   unsigned char
617     alpha_sample_depth,
618     compression_method,
619     color_type,
620     concrete,
621     filter_method,
622     frozen,
623     image_type,
624     interlace_method,
625     pixel_sample_depth,
626     plte_length,
627     sample_depth,
628     viewable;
629 } MngBuffer;
630 #endif
631
632 typedef struct _MngInfo
633 {
634
635 #ifdef MNG_OBJECT_BUFFERS
636   MngBuffer
637     *ob[MNG_MAX_OBJECTS];
638 #endif
639
640   Image *
641     image;
642
643   RectangleInfo
644     page;
645
646   int
647     adjoin,
648 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
649     bytes_in_read_buffer,
650     found_empty_plte,
651 #endif
652     equal_backgrounds,
653     equal_chrms,
654     equal_gammas,
655 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
656     defined(PNG_MNG_FEATURES_SUPPORTED)
657     equal_palettes,
658 #endif
659     equal_physs,
660     equal_srgbs,
661     framing_mode,
662     have_global_bkgd,
663     have_global_chrm,
664     have_global_gama,
665     have_global_phys,
666     have_global_sbit,
667     have_global_srgb,
668     have_saved_bkgd_index,
669     have_write_global_chrm,
670     have_write_global_gama,
671     have_write_global_plte,
672     have_write_global_srgb,
673     need_fram,
674     object_id,
675     old_framing_mode,
676     saved_bkgd_index;
677
678   int
679     new_number_colors;
680
681   ssize_t
682     image_found,
683     loop_count[256],
684     loop_iteration[256],
685     scenes_found,
686     x_off[MNG_MAX_OBJECTS],
687     y_off[MNG_MAX_OBJECTS];
688
689   MngBox
690     clip,
691     frame,
692     image_box,
693     object_clip[MNG_MAX_OBJECTS];
694
695   unsigned char
696     /* These flags could be combined into one byte */
697     exists[MNG_MAX_OBJECTS],
698     frozen[MNG_MAX_OBJECTS],
699     loop_active[256],
700     invisible[MNG_MAX_OBJECTS],
701     viewable[MNG_MAX_OBJECTS];
702
703   MagickOffsetType
704     loop_jump[256];
705
706   png_colorp
707     global_plte;
708
709   png_color_8
710     global_sbit;
711
712   png_byte
713 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
714     read_buffer[8],
715 #endif
716     global_trns[256];
717
718   float
719     global_gamma;
720
721   ChromaticityInfo
722     global_chrm;
723
724   RenderingIntent
725     global_srgb_intent;
726
727   unsigned int
728     delay,
729     global_plte_length,
730     global_trns_length,
731     global_x_pixels_per_unit,
732     global_y_pixels_per_unit,
733     mng_width,
734     mng_height,
735     ticks_per_second;
736
737   MagickBooleanType
738     need_blob;
739
740   unsigned int
741     IsPalette,
742     global_phys_unit_type,
743     basi_warning,
744     clon_warning,
745     dhdr_warning,
746     jhdr_warning,
747     magn_warning,
748     past_warning,
749     phyg_warning,
750     phys_warning,
751     sbit_warning,
752     show_warning,
753     mng_type,
754     write_mng,
755     write_png_colortype,
756     write_png_depth,
757     write_png_compression_level,
758     write_png_compression_strategy,
759     write_png_compression_filter,
760     write_png8,
761     write_png24,
762     write_png32,
763     write_png48,
764     write_png64;
765
766 #ifdef MNG_BASI_SUPPORTED
767   size_t
768     basi_width,
769     basi_height;
770
771   unsigned int
772     basi_depth,
773     basi_color_type,
774     basi_compression_method,
775     basi_filter_type,
776     basi_interlace_method,
777     basi_red,
778     basi_green,
779     basi_blue,
780     basi_alpha,
781     basi_viewable;
782 #endif
783
784   png_uint_16
785     magn_first,
786     magn_last,
787     magn_mb,
788     magn_ml,
789     magn_mr,
790     magn_mt,
791     magn_mx,
792     magn_my,
793     magn_methx,
794     magn_methy;
795
796   PixelInfo
797     mng_global_bkgd;
798
799   /* Added at version 6.6.6-7 */
800   MagickBooleanType
801     ping_exclude_bKGD,
802     ping_exclude_cHRM,
803     ping_exclude_date,
804     ping_exclude_EXIF,
805     ping_exclude_gAMA,
806     ping_exclude_iCCP,
807     /* ping_exclude_iTXt, */
808     ping_exclude_oFFs,
809     ping_exclude_pHYs,
810     ping_exclude_sRGB,
811     ping_exclude_tEXt,
812     ping_exclude_tRNS,
813     ping_exclude_vpAg,
814     ping_exclude_zCCP, /* hex-encoded iCCP */
815     ping_exclude_zTXt,
816     ping_preserve_colormap,
817   /* Added at version 6.8.5-7 */
818     ping_preserve_iCCP,
819   /* Added at version 6.8.9-9 */
820     ping_exclude_tIME;
821
822 } MngInfo;
823 #endif /* VER */
824 \f
825 /*
826   Forward declarations.
827 */
828 static MagickBooleanType
829   WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
830
831 static MagickBooleanType
832   WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
833
834 #if defined(JNG_SUPPORTED)
835 static MagickBooleanType
836   WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
837 #endif
838
839 #if PNG_LIBPNG_VER > 10011
840
841
842 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
843 static MagickBooleanType
844 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
845 {
846     /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
847      *
848      * This is true if the high byte and the next highest byte of
849      * each sample of the image, the colormap, and the background color
850      * are equal to each other.  We check this by seeing if the samples
851      * are unchanged when we scale them down to 8 and back up to Quantum.
852      *
853      * We don't use the method GetImageDepth() because it doesn't check
854      * background and doesn't handle PseudoClass specially.
855      */
856
857 #define QuantumToCharToQuantumEqQuantum(quantum) \
858   ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
859
860     MagickBooleanType
861       ok_to_reduce=MagickFalse;
862
863     if (image->depth >= 16)
864       {
865
866         const Quantum
867           *p;
868
869         ok_to_reduce=
870            QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
871            QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
872            QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
873            MagickTrue : MagickFalse;
874
875         if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
876           {
877             int indx;
878
879             for (indx=0; indx < (ssize_t) image->colors; indx++)
880               {
881                 ok_to_reduce=(
882                    QuantumToCharToQuantumEqQuantum(
883                    image->colormap[indx].red) &&
884                    QuantumToCharToQuantumEqQuantum(
885                    image->colormap[indx].green) &&
886                    QuantumToCharToQuantumEqQuantum(
887                    image->colormap[indx].blue)) ?
888                    MagickTrue : MagickFalse;
889
890                 if (ok_to_reduce == MagickFalse)
891                    break;
892               }
893           }
894
895         if ((ok_to_reduce != MagickFalse) &&
896             (image->storage_class != PseudoClass))
897           {
898             ssize_t
899               y;
900
901             register ssize_t
902               x;
903
904             for (y=0; y < (ssize_t) image->rows; y++)
905             {
906               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
907
908               if (p == (const Quantum *) NULL)
909                 {
910                   ok_to_reduce = MagickFalse;
911                   break;
912                 }
913
914               for (x=(ssize_t) image->columns-1; x >= 0; x--)
915               {
916                 ok_to_reduce=
917                    QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
918                    QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
919                    QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
920                    MagickTrue : MagickFalse;
921
922                 if (ok_to_reduce == MagickFalse)
923                   break;
924
925                 p+=GetPixelChannels(image);
926               }
927               if (x >= 0)
928                 break;
929             }
930           }
931
932         if (ok_to_reduce != MagickFalse)
933           {
934             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
935                 "    OK to reduce PNG bit depth to 8 without loss of info");
936           }
937         else
938           {
939             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
940                 "    Not OK to reduce PNG bit depth to 8 without loss of info");
941           }
942       }
943
944     return ok_to_reduce;
945 }
946 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
947
948 static const char* PngColorTypeToString(const unsigned int color_type)
949 {
950   const char
951     *result = "Unknown";
952
953   switch (color_type)
954     {
955     case PNG_COLOR_TYPE_GRAY:
956       result = "Gray";
957       break;
958     case PNG_COLOR_TYPE_GRAY_ALPHA:
959       result = "Gray+Alpha";
960       break;
961     case PNG_COLOR_TYPE_PALETTE:
962       result = "Palette";
963       break;
964     case PNG_COLOR_TYPE_RGB:
965       result = "RGB";
966       break;
967     case PNG_COLOR_TYPE_RGB_ALPHA:
968       result = "RGB+Alpha";
969       break;
970     }
971
972   return result;
973 }
974
975 static int
976 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
977 {
978   switch (intent)
979   {
980     case PerceptualIntent:
981        return 0;
982
983     case RelativeIntent:
984        return 1;
985
986     case SaturationIntent:
987        return 2;
988
989     case AbsoluteIntent:
990        return 3;
991
992     default:
993        return -1;
994   }
995 }
996
997 static RenderingIntent
998 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
999 {
1000   switch (ping_intent)
1001   {
1002     case 0:
1003       return PerceptualIntent;
1004
1005     case 1:
1006       return RelativeIntent;
1007
1008     case 2:
1009       return SaturationIntent;
1010
1011     case 3:
1012       return AbsoluteIntent;
1013
1014     default:
1015       return UndefinedIntent;
1016     }
1017 }
1018
1019 static const char *
1020 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1021 {
1022   switch (ping_intent)
1023   {
1024     case 0:
1025       return "Perceptual Intent";
1026
1027     case 1:
1028       return "Relative Intent";
1029
1030     case 2:
1031       return "Saturation Intent";
1032
1033     case 3:
1034       return "Absolute Intent";
1035
1036     default:
1037       return "Undefined Intent";
1038     }
1039 }
1040
1041 static const char *
1042 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1043 {
1044   switch (ping_colortype)
1045   {
1046     case 0:
1047       return "Grayscale";
1048
1049     case 2:
1050       return "Truecolor";
1051
1052     case 3:
1053       return "Indexed";
1054
1055     case 4:
1056       return "GrayAlpha";
1057
1058     case 6:
1059       return "RGBA";
1060
1061     default:
1062       return "UndefinedColorType";
1063     }
1064 }
1065
1066 #endif /* PNG_LIBPNG_VER > 10011 */
1067 #endif /* MAGICKCORE_PNG_DELEGATE */
1068 \f
1069 /*
1070 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1071 %                                                                             %
1072 %                                                                             %
1073 %                                                                             %
1074 %   I s M N G                                                                 %
1075 %                                                                             %
1076 %                                                                             %
1077 %                                                                             %
1078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1079 %
1080 %  IsMNG() returns MagickTrue if the image format type, identified by the
1081 %  magick string, is MNG.
1082 %
1083 %  The format of the IsMNG method is:
1084 %
1085 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1086 %
1087 %  A description of each parameter follows:
1088 %
1089 %    o magick: compare image format pattern against these bytes.
1090 %
1091 %    o length: Specifies the length of the magick string.
1092 %
1093 %
1094 */
1095 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1096 {
1097   if (length < 8)
1098     return(MagickFalse);
1099
1100   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1101     return(MagickTrue);
1102
1103   return(MagickFalse);
1104 }
1105 \f
1106 /*
1107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1108 %                                                                             %
1109 %                                                                             %
1110 %                                                                             %
1111 %   I s J N G                                                                 %
1112 %                                                                             %
1113 %                                                                             %
1114 %                                                                             %
1115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1116 %
1117 %  IsJNG() returns MagickTrue if the image format type, identified by the
1118 %  magick string, is JNG.
1119 %
1120 %  The format of the IsJNG method is:
1121 %
1122 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1123 %
1124 %  A description of each parameter follows:
1125 %
1126 %    o magick: compare image format pattern against these bytes.
1127 %
1128 %    o length: Specifies the length of the magick string.
1129 %
1130 %
1131 */
1132 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1133 {
1134   if (length < 8)
1135     return(MagickFalse);
1136
1137   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1138     return(MagickTrue);
1139
1140   return(MagickFalse);
1141 }
1142 \f
1143 /*
1144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1145 %                                                                             %
1146 %                                                                             %
1147 %                                                                             %
1148 %   I s P N G                                                                 %
1149 %                                                                             %
1150 %                                                                             %
1151 %                                                                             %
1152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1153 %
1154 %  IsPNG() returns MagickTrue if the image format type, identified by the
1155 %  magick string, is PNG.
1156 %
1157 %  The format of the IsPNG method is:
1158 %
1159 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1160 %
1161 %  A description of each parameter follows:
1162 %
1163 %    o magick: compare image format pattern against these bytes.
1164 %
1165 %    o length: Specifies the length of the magick string.
1166 %
1167 */
1168 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1169 {
1170   if (length < 8)
1171     return(MagickFalse);
1172
1173   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1174     return(MagickTrue);
1175
1176   return(MagickFalse);
1177 }
1178 \f
1179 #if defined(MAGICKCORE_PNG_DELEGATE)
1180 #if defined(__cplusplus) || defined(c_plusplus)
1181 extern "C" {
1182 #endif
1183
1184 #if (PNG_LIBPNG_VER > 10011)
1185 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1186 {
1187   unsigned char
1188     buffer[4];
1189
1190   assert(image != (Image *) NULL);
1191   assert(image->signature == MagickSignature);
1192   buffer[0]=(unsigned char) (value >> 24);
1193   buffer[1]=(unsigned char) (value >> 16);
1194   buffer[2]=(unsigned char) (value >> 8);
1195   buffer[3]=(unsigned char) value;
1196   return((size_t) WriteBlob(image,4,buffer));
1197 }
1198
1199 static void PNGLong(png_bytep p,png_uint_32 value)
1200 {
1201   *p++=(png_byte) ((value >> 24) & 0xff);
1202   *p++=(png_byte) ((value >> 16) & 0xff);
1203   *p++=(png_byte) ((value >> 8) & 0xff);
1204   *p++=(png_byte) (value & 0xff);
1205 }
1206
1207 #if defined(JNG_SUPPORTED)
1208 static void PNGsLong(png_bytep p,png_int_32 value)
1209 {
1210   *p++=(png_byte) ((value >> 24) & 0xff);
1211   *p++=(png_byte) ((value >> 16) & 0xff);
1212   *p++=(png_byte) ((value >> 8) & 0xff);
1213   *p++=(png_byte) (value & 0xff);
1214 }
1215 #endif
1216
1217 static void PNGShort(png_bytep p,png_uint_16 value)
1218 {
1219   *p++=(png_byte) ((value >> 8) & 0xff);
1220   *p++=(png_byte) (value & 0xff);
1221 }
1222
1223 static void PNGType(png_bytep p,png_bytep type)
1224 {
1225   (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1226 }
1227
1228 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1229    size_t length)
1230 {
1231   if (logging != MagickFalse)
1232     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1233       "  Writing %c%c%c%c chunk, length: %.20g",
1234       type[0],type[1],type[2],type[3],(double) length);
1235 }
1236 #endif /* PNG_LIBPNG_VER > 10011 */
1237
1238 #if defined(__cplusplus) || defined(c_plusplus)
1239 }
1240 #endif
1241
1242 #if PNG_LIBPNG_VER > 10011
1243 /*
1244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245 %                                                                             %
1246 %                                                                             %
1247 %                                                                             %
1248 %   R e a d P N G I m a g e                                                   %
1249 %                                                                             %
1250 %                                                                             %
1251 %                                                                             %
1252 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253 %
1254 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1255 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1256 %  allocates the memory necessary for the new Image structure and returns a
1257 %  pointer to the new image or set of images.
1258 %
1259 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1260 %
1261 %  The format of the ReadPNGImage method is:
1262 %
1263 %      Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1264 %
1265 %  A description of each parameter follows:
1266 %
1267 %    o image_info: the image info.
1268 %
1269 %    o exception: return any errors or warnings in this structure.
1270 %
1271 %  To do, more or less in chronological order (as of version 5.5.2,
1272 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1273 %
1274 %    Get 16-bit cheap transparency working.
1275 %
1276 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1277 %
1278 %    Preserve all unknown and not-yet-handled known chunks found in input
1279 %    PNG file and copy them into output PNG files according to the PNG
1280 %    copying rules.
1281 %
1282 %    (At this point, PNG encoding should be in full MNG compliance)
1283 %
1284 %    Provide options for choice of background to use when the MNG BACK
1285 %    chunk is not present or is not mandatory (i.e., leave transparent,
1286 %    user specified, MNG BACK, PNG bKGD)
1287 %
1288 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1289 %    efficiently by linking in the duplicate frames.].
1290 %
1291 %    Decode and act on the MHDR simplicity profile (offer option to reject
1292 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1293 %
1294 %    Upgrade to full MNG without Delta-PNG.
1295 %
1296 %        o  BACK [done a while ago except for background image ID]
1297 %        o  MOVE [done 15 May 1999]
1298 %        o  CLIP [done 15 May 1999]
1299 %        o  DISC [done 19 May 1999]
1300 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1301 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1302 %        o  SHOW
1303 %        o  PAST
1304 %        o  BASI
1305 %        o  MNG-level tEXt/iTXt/zTXt
1306 %        o  pHYg
1307 %        o  pHYs
1308 %        o  sBIT
1309 %        o  bKGD
1310 %        o  iTXt (wait for libpng implementation).
1311 %
1312 %    Use the scene signature to discover when an identical scene is
1313 %    being reused, and just point to the original image->exception instead
1314 %    of storing another set of pixels.  This not specific to MNG
1315 %    but could be applied generally.
1316 %
1317 %    Upgrade to full MNG with Delta-PNG.
1318 %
1319 %    JNG tEXt/iTXt/zTXt
1320 %
1321 %    We will not attempt to read files containing the CgBI chunk.
1322 %    They are really Xcode files meant for display on the iPhone.
1323 %    These are not valid PNG files and it is impossible to recover
1324 %    the original PNG from files that have been converted to Xcode-PNG,
1325 %    since irretrievable loss of color data has occurred due to the
1326 %    use of premultiplied alpha.
1327 */
1328
1329 #if defined(__cplusplus) || defined(c_plusplus)
1330 extern "C" {
1331 #endif
1332
1333 /*
1334   This the function that does the actual reading of data.  It is
1335   the same as the one supplied in libpng, except that it receives the
1336   datastream from the ReadBlob() function instead of standard input.
1337 */
1338 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1339 {
1340   Image
1341     *image;
1342
1343   image=(Image *) png_get_io_ptr(png_ptr);
1344   if (length != 0)
1345     {
1346       png_size_t
1347         check;
1348
1349       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1350       if (check != length)
1351         {
1352           char
1353             msg[MaxTextExtent];
1354
1355           (void) FormatLocaleString(msg,MaxTextExtent,
1356             "Expected %.20g bytes; found %.20g bytes",(double) length,
1357             (double) check);
1358           png_warning(png_ptr,msg);
1359           png_error(png_ptr,"Read Exception");
1360         }
1361     }
1362 }
1363
1364 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1365     !defined(PNG_MNG_FEATURES_SUPPORTED)
1366 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1367  * older than libpng-1.0.3a, which was the first to allow the empty
1368  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1369  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1370  * encountered after an empty PLTE, so we have to look ahead for bKGD
1371  * chunks and remove them from the datastream that is passed to libpng,
1372  * and store their contents for later use.
1373  */
1374 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1375 {
1376   MngInfo
1377     *mng_info;
1378
1379   Image
1380     *image;
1381
1382   png_size_t
1383     check;
1384
1385   register ssize_t
1386     i;
1387
1388   i=0;
1389   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1390   image=(Image *) mng_info->image;
1391   while (mng_info->bytes_in_read_buffer && length)
1392   {
1393     data[i]=mng_info->read_buffer[i];
1394     mng_info->bytes_in_read_buffer--;
1395     length--;
1396     i++;
1397   }
1398   if (length != 0)
1399     {
1400       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1401
1402       if (check != length)
1403         png_error(png_ptr,"Read Exception");
1404
1405       if (length == 4)
1406         {
1407           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1408               (data[3] == 0))
1409             {
1410               check=(png_size_t) ReadBlob(image,(size_t) length,
1411                 (char *) mng_info->read_buffer);
1412               mng_info->read_buffer[4]=0;
1413               mng_info->bytes_in_read_buffer=4;
1414               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1415                 mng_info->found_empty_plte=MagickTrue;
1416               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1417                 {
1418                   mng_info->found_empty_plte=MagickFalse;
1419                   mng_info->have_saved_bkgd_index=MagickFalse;
1420                 }
1421             }
1422
1423           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1424               (data[3] == 1))
1425             {
1426               check=(png_size_t) ReadBlob(image,(size_t) length,
1427                 (char *) mng_info->read_buffer);
1428               mng_info->read_buffer[4]=0;
1429               mng_info->bytes_in_read_buffer=4;
1430               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1431                 if (mng_info->found_empty_plte)
1432                   {
1433                     /*
1434                       Skip the bKGD data byte and CRC.
1435                     */
1436                     check=(png_size_t)
1437                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1438                     check=(png_size_t) ReadBlob(image,(size_t) length,
1439                       (char *) mng_info->read_buffer);
1440                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1441                     mng_info->have_saved_bkgd_index=MagickTrue;
1442                     mng_info->bytes_in_read_buffer=0;
1443                   }
1444             }
1445         }
1446     }
1447 }
1448 #endif
1449
1450 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1451 {
1452   Image
1453     *image;
1454
1455   image=(Image *) png_get_io_ptr(png_ptr);
1456   if (length != 0)
1457     {
1458       png_size_t
1459         check;
1460
1461       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1462
1463       if (check != length)
1464         png_error(png_ptr,"WriteBlob Failed");
1465     }
1466 }
1467
1468 static void png_flush_data(png_structp png_ptr)
1469 {
1470   (void) png_ptr;
1471 }
1472
1473 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1474 static int PalettesAreEqual(Image *a,Image *b)
1475 {
1476   ssize_t
1477     i;
1478
1479   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1480     return((int) MagickFalse);
1481
1482   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1483     return((int) MagickFalse);
1484
1485   if (a->colors != b->colors)
1486     return((int) MagickFalse);
1487
1488   for (i=0; i < (ssize_t) a->colors; i++)
1489   {
1490     if ((a->colormap[i].red != b->colormap[i].red) ||
1491         (a->colormap[i].green != b->colormap[i].green) ||
1492         (a->colormap[i].blue != b->colormap[i].blue))
1493       return((int) MagickFalse);
1494   }
1495
1496   return((int) MagickTrue);
1497 }
1498 #endif
1499
1500 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1501 {
1502   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1503       mng_info->exists[i] && !mng_info->frozen[i])
1504     {
1505 #ifdef MNG_OBJECT_BUFFERS
1506       if (mng_info->ob[i] != (MngBuffer *) NULL)
1507         {
1508           if (mng_info->ob[i]->reference_count > 0)
1509             mng_info->ob[i]->reference_count--;
1510
1511           if (mng_info->ob[i]->reference_count == 0)
1512             {
1513               if (mng_info->ob[i]->image != (Image *) NULL)
1514                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1515
1516               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1517             }
1518         }
1519       mng_info->ob[i]=(MngBuffer *) NULL;
1520 #endif
1521       mng_info->exists[i]=MagickFalse;
1522       mng_info->invisible[i]=MagickFalse;
1523       mng_info->viewable[i]=MagickFalse;
1524       mng_info->frozen[i]=MagickFalse;
1525       mng_info->x_off[i]=0;
1526       mng_info->y_off[i]=0;
1527       mng_info->object_clip[i].left=0;
1528       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1529       mng_info->object_clip[i].top=0;
1530       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1531     }
1532 }
1533
1534 static void MngInfoFreeStruct(MngInfo *mng_info,
1535     MagickBooleanType *have_mng_structure)
1536 {
1537   if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1538     {
1539       register ssize_t
1540         i;
1541
1542       for (i=1; i < MNG_MAX_OBJECTS; i++)
1543         MngInfoDiscardObject(mng_info,i);
1544
1545       if (mng_info->global_plte != (png_colorp) NULL)
1546         mng_info->global_plte=(png_colorp)
1547           RelinquishMagickMemory(mng_info->global_plte);
1548
1549       mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1550       *have_mng_structure=MagickFalse;
1551     }
1552 }
1553
1554 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1555 {
1556   MngBox
1557     box;
1558
1559   box=box1;
1560   if (box.left < box2.left)
1561     box.left=box2.left;
1562
1563   if (box.top < box2.top)
1564     box.top=box2.top;
1565
1566   if (box.right > box2.right)
1567     box.right=box2.right;
1568
1569   if (box.bottom > box2.bottom)
1570     box.bottom=box2.bottom;
1571
1572   return box;
1573 }
1574
1575 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1576 {
1577    MngBox
1578       box;
1579
1580   /*
1581     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1582   */
1583   box.left=(ssize_t) ((p[0]  << 24) | (p[1]  << 16) | (p[2]  << 8) | p[3]);
1584   box.right=(ssize_t) ((p[4]  << 24) | (p[5]  << 16) | (p[6]  << 8) | p[7]);
1585   box.top=(ssize_t) ((p[8]  << 24) | (p[9]  << 16) | (p[10] << 8) | p[11]);
1586   box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1587   if (delta_type != 0)
1588     {
1589       box.left+=previous_box.left;
1590       box.right+=previous_box.right;
1591       box.top+=previous_box.top;
1592       box.bottom+=previous_box.bottom;
1593     }
1594
1595   return(box);
1596 }
1597
1598 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1599   unsigned char *p)
1600 {
1601   MngPair
1602     pair;
1603   /*
1604     Read two ssize_ts from CLON, MOVE or PAST chunk
1605   */
1606   pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1607   pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1608
1609   if (delta_type != 0)
1610     {
1611       pair.a+=previous_pair.a;
1612       pair.b+=previous_pair.b;
1613     }
1614
1615   return(pair);
1616 }
1617
1618 static long mng_get_long(unsigned char *p)
1619 {
1620   return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1621 }
1622
1623 typedef struct _PNGErrorInfo
1624 {
1625   Image
1626     *image;
1627
1628   ExceptionInfo
1629     *exception;
1630 } PNGErrorInfo;
1631
1632 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1633 {
1634   ExceptionInfo
1635     *exception;
1636
1637   Image
1638     *image;
1639
1640   PNGErrorInfo
1641     *error_info;
1642
1643   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1644   image=error_info->image;
1645   exception=error_info->exception;
1646
1647   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1648     "  libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1649
1650   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1651     "`%s'",image->filename);
1652
1653 #if (PNG_LIBPNG_VER < 10500)
1654   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1655    * are building with libpng-1.4.x and can be ignored.
1656    */
1657   longjmp(ping->jmpbuf,1);
1658 #else
1659   png_longjmp(ping,1);
1660 #endif
1661 }
1662
1663 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1664 {
1665   ExceptionInfo
1666     *exception;
1667
1668   Image
1669     *image;
1670
1671   PNGErrorInfo
1672     *error_info;
1673
1674   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1675     png_error(ping, message);
1676
1677   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1678   image=error_info->image;
1679   exception=error_info->exception;
1680   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1681     "  libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1682
1683   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1684     message,"`%s'",image->filename);
1685 }
1686
1687 #ifdef PNG_USER_MEM_SUPPORTED
1688 #if PNG_LIBPNG_VER >= 10400
1689 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1690 #else
1691 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1692 #endif
1693 {
1694   (void) png_ptr;
1695   return((png_voidp) AcquireMagickMemory((size_t) size));
1696 }
1697
1698 /*
1699   Free a pointer.  It is removed from the list at the same time.
1700 */
1701 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1702 {
1703   (void) png_ptr;
1704   ptr=RelinquishMagickMemory(ptr);
1705   return((png_free_ptr) NULL);
1706 }
1707 #endif
1708
1709 #if defined(__cplusplus) || defined(c_plusplus)
1710 }
1711 #endif
1712
1713 static int
1714 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1715    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1716 {
1717   register ssize_t
1718     i;
1719
1720   register unsigned char
1721     *dp;
1722
1723   register png_charp
1724     sp;
1725
1726   png_uint_32
1727     length,
1728     nibbles;
1729
1730   StringInfo
1731     *profile;
1732
1733   const unsigned char
1734     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1735                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1736                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1737                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1738                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1739                  13,14,15};
1740
1741   sp=text[ii].text+1;
1742   /* look for newline */
1743   while (*sp != '\n')
1744      sp++;
1745
1746   /* look for length */
1747   while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1748      sp++;
1749
1750   length=(png_uint_32) StringToLong(sp);
1751
1752   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1753        "      length: %lu",(unsigned long) length);
1754
1755   while (*sp != ' ' && *sp != '\n')
1756      sp++;
1757
1758   /* allocate space */
1759   if (length == 0)
1760   {
1761     png_warning(ping,"invalid profile length");
1762     return(MagickFalse);
1763   }
1764
1765   profile=BlobToStringInfo((const void *) NULL,length);
1766
1767   if (profile == (StringInfo *) NULL)
1768   {
1769     png_warning(ping, "unable to copy profile");
1770     return(MagickFalse);
1771   }
1772
1773   /* copy profile, skipping white space and column 1 "=" signs */
1774   dp=GetStringInfoDatum(profile);
1775   nibbles=length*2;
1776
1777   for (i=0; i < (ssize_t) nibbles; i++)
1778   {
1779     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1780     {
1781       if (*sp == '\0')
1782         {
1783           png_warning(ping, "ran out of profile data");
1784           profile=DestroyStringInfo(profile);
1785           return(MagickFalse);
1786         }
1787       sp++;
1788     }
1789
1790     if (i%2 == 0)
1791       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1792
1793     else
1794       (*dp++)+=unhex[(int) *sp++];
1795   }
1796   /*
1797     We have already read "Raw profile type.
1798   */
1799   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1800   profile=DestroyStringInfo(profile);
1801
1802   if (image_info->verbose)
1803     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1804
1805   return MagickTrue;
1806 }
1807
1808 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1809 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1810 {
1811   Image
1812     *image;
1813
1814
1815   /* The unknown chunk structure contains the chunk data:
1816      png_byte name[5];
1817      png_byte *data;
1818      png_size_t size;
1819
1820      Note that libpng has already taken care of the CRC handling.
1821   */
1822
1823   LogMagickEvent(CoderEvent,GetMagickModule(),
1824      " read_vpag_chunk: found %c%c%c%c chunk",
1825        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1826
1827   if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1828       chunk->name[2] != 65 ||chunk-> name[3] != 103)
1829     return(0); /* Did not recognize */
1830
1831   /* recognized vpAg */
1832
1833   if (chunk->size != 9)
1834     return(-1); /* Error return */
1835
1836   if (chunk->data[8] != 0)
1837     return(0);  /* ImageMagick requires pixel units */
1838
1839   image=(Image *) png_get_user_chunk_ptr(ping);
1840
1841   image->page.width=(size_t) ((chunk->data[0] << 24) |
1842      (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1843
1844   image->page.height=(size_t) ((chunk->data[4] << 24) |
1845      (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1846
1847   /* Return one of the following: */
1848      /* return(-n);  chunk had an error */
1849      /* return(0);  did not recognize */
1850      /* return(n);  success */
1851
1852   return(1);
1853
1854 }
1855 #endif
1856
1857 #if defined(PNG_tIME_SUPPORTED)
1858 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
1859   ExceptionInfo *exception)
1860 {
1861   png_timep
1862     time;
1863
1864   if (png_get_tIME(ping,info,&time))
1865     {
1866       char
1867         timestamp[21];
1868
1869       FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
1870         time->year,time->month,time->day,time->hour,time->minute,time->second);
1871       SetImageProperty(image,"png:tIME",timestamp,exception);
1872     }
1873 }
1874 #endif
1875
1876 /*
1877 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1878 %                                                                             %
1879 %                                                                             %
1880 %                                                                             %
1881 %   R e a d O n e P N G I m a g e                                             %
1882 %                                                                             %
1883 %                                                                             %
1884 %                                                                             %
1885 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1886 %
1887 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1888 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
1889 %  necessary for the new Image structure and returns a pointer to the new
1890 %  image.
1891 %
1892 %  The format of the ReadOnePNGImage method is:
1893 %
1894 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1895 %         ExceptionInfo *exception)
1896 %
1897 %  A description of each parameter follows:
1898 %
1899 %    o mng_info: Specifies a pointer to a MngInfo structure.
1900 %
1901 %    o image_info: the image info.
1902 %
1903 %    o exception: return any errors or warnings in this structure.
1904 %
1905 */
1906 static Image *ReadOnePNGImage(MngInfo *mng_info,
1907     const ImageInfo *image_info, ExceptionInfo *exception)
1908 {
1909   /* Read one PNG image */
1910
1911   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1912
1913   Image
1914     *image;
1915
1916   char
1917     im_vers[32],
1918     libpng_runv[32],
1919     libpng_vers[32],
1920     zlib_runv[32],
1921     zlib_vers[32];
1922
1923   int
1924     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
1925     num_raw_profiles,
1926     num_text,
1927     num_text_total,
1928     num_passes,
1929     number_colors,
1930     pass,
1931     ping_bit_depth,
1932     ping_color_type,
1933     ping_file_depth,
1934     ping_interlace_method,
1935     ping_compression_method,
1936     ping_filter_method,
1937     ping_num_trans,
1938     unit_type;
1939
1940   double
1941     file_gamma;
1942
1943   MagickBooleanType
1944     logging,
1945     ping_found_cHRM,
1946     ping_found_gAMA,
1947     ping_found_iCCP,
1948     ping_found_sRGB,
1949     ping_found_sRGB_cHRM,
1950     ping_preserve_iCCP,
1951     status;
1952
1953   MemoryInfo
1954     *volatile pixel_info;
1955
1956   PixelInfo
1957     transparent_color;
1958
1959   PNGErrorInfo
1960     error_info;
1961
1962   png_bytep
1963      ping_trans_alpha;
1964
1965   png_color_16p
1966      ping_background,
1967      ping_trans_color;
1968
1969   png_info
1970     *end_info,
1971     *ping_info;
1972
1973   png_struct
1974     *ping;
1975
1976   png_textp
1977     text;
1978
1979   png_uint_32
1980     ping_height,
1981     ping_width,
1982     x_resolution,
1983     y_resolution;
1984
1985   QuantumInfo
1986     *quantum_info;
1987
1988   ssize_t
1989     ping_rowbytes,
1990     y;
1991
1992   register unsigned char
1993     *p;
1994
1995   register ssize_t
1996     i,
1997     x;
1998
1999   register Quantum
2000     *q;
2001
2002   size_t
2003     length,
2004     row_offset;
2005
2006   ssize_t
2007     j;
2008
2009   unsigned char
2010     *ping_pixels;
2011
2012 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2013   png_byte unused_chunks[]=
2014   {
2015     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2016     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2017     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2018     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2019     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2020 #if !defined(PNG_tIME_SUPPORTED)
2021     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2022 #endif
2023 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2024                           /* ignore the APNG chunks */
2025      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2026     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2027     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2028 #endif
2029   };
2030 #endif
2031
2032   /* Define these outside of the following "if logging()" block so they will
2033    * show in debuggers.
2034    */
2035   *im_vers='\0';
2036   (void) ConcatenateMagickString(im_vers,
2037          MagickLibVersionText,32);
2038   (void) ConcatenateMagickString(im_vers,
2039          MagickLibAddendum,32);
2040
2041   *libpng_vers='\0';
2042   (void) ConcatenateMagickString(libpng_vers,
2043          PNG_LIBPNG_VER_STRING,32);
2044   *libpng_runv='\0';
2045   (void) ConcatenateMagickString(libpng_runv,
2046          png_get_libpng_ver(NULL),32);
2047
2048   *zlib_vers='\0';
2049   (void) ConcatenateMagickString(zlib_vers,
2050          ZLIB_VERSION,32);
2051   *zlib_runv='\0';
2052   (void) ConcatenateMagickString(zlib_runv,
2053          zlib_version,32);
2054
2055   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2056        "  Enter ReadOnePNGImage()\n"
2057        "    IM version     = %s\n"
2058        "    Libpng version = %s",
2059        im_vers, libpng_vers);
2060
2061   if (logging != MagickFalse)
2062   {
2063     if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2064     {
2065     LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2066         libpng_runv);
2067     }
2068     LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
2069         zlib_vers);
2070     if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2071     {
2072     LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2073         zlib_runv);
2074     }
2075   }
2076
2077 #if (PNG_LIBPNG_VER < 10200)
2078   if (image_info->verbose)
2079     printf("Your PNG library (libpng-%s) is rather old.\n",
2080        PNG_LIBPNG_VER_STRING);
2081 #endif
2082
2083 #if (PNG_LIBPNG_VER >= 10400)
2084 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
2085   if (image_info->verbose)
2086     {
2087       printf("Your PNG library (libpng-%s) is an old beta version.\n",
2088            PNG_LIBPNG_VER_STRING);
2089       printf("Please update it.\n");
2090     }
2091 #  endif
2092 #endif
2093
2094
2095   quantum_info = (QuantumInfo *) NULL;
2096   image=mng_info->image;
2097
2098   if (logging != MagickFalse)
2099   {
2100     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2101        "    Before reading:\n"
2102        "      image->alpha_trait=%d"
2103        "      image->rendering_intent=%d\n"
2104        "      image->colorspace=%d\n"
2105        "      image->gamma=%f",
2106        (int) image->alpha_trait, (int) image->rendering_intent,
2107        (int) image->colorspace, image->gamma);
2108   }
2109   intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2110
2111   /* Set to an out-of-range color unless tRNS chunk is present */
2112   transparent_color.red=65537;
2113   transparent_color.green=65537;
2114   transparent_color.blue=65537;
2115   transparent_color.alpha=65537;
2116
2117   number_colors=0;
2118   num_text = 0;
2119   num_text_total = 0;
2120   num_raw_profiles = 0;
2121
2122   ping_found_cHRM = MagickFalse;
2123   ping_found_gAMA = MagickFalse;
2124   ping_found_iCCP = MagickFalse;
2125   ping_found_sRGB = MagickFalse;
2126   ping_found_sRGB_cHRM = MagickFalse;
2127   ping_preserve_iCCP = MagickFalse;
2128
2129
2130   /*
2131     Allocate the PNG structures
2132   */
2133 #ifdef PNG_USER_MEM_SUPPORTED
2134  error_info.image=image;
2135  error_info.exception=exception;
2136  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2137    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2138    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2139 #else
2140   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2141     MagickPNGErrorHandler,MagickPNGWarningHandler);
2142 #endif
2143   if (ping == (png_struct *) NULL)
2144     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2145
2146   ping_info=png_create_info_struct(ping);
2147
2148   if (ping_info == (png_info *) NULL)
2149     {
2150       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2151       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2152     }
2153
2154   end_info=png_create_info_struct(ping);
2155
2156   if (end_info == (png_info *) NULL)
2157     {
2158       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2159       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2160     }
2161
2162   pixel_info=(MemoryInfo *) NULL;
2163
2164   if (setjmp(png_jmpbuf(ping)))
2165     {
2166       /*
2167         PNG image is corrupt.
2168       */
2169       png_destroy_read_struct(&ping,&ping_info,&end_info);
2170
2171 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2172       UnlockSemaphoreInfo(ping_semaphore);
2173 #endif
2174
2175       if (pixel_info != (MemoryInfo *) NULL)
2176         pixel_info=RelinquishVirtualMemory(pixel_info);
2177
2178       if (logging != MagickFalse)
2179         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2180           "  exit ReadOnePNGImage() with error.");
2181
2182       if (image != (Image *) NULL)
2183         image->columns=0;
2184
2185       return(GetFirstImageInList(image));
2186     }
2187
2188   /* {  For navigation to end of SETJMP-protected block.  Within this
2189    *    block, use png_error() instead of Throwing an Exception, to ensure
2190    *    that libpng is able to clean up, and that the semaphore is unlocked.
2191    */
2192
2193 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2194   LockSemaphoreInfo(ping_semaphore);
2195 #endif
2196
2197 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2198   /* Allow benign errors */
2199   png_set_benign_errors(ping, 1);
2200 #endif
2201
2202   /*
2203     Prepare PNG for reading.
2204   */
2205
2206   mng_info->image_found++;
2207   png_set_sig_bytes(ping,8);
2208
2209   if (LocaleCompare(image_info->magick,"MNG") == 0)
2210     {
2211 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2212       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2213       png_set_read_fn(ping,image,png_get_data);
2214 #else
2215 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2216       png_permit_empty_plte(ping,MagickTrue);
2217       png_set_read_fn(ping,image,png_get_data);
2218 #else
2219       mng_info->image=image;
2220       mng_info->bytes_in_read_buffer=0;
2221       mng_info->found_empty_plte=MagickFalse;
2222       mng_info->have_saved_bkgd_index=MagickFalse;
2223       png_set_read_fn(ping,mng_info,mng_get_data);
2224 #endif
2225 #endif
2226     }
2227
2228   else
2229     png_set_read_fn(ping,image,png_get_data);
2230
2231   {
2232     const char
2233       *value;
2234
2235     value=GetImageOption(image_info,"profile:skip");
2236
2237     if (IsOptionMember("ICC",value) == MagickFalse)
2238     {
2239
2240        value=GetImageOption(image_info,"png:preserve-iCCP");
2241
2242        if (value == NULL)
2243           value=GetImageArtifact(image,"png:preserve-iCCP");
2244
2245        if (value != NULL)
2246           ping_preserve_iCCP=MagickTrue;
2247
2248 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2249        /* Don't let libpng check for ICC/sRGB profile because we're going
2250         * to do that anyway.  This feature was added at libpng-1.6.12.
2251         * If logging, go ahead and check and issue a warning as appropriate.
2252         */
2253        if (logging == MagickFalse)
2254           png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2255 #endif
2256     }
2257 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2258     else
2259     {
2260        png_set_keep_unknown_chunks(ping, 1, mng_iCCP, 1);
2261     }
2262 #endif
2263   }
2264 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2265   /* Ignore unused chunks and all unknown chunks except for vpAg */
2266 #if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2267   png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2268 #else
2269   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2270 #endif
2271   png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2272   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2273      (int)sizeof(unused_chunks)/5);
2274   /* Callback for other unknown chunks */
2275   png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2276 #endif
2277
2278 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2279 #  if (PNG_LIBPNG_VER >= 10400)
2280     /* Limit the size of the chunk storage cache used for sPLT, text,
2281      * and unknown chunks.
2282      */
2283     png_set_chunk_cache_max(ping, 32767);
2284 #  endif
2285 #endif
2286
2287 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2288     /* Disable new libpng-1.5.10 feature */
2289     png_set_check_for_invalid_index (ping, 0);
2290 #endif
2291
2292 #if (PNG_LIBPNG_VER < 10400)
2293 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2294    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2295   /* Disable thread-unsafe features of pnggccrd */
2296   if (png_access_version_number() >= 10200)
2297   {
2298     png_uint_32 mmx_disable_mask=0;
2299     png_uint_32 asm_flags;
2300
2301     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2302                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2303                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2304                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2305     asm_flags=png_get_asm_flags(ping);
2306     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2307   }
2308 #  endif
2309 #endif
2310
2311   png_read_info(ping,ping_info);
2312
2313   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2314                &ping_bit_depth,&ping_color_type,
2315                &ping_interlace_method,&ping_compression_method,
2316                &ping_filter_method);
2317
2318   ping_file_depth = ping_bit_depth;
2319
2320   /* Swap bytes if requested */
2321   if (ping_file_depth == 16)
2322   {
2323      const char
2324        *value;
2325
2326      value=GetImageOption(image_info,"png:swap-bytes");
2327
2328      if (value == NULL)
2329         value=GetImageArtifact(image,"png:swap-bytes");
2330
2331      if (value != NULL)
2332         png_set_swap(ping);
2333   }
2334
2335   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2336   {
2337       char
2338         msg[MaxTextExtent];
2339
2340       (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2341       (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2342
2343       (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2344       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2345   }
2346
2347   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2348                       &ping_trans_color);
2349
2350   (void) png_get_bKGD(ping, ping_info, &ping_background);
2351
2352   if (ping_bit_depth < 8)
2353     {
2354        png_set_packing(ping);
2355        ping_bit_depth = 8;
2356     }
2357
2358   image->depth=ping_bit_depth;
2359   image->depth=GetImageQuantumDepth(image,MagickFalse);
2360   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2361
2362   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2363       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2364     {
2365       image->rendering_intent=UndefinedIntent;
2366       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2367       (void) ResetMagickMemory(&image->chromaticity,0,
2368         sizeof(image->chromaticity));
2369     }
2370
2371   if (logging != MagickFalse)
2372     {
2373       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2374         "    PNG width: %.20g, height: %.20g\n"
2375         "    PNG color_type: %d, bit_depth: %d\n"
2376         "    PNG compression_method: %d\n"
2377         "    PNG interlace_method: %d, filter_method: %d",
2378         (double) ping_width, (double) ping_height,
2379         ping_color_type, ping_bit_depth,
2380         ping_compression_method,
2381         ping_interlace_method,ping_filter_method);
2382
2383     }
2384
2385   if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2386     {
2387       ping_found_iCCP=MagickTrue;
2388       if (logging != MagickFalse)
2389         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2390           "    Found PNG iCCP chunk.");
2391     }
2392
2393   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2394     {
2395       ping_found_gAMA=MagickTrue;
2396       if (logging != MagickFalse)
2397         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2398           "    Found PNG gAMA chunk.");
2399     }
2400
2401   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2402     {
2403       ping_found_cHRM=MagickTrue;
2404       if (logging != MagickFalse)
2405         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2406           "    Found PNG cHRM chunk.");
2407     }
2408
2409   if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2410       PNG_INFO_sRGB))
2411     {
2412       ping_found_sRGB=MagickTrue;
2413       if (logging != MagickFalse)
2414         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2415           "    Found PNG sRGB chunk.");
2416     }
2417
2418 #ifdef PNG_READ_iCCP_SUPPORTED
2419     if (ping_found_iCCP !=MagickTrue &&
2420       ping_found_sRGB != MagickTrue &&
2421       png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2422     {
2423       ping_found_iCCP=MagickTrue;
2424       if (logging != MagickFalse)
2425         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2426           "    Found PNG iCCP chunk.");
2427     }
2428
2429   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2430     {
2431       int
2432         compression;
2433
2434 #if (PNG_LIBPNG_VER < 10500)
2435       png_charp
2436         info;
2437 #else
2438       png_bytep
2439         info;
2440 #endif
2441
2442       png_charp
2443         name;
2444
2445       png_uint_32
2446         profile_length;
2447
2448       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2449         &profile_length);
2450
2451       if (profile_length != 0)
2452         {
2453           StringInfo
2454             *profile;
2455
2456           if (logging != MagickFalse)
2457             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2458               "    Reading PNG iCCP chunk.");
2459
2460           profile=BlobToStringInfo(info,profile_length);
2461
2462           if (profile == (StringInfo *) NULL)
2463           {
2464             png_warning(ping, "ICC profile is NULL");
2465             profile=DestroyStringInfo(profile);
2466           }
2467           else
2468           {
2469             if (ping_preserve_iCCP == MagickFalse)
2470             {
2471                  int
2472                    icheck,
2473                    got_crc=0;
2474
2475
2476                  png_uint_32
2477                    length,
2478                    profile_crc=0;
2479
2480                  unsigned char
2481                    *data;
2482
2483                  length=(png_uint_32) GetStringInfoLength(profile);
2484
2485                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2486                  {
2487                    if (length == sRGB_info[icheck].len)
2488                    {
2489                      if (got_crc == 0)
2490                      {
2491                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2492                          "    Got a %lu-byte ICC profile (potentially sRGB)",
2493                          (unsigned long) length);
2494
2495                        data=GetStringInfoDatum(profile);
2496                        profile_crc=crc32(0,data,length);
2497
2498                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2499                            "      with crc=%8x",(unsigned int) profile_crc);
2500                        got_crc++;
2501                      }
2502
2503                      if (profile_crc == sRGB_info[icheck].crc)
2504                      {
2505                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2506                             "      It is sRGB with rendering intent = %s",
2507                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
2508                              sRGB_info[icheck].intent));
2509                         if (image->rendering_intent==UndefinedIntent)
2510                         {
2511                           image->rendering_intent=
2512                           Magick_RenderingIntent_from_PNG_RenderingIntent(
2513                              sRGB_info[icheck].intent);
2514                         }
2515                         break;
2516                      }
2517                    }
2518                  }
2519                  if (sRGB_info[icheck].len == 0)
2520                  {
2521                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2522                         "    Got a %lu-byte ICC profile not recognized as sRGB",
2523                         (unsigned long) length);
2524                     (void) SetImageProfile(image,"icc",profile,exception);
2525                  }
2526             }
2527             else /* Preserve-iCCP */
2528             {
2529                     (void) SetImageProfile(image,"icc",profile,exception);
2530             }
2531
2532             profile=DestroyStringInfo(profile);
2533           }
2534       }
2535     }
2536 #endif
2537
2538 #if defined(PNG_READ_sRGB_SUPPORTED)
2539   {
2540     if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2541         PNG_INFO_sRGB))
2542     {
2543       if (png_get_sRGB(ping,ping_info,&intent))
2544       {
2545         if (image->rendering_intent == UndefinedIntent)
2546           image->rendering_intent=
2547              Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2548
2549         if (logging != MagickFalse)
2550           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2551             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2552       }
2553     }
2554
2555     else if (mng_info->have_global_srgb)
2556       {
2557         if (image->rendering_intent == UndefinedIntent)
2558           image->rendering_intent=
2559             Magick_RenderingIntent_from_PNG_RenderingIntent
2560             (mng_info->global_srgb_intent);
2561       }
2562   }
2563 #endif
2564
2565
2566   {
2567      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2568        if (mng_info->have_global_gama)
2569          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2570
2571      if (png_get_gAMA(ping,ping_info,&file_gamma))
2572        {
2573          image->gamma=(float) file_gamma;
2574          if (logging != MagickFalse)
2575            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2576              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2577        }
2578   }
2579
2580   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2581     {
2582       if (mng_info->have_global_chrm != MagickFalse)
2583         {
2584           (void) png_set_cHRM(ping,ping_info,
2585             mng_info->global_chrm.white_point.x,
2586             mng_info->global_chrm.white_point.y,
2587             mng_info->global_chrm.red_primary.x,
2588             mng_info->global_chrm.red_primary.y,
2589             mng_info->global_chrm.green_primary.x,
2590             mng_info->global_chrm.green_primary.y,
2591             mng_info->global_chrm.blue_primary.x,
2592             mng_info->global_chrm.blue_primary.y);
2593         }
2594     }
2595
2596   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2597     {
2598       (void) png_get_cHRM(ping,ping_info,
2599         &image->chromaticity.white_point.x,
2600         &image->chromaticity.white_point.y,
2601         &image->chromaticity.red_primary.x,
2602         &image->chromaticity.red_primary.y,
2603         &image->chromaticity.green_primary.x,
2604         &image->chromaticity.green_primary.y,
2605         &image->chromaticity.blue_primary.x,
2606         &image->chromaticity.blue_primary.y);
2607
2608        ping_found_cHRM=MagickTrue;
2609
2610        if (image->chromaticity.red_primary.x>0.6399f &&
2611            image->chromaticity.red_primary.x<0.6401f &&
2612            image->chromaticity.red_primary.y>0.3299f &&
2613            image->chromaticity.red_primary.y<0.3301f &&
2614            image->chromaticity.green_primary.x>0.2999f &&
2615            image->chromaticity.green_primary.x<0.3001f &&
2616            image->chromaticity.green_primary.y>0.5999f &&
2617            image->chromaticity.green_primary.y<0.6001f &&
2618            image->chromaticity.blue_primary.x>0.1499f &&
2619            image->chromaticity.blue_primary.x<0.1501f &&
2620            image->chromaticity.blue_primary.y>0.0599f &&
2621            image->chromaticity.blue_primary.y<0.0601f &&
2622            image->chromaticity.white_point.x>0.3126f &&
2623            image->chromaticity.white_point.x<0.3128f &&
2624            image->chromaticity.white_point.y>0.3289f &&
2625            image->chromaticity.white_point.y<0.3291f)
2626           ping_found_sRGB_cHRM=MagickTrue;
2627     }
2628
2629   if (image->rendering_intent != UndefinedIntent)
2630     {
2631       if (ping_found_sRGB != MagickTrue &&
2632           (ping_found_gAMA != MagickTrue ||
2633           (image->gamma > .45 && image->gamma < .46)) &&
2634           (ping_found_cHRM != MagickTrue ||
2635           ping_found_sRGB_cHRM != MagickFalse) &&
2636           ping_found_iCCP != MagickTrue)
2637       {
2638          png_set_sRGB(ping,ping_info,
2639             Magick_RenderingIntent_to_PNG_RenderingIntent
2640             (image->rendering_intent));
2641          file_gamma=1.000f/2.200f;
2642          ping_found_sRGB=MagickTrue;
2643          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2644            "    Setting sRGB as if in input");
2645       }
2646     }
2647
2648 #if defined(PNG_oFFs_SUPPORTED)
2649   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2650     {
2651       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2652       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2653
2654       if (logging != MagickFalse)
2655         if (image->page.x || image->page.y)
2656           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2657             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2658             image->page.x,(double) image->page.y);
2659     }
2660 #endif
2661 #if defined(PNG_pHYs_SUPPORTED)
2662   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2663     {
2664       if (mng_info->have_global_phys)
2665         {
2666           png_set_pHYs(ping,ping_info,
2667                        mng_info->global_x_pixels_per_unit,
2668                        mng_info->global_y_pixels_per_unit,
2669                        mng_info->global_phys_unit_type);
2670         }
2671     }
2672
2673   x_resolution=0;
2674   y_resolution=0;
2675   unit_type=0;
2676   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2677     {
2678       /*
2679         Set image resolution.
2680       */
2681       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2682         &unit_type);
2683       image->resolution.x=(double) x_resolution;
2684       image->resolution.y=(double) y_resolution;
2685
2686       if (unit_type == PNG_RESOLUTION_METER)
2687         {
2688           image->units=PixelsPerCentimeterResolution;
2689           image->resolution.x=(double) x_resolution/100.0;
2690           image->resolution.y=(double) y_resolution/100.0;
2691         }
2692
2693       if (logging != MagickFalse)
2694         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2695           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2696           (double) x_resolution,(double) y_resolution,unit_type);
2697     }
2698 #endif
2699
2700   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2701     {
2702       png_colorp
2703         palette;
2704
2705       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2706
2707       if ((number_colors == 0) &&
2708           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2709         {
2710           if (mng_info->global_plte_length)
2711             {
2712               png_set_PLTE(ping,ping_info,mng_info->global_plte,
2713                 (int) mng_info->global_plte_length);
2714
2715               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2716               {
2717                 if (mng_info->global_trns_length)
2718                   {
2719                     png_warning(ping,
2720                       "global tRNS has more entries than global PLTE");
2721                   }
2722                 else
2723                   {
2724                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
2725                        (int) mng_info->global_trns_length,NULL);
2726                   }
2727                }
2728 #ifdef PNG_READ_bKGD_SUPPORTED
2729               if (
2730 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2731                    mng_info->have_saved_bkgd_index ||
2732 #endif
2733                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2734                     {
2735                       png_color_16
2736                          background;
2737
2738 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2739                       if (mng_info->have_saved_bkgd_index)
2740                         background.index=mng_info->saved_bkgd_index;
2741 #endif
2742                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2743                         background.index=ping_background->index;
2744
2745                       background.red=(png_uint_16)
2746                         mng_info->global_plte[background.index].red;
2747
2748                       background.green=(png_uint_16)
2749                         mng_info->global_plte[background.index].green;
2750
2751                       background.blue=(png_uint_16)
2752                         mng_info->global_plte[background.index].blue;
2753
2754                       background.gray=(png_uint_16)
2755                         mng_info->global_plte[background.index].green;
2756
2757                       png_set_bKGD(ping,ping_info,&background);
2758                     }
2759 #endif
2760                 }
2761               else
2762                 png_error(ping,"No global PLTE in file");
2763             }
2764         }
2765
2766 #ifdef PNG_READ_bKGD_SUPPORTED
2767   if (mng_info->have_global_bkgd &&
2768           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2769       image->background_color=mng_info->mng_global_bkgd;
2770
2771   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2772     {
2773       unsigned int
2774         bkgd_scale;
2775
2776       /* Set image background color.
2777        * Scale background components to 16-bit, then scale
2778        * to quantum depth
2779        */
2780
2781         bkgd_scale = 1;
2782
2783         if (ping_file_depth == 1)
2784            bkgd_scale = 255;
2785
2786         else if (ping_file_depth == 2)
2787            bkgd_scale = 85;
2788
2789         else if (ping_file_depth == 4)
2790            bkgd_scale = 17;
2791
2792         if (ping_file_depth <= 8)
2793            bkgd_scale *= 257;
2794
2795         ping_background->red *= bkgd_scale;
2796         ping_background->green *= bkgd_scale;
2797         ping_background->blue *= bkgd_scale;
2798
2799         if (logging != MagickFalse)
2800           {
2801             if (logging != MagickFalse)
2802               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2803                  "    Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d).\n"
2804                  "    bkgd_scale=%d.  ping_background=(%d,%d,%d).",
2805                  ping_background->red,ping_background->green,
2806                  ping_background->blue,
2807                  bkgd_scale,ping_background->red,
2808                  ping_background->green,ping_background->blue);
2809           }
2810
2811         image->background_color.red=
2812             ScaleShortToQuantum(ping_background->red);
2813
2814         image->background_color.green=
2815             ScaleShortToQuantum(ping_background->green);
2816
2817         image->background_color.blue=
2818           ScaleShortToQuantum(ping_background->blue);
2819
2820         image->background_color.alpha=OpaqueAlpha;
2821
2822         if (logging != MagickFalse)
2823           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2824             "    image->background_color=(%.20g,%.20g,%.20g).",
2825             (double) image->background_color.red,
2826             (double) image->background_color.green,
2827             (double) image->background_color.blue);
2828     }
2829 #endif /* PNG_READ_bKGD_SUPPORTED */
2830
2831   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2832     {
2833       /*
2834         Image has a tRNS chunk.
2835       */
2836       int
2837         max_sample;
2838
2839       size_t
2840         one=1;
2841
2842       if (logging != MagickFalse)
2843         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2844           "    Reading PNG tRNS chunk.");
2845
2846       max_sample = (int) ((one << ping_file_depth) - 1);
2847
2848       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2849           (int)ping_trans_color->gray > max_sample) ||
2850           (ping_color_type == PNG_COLOR_TYPE_RGB &&
2851           ((int)ping_trans_color->red > max_sample ||
2852           (int)ping_trans_color->green > max_sample ||
2853           (int)ping_trans_color->blue > max_sample)))
2854         {
2855           if (logging != MagickFalse)
2856             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2857               "    Ignoring PNG tRNS chunk with out-of-range sample.");
2858           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2859           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2860           image->alpha_trait=UndefinedPixelTrait;
2861         }
2862       else
2863         {
2864           int
2865             scale_to_short;
2866
2867           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
2868
2869           /* Scale transparent_color to short */
2870           transparent_color.red= scale_to_short*ping_trans_color->red;
2871           transparent_color.green= scale_to_short*ping_trans_color->green;
2872           transparent_color.blue= scale_to_short*ping_trans_color->blue;
2873           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2874
2875           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2876             {
2877               if (logging != MagickFalse)
2878               {
2879                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2880                   "    Raw tRNS graylevel = %d, scaled graylevel = %d.",
2881                   (int) ping_trans_color->gray,(int) transparent_color.alpha);
2882
2883               }
2884               transparent_color.red=transparent_color.alpha;
2885               transparent_color.green=transparent_color.alpha;
2886               transparent_color.blue=transparent_color.alpha;
2887             }
2888         }
2889     }
2890 #if defined(PNG_READ_sBIT_SUPPORTED)
2891   if (mng_info->have_global_sbit)
2892     {
2893       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2894         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2895     }
2896 #endif
2897   num_passes=png_set_interlace_handling(ping);
2898
2899   png_read_update_info(ping,ping_info);
2900
2901   ping_rowbytes=png_get_rowbytes(ping,ping_info);
2902
2903   /*
2904     Initialize image structure.
2905   */
2906   mng_info->image_box.left=0;
2907   mng_info->image_box.right=(ssize_t) ping_width;
2908   mng_info->image_box.top=0;
2909   mng_info->image_box.bottom=(ssize_t) ping_height;
2910   if (mng_info->mng_type == 0)
2911     {
2912       mng_info->mng_width=ping_width;
2913       mng_info->mng_height=ping_height;
2914       mng_info->frame=mng_info->image_box;
2915       mng_info->clip=mng_info->image_box;
2916     }
2917
2918   else
2919     {
2920       image->page.y=mng_info->y_off[mng_info->object_id];
2921     }
2922
2923   image->compression=ZipCompression;
2924   image->columns=ping_width;
2925   image->rows=ping_height;
2926
2927   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2928       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2929     {
2930       double
2931         image_gamma = image->gamma;
2932
2933       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2934          "    image->gamma=%f",(float) image_gamma);
2935
2936       if (image_gamma > 0.75)
2937         {
2938           /* Set image->rendering_intent to Undefined,
2939            * image->colorspace to GRAY, and reset image->chromaticity.
2940            */
2941           image->intensity = Rec709LuminancePixelIntensityMethod;
2942           SetImageColorspace(image,GRAYColorspace,exception);
2943         }
2944       else
2945         {
2946           RenderingIntent
2947             save_rendering_intent = image->rendering_intent;
2948           ChromaticityInfo
2949             save_chromaticity = image->chromaticity;
2950
2951           SetImageColorspace(image,GRAYColorspace,exception);
2952           image->rendering_intent = save_rendering_intent;
2953           image->chromaticity = save_chromaticity;
2954         }
2955
2956       image->gamma = image_gamma;
2957     }
2958
2959   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2960       "    image->colorspace=%d",(int) image->colorspace);
2961
2962   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2963       ((int) ping_bit_depth < 16 &&
2964       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2965     {
2966       size_t
2967         one;
2968
2969       image->storage_class=PseudoClass;
2970       one=1;
2971       image->colors=one << ping_file_depth;
2972 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2973       if (image->colors > 256)
2974         image->colors=256;
2975 #else
2976       if (image->colors > 65536L)
2977         image->colors=65536L;
2978 #endif
2979       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2980         {
2981           png_colorp
2982             palette;
2983
2984           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2985           image->colors=(size_t) number_colors;
2986
2987           if (logging != MagickFalse)
2988             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2989               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2990         }
2991     }
2992
2993   if (image->storage_class == PseudoClass)
2994     {
2995       /*
2996         Initialize image colormap.
2997       */
2998       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
2999         png_error(ping,"Memory allocation failed");
3000
3001       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3002         {
3003           png_colorp
3004             palette;
3005
3006           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3007
3008           for (i=0; i < (ssize_t) number_colors; i++)
3009           {
3010             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3011             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3012             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3013           }
3014
3015           for ( ; i < (ssize_t) image->colors; i++)
3016           {
3017             image->colormap[i].red=0;
3018             image->colormap[i].green=0;
3019             image->colormap[i].blue=0;
3020           }
3021         }
3022
3023       else
3024         {
3025           Quantum
3026             scale;
3027
3028           scale = (Quantum) (65535UL)/((1UL << ping_file_depth)-1);
3029
3030 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
3031           scale = ScaleShortToQuantum(scale);
3032 #endif
3033
3034           for (i=0; i < (ssize_t) image->colors; i++)
3035           {
3036             image->colormap[i].red=(Quantum) (i*scale);
3037             image->colormap[i].green=(Quantum) (i*scale);
3038             image->colormap[i].blue=(Quantum) (i*scale);
3039           }
3040        }
3041     }
3042
3043    /* Set some properties for reporting by "identify" */
3044     {
3045       char
3046         msg[MaxTextExtent];
3047
3048      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3049         ping_interlace_method in value */
3050
3051      (void) FormatLocaleString(msg,MaxTextExtent,
3052          "%d, %d",(int) ping_width, (int) ping_height);
3053      (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3054
3055      (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
3056      (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3057
3058      (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
3059          (int) ping_color_type,
3060          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3061      (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3062
3063      if (ping_interlace_method == 0)
3064        {
3065          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
3066             (int) ping_interlace_method);
3067        }
3068      else if (ping_interlace_method == 1)
3069        {
3070          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
3071             (int) ping_interlace_method);
3072        }
3073      else
3074        {
3075          (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
3076             (int) ping_interlace_method);
3077        }
3078        (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3079
3080      if (number_colors != 0)
3081        {
3082          (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3083             (int) number_colors);
3084          (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3085             exception);
3086        }
3087    }
3088 #if defined(PNG_tIME_SUPPORTED)
3089    read_tIME_chunk(image,ping,ping_info,exception);
3090 #endif
3091
3092
3093   /*
3094     Read image scanlines.
3095   */
3096   if (image->delay != 0)
3097     mng_info->scenes_found++;
3098
3099   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3100       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3101       (image_info->first_scene+image_info->number_scenes))))
3102     {
3103       /* This happens later in non-ping decodes */
3104       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3105         image->storage_class=DirectClass;
3106
3107       if (logging != MagickFalse)
3108         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3109           "    Skipping PNG image data for scene %.20g",(double)
3110           mng_info->scenes_found-1);
3111       png_destroy_read_struct(&ping,&ping_info,&end_info);
3112
3113 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3114       UnlockSemaphoreInfo(ping_semaphore);
3115 #endif
3116
3117       if (logging != MagickFalse)
3118         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3119           "  exit ReadOnePNGImage().");
3120
3121       return(image);
3122     }
3123
3124   if (logging != MagickFalse)
3125     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3126       "    Reading PNG IDAT chunk(s)");
3127
3128   if (num_passes > 1)
3129     pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3130       sizeof(*ping_pixels));
3131   else
3132     pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3133
3134   if (pixel_info == (MemoryInfo *) NULL)
3135     png_error(ping,"Memory allocation failed");
3136   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3137
3138   if (logging != MagickFalse)
3139     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3140       "    Converting PNG pixels to pixel packets");
3141   /*
3142     Convert PNG pixels to pixel packets.
3143   */
3144   quantum_info=AcquireQuantumInfo(image_info,image);
3145
3146   if (quantum_info == (QuantumInfo *) NULL)
3147      png_error(ping,"Failed to allocate quantum_info");
3148
3149   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3150
3151   {
3152
3153    MagickBooleanType
3154      found_transparent_pixel;
3155
3156   found_transparent_pixel=MagickFalse;
3157
3158   if (image->storage_class == DirectClass)
3159     {
3160       for (pass=0; pass < num_passes; pass++)
3161       {
3162         /*
3163           Convert image to DirectClass pixel packets.
3164         */
3165         image->alpha_trait=
3166             (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3167             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3168             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3169             BlendPixelTrait : UndefinedPixelTrait;
3170
3171         for (y=0; y < (ssize_t) image->rows; y++)
3172         {
3173           if (num_passes > 1)
3174             row_offset=ping_rowbytes*y;
3175
3176           else
3177             row_offset=0;
3178
3179           png_read_row(ping,ping_pixels+row_offset,NULL);
3180
3181           if (pass < num_passes-1)
3182             continue;
3183
3184           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3185
3186           if (q == (Quantum *) NULL)
3187             break;
3188
3189           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3190             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3191               GrayQuantum,ping_pixels+row_offset,exception);
3192
3193           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3194             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3195               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3196
3197           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3198             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3199               RGBAQuantum,ping_pixels+row_offset,exception);
3200
3201           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3202             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3203               IndexQuantum,ping_pixels+row_offset,exception);
3204
3205           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3206             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3207               RGBQuantum,ping_pixels+row_offset,exception);
3208
3209           if (found_transparent_pixel == MagickFalse)
3210             {
3211               /* Is there a transparent pixel in the row? */
3212               if (y== 0 && logging != MagickFalse)
3213                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3214                    "    Looking for cheap transparent pixel");
3215
3216               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3217               {
3218                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3219                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3220                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3221                   {
3222                     if (logging != MagickFalse)
3223                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3224                         "    ...got one.");
3225
3226                     found_transparent_pixel = MagickTrue;
3227                     break;
3228                   }
3229                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3230                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3231                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3232                     transparent_color.red &&
3233                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3234                     transparent_color.green &&
3235                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3236                     transparent_color.blue))
3237                   {
3238                     if (logging != MagickFalse)
3239                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3240                         "    ...got one.");
3241                     found_transparent_pixel = MagickTrue;
3242                     break;
3243                   }
3244                 q+=GetPixelChannels(image);
3245               }
3246             }
3247
3248           if (num_passes == 1)
3249             {
3250               status=SetImageProgress(image,LoadImageTag,
3251                   (MagickOffsetType) y, image->rows);
3252
3253               if (status == MagickFalse)
3254                 break;
3255             }
3256           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3257             break;
3258         }
3259
3260         if (num_passes != 1)
3261           {
3262             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3263             if (status == MagickFalse)
3264               break;
3265           }
3266       }
3267     }
3268
3269   else /* image->storage_class != DirectClass */
3270
3271     for (pass=0; pass < num_passes; pass++)
3272     {
3273       Quantum
3274         *quantum_scanline;
3275
3276       register Quantum
3277         *r;
3278
3279       /*
3280         Convert grayscale image to PseudoClass pixel packets.
3281       */
3282       if (logging != MagickFalse)
3283         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3284           "    Converting grayscale pixels to pixel packets");
3285
3286       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3287         BlendPixelTrait : UndefinedPixelTrait;
3288
3289       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3290         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3291         sizeof(*quantum_scanline));
3292
3293       if (quantum_scanline == (Quantum *) NULL)
3294         png_error(ping,"Memory allocation failed");
3295
3296       for (y=0; y < (ssize_t) image->rows; y++)
3297       {
3298         Quantum
3299            alpha;
3300
3301         if (num_passes > 1)
3302           row_offset=ping_rowbytes*y;
3303
3304         else
3305           row_offset=0;
3306
3307         png_read_row(ping,ping_pixels+row_offset,NULL);
3308
3309         if (pass < num_passes-1)
3310           continue;
3311
3312         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3313
3314         if (q == (Quantum *) NULL)
3315           break;
3316
3317         p=ping_pixels+row_offset;
3318         r=quantum_scanline;
3319
3320         switch (ping_bit_depth)
3321         {
3322           case 8:
3323           {
3324
3325             if (ping_color_type == 4)
3326               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3327               {
3328                 *r++=*p++;
3329
3330                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3331
3332                 SetPixelAlpha(image,alpha,q);
3333
3334                 if (alpha != OpaqueAlpha)
3335                   found_transparent_pixel = MagickTrue;
3336
3337                 q+=GetPixelChannels(image);
3338               }
3339
3340             else
3341               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3342                 *r++=*p++;
3343
3344             break;
3345           }
3346
3347           case 16:
3348           {
3349             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3350             {
3351 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3352               unsigned short
3353                 quantum;
3354
3355               if (image->colors > 256)
3356                 quantum=((*p++) << 8);
3357
3358               else
3359                 quantum=0;
3360
3361               quantum|=(*p++);
3362               *r=ScaleShortToQuantum(quantum);
3363               r++;
3364
3365               if (ping_color_type == 4)
3366                 {
3367                   if (image->colors > 256)
3368                     quantum=((*p++) << 8);
3369                   else
3370                     quantum=0;
3371
3372                   quantum|=(*p++);
3373
3374                   alpha=ScaleShortToQuantum(quantum);
3375                   SetPixelAlpha(image,alpha,q);
3376
3377                   if (alpha != OpaqueAlpha)
3378                     found_transparent_pixel = MagickTrue;
3379
3380                   q+=GetPixelChannels(image);
3381                 }
3382
3383 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3384               *r++=(*p++);
3385               p++; /* strip low byte */
3386
3387               if (ping_color_type == 4)
3388                 {
3389                   SetPixelAlpha(image,*p++,q);
3390
3391                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3392                     found_transparent_pixel = MagickTrue;
3393
3394                   p++;
3395                   q+=GetPixelChannels(image);
3396                 }
3397 #endif
3398             }
3399
3400             break;
3401           }
3402
3403           default:
3404             break;
3405         }
3406
3407         /*
3408           Transfer image scanline.
3409         */
3410         r=quantum_scanline;
3411
3412         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3413
3414         if (q == (Quantum *) NULL)
3415           break;
3416         for (x=0; x < (ssize_t) image->columns; x++)
3417         {
3418           SetPixelIndex(image,*r++,q);
3419           q+=GetPixelChannels(image);
3420         }
3421
3422         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3423           break;
3424
3425         if (num_passes == 1)
3426           {
3427             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3428               image->rows);
3429
3430             if (status == MagickFalse)
3431               break;
3432           }
3433       }
3434
3435       if (num_passes != 1)
3436         {
3437           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3438
3439           if (status == MagickFalse)
3440             break;
3441         }
3442
3443       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3444     }
3445
3446     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3447       UndefinedPixelTrait;
3448
3449     if (logging != MagickFalse)
3450       {
3451         if (found_transparent_pixel != MagickFalse)
3452           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3453             "    Found transparent pixel");
3454         else
3455           {
3456             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3457               "    No transparent pixel was found");
3458
3459             ping_color_type&=0x03;
3460           }
3461       }
3462     }
3463
3464   if (quantum_info != (QuantumInfo *) NULL)
3465     quantum_info=DestroyQuantumInfo(quantum_info);
3466
3467   if (image->storage_class == PseudoClass)
3468     {
3469       PixelTrait
3470         alpha_trait;
3471
3472       alpha_trait=image->alpha_trait;
3473       image->alpha_trait=UndefinedPixelTrait;
3474       (void) SyncImage(image,exception);
3475       image->alpha_trait=alpha_trait;
3476     }
3477
3478   png_read_end(ping,end_info);
3479
3480   if (logging != MagickFalse)
3481   {
3482     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3483        "  image->storage_class=%d\n",(int) image->storage_class);
3484   }
3485
3486   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3487       (ssize_t) image_info->first_scene && image->delay != 0)
3488     {
3489       png_destroy_read_struct(&ping,&ping_info,&end_info);
3490       pixel_info=RelinquishVirtualMemory(pixel_info);
3491       image->colors=2;
3492       (void) SetImageBackgroundColor(image,exception);
3493 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3494       UnlockSemaphoreInfo(ping_semaphore);
3495 #endif
3496       if (logging != MagickFalse)
3497         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3498           "  exit ReadOnePNGImage() early.");
3499       return(image);
3500     }
3501
3502   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3503     {
3504       ClassType
3505         storage_class;
3506
3507       /*
3508         Image has a transparent background.
3509       */
3510       storage_class=image->storage_class;
3511       image->alpha_trait=BlendPixelTrait;
3512
3513 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3514
3515       if (storage_class == PseudoClass)
3516         {
3517           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3518             {
3519               for (x=0; x < ping_num_trans; x++)
3520               {
3521                  image->colormap[x].alpha_trait=BlendPixelTrait;
3522                  image->colormap[x].alpha =
3523                    ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3524               }
3525             }
3526
3527           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3528             {
3529               for (x=0; x < (int) image->colors; x++)
3530               {
3531                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3532                      transparent_color.alpha)
3533                  {
3534                     image->colormap[x].alpha_trait=BlendPixelTrait;
3535                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3536                  }
3537               }
3538             }
3539           (void) SyncImage(image,exception);
3540         }
3541
3542 #if 1 /* Should have already been done above, but glennrp problem P10
3543        * needs this.
3544        */
3545       else
3546         {
3547           for (y=0; y < (ssize_t) image->rows; y++)
3548           {
3549             image->storage_class=storage_class;
3550             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3551
3552             if (q == (Quantum *) NULL)
3553               break;
3554
3555
3556             /* Caution: on a Q8 build, this does not distinguish between
3557              * 16-bit colors that differ only in the low byte
3558              */
3559             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3560             {
3561               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3562                   transparent_color.red &&
3563                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3564                   transparent_color.green &&
3565                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3566                   transparent_color.blue)
3567                 {
3568                   SetPixelAlpha(image,TransparentAlpha,q);
3569                 }
3570
3571 #if 0 /* I have not found a case where this is needed. */
3572               else
3573                 {
3574                   SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3575                 }
3576 #endif
3577
3578               q+=GetPixelChannels(image);
3579             }
3580
3581             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3582                break;
3583           }
3584         }
3585 #endif
3586
3587       image->storage_class=DirectClass;
3588     }
3589
3590   for (j = 0; j < 2; j++)
3591   {
3592     if (j == 0)
3593       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3594           MagickTrue : MagickFalse;
3595     else
3596       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3597           MagickTrue : MagickFalse;
3598
3599     if (status != MagickFalse)
3600       for (i=0; i < (ssize_t) num_text; i++)
3601       {
3602         /* Check for a profile */
3603
3604         if (logging != MagickFalse)
3605           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3606             "    Reading PNG text chunk");
3607
3608         if (strlen(text[i].key) > 16 &&
3609             memcmp(text[i].key, "Raw profile type ",17) == 0)
3610           {
3611             const char
3612               *value;
3613
3614             value=GetImageOption(image_info,"profile:skip");
3615
3616             if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3617             {
3618                (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3619                   (int) i,exception);
3620                num_raw_profiles++;
3621                if (logging != MagickFalse)
3622                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3623                    "    Read raw profile %s",text[i].key+17);
3624             }
3625             else
3626             {
3627                if (logging != MagickFalse)
3628                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3629                    "    Skipping raw profile %s",text[i].key+17);
3630             }
3631           }
3632
3633         else
3634           {
3635             char
3636               *value;
3637
3638             length=text[i].text_length;
3639             value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3640               sizeof(*value));
3641             if (value == (char *) NULL)
3642               {
3643                 png_error(ping,"Memory allocation failed");
3644                 break;
3645               }
3646             *value='\0';
3647             (void) ConcatenateMagickString(value,text[i].text,length+2);
3648
3649             /* Don't save "density" or "units" property if we have a pHYs
3650              * chunk
3651              */
3652             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3653                 (LocaleCompare(text[i].key,"density") != 0 &&
3654                 LocaleCompare(text[i].key,"units") != 0))
3655                (void) SetImageProperty(image,text[i].key,value,exception);
3656
3657             if (logging != MagickFalse)
3658             {
3659               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3660                 "      length: %lu\n"
3661                 "      Keyword: %s",
3662                 (unsigned long) length,
3663                 text[i].key);
3664             }
3665
3666             value=DestroyString(value);
3667           }
3668       }
3669     num_text_total += num_text;
3670   }
3671
3672 #ifdef MNG_OBJECT_BUFFERS
3673   /*
3674     Store the object if necessary.
3675   */
3676   if (object_id && !mng_info->frozen[object_id])
3677     {
3678       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3679         {
3680           /*
3681             create a new object buffer.
3682           */
3683           mng_info->ob[object_id]=(MngBuffer *)
3684             AcquireMagickMemory(sizeof(MngBuffer));
3685
3686           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3687             {
3688               mng_info->ob[object_id]->image=(Image *) NULL;
3689               mng_info->ob[object_id]->reference_count=1;
3690             }
3691         }
3692
3693       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3694           mng_info->ob[object_id]->frozen)
3695         {
3696           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3697              png_error(ping,"Memory allocation failed");
3698
3699           if (mng_info->ob[object_id]->frozen)
3700             png_error(ping,"Cannot overwrite frozen MNG object buffer");
3701         }
3702
3703       else
3704         {
3705
3706           if (mng_info->ob[object_id]->image != (Image *) NULL)
3707             mng_info->ob[object_id]->image=DestroyImage
3708                 (mng_info->ob[object_id]->image);
3709
3710           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3711             exception);
3712
3713           if (mng_info->ob[object_id]->image != (Image *) NULL)
3714             mng_info->ob[object_id]->image->file=(FILE *) NULL;
3715
3716           else
3717             png_error(ping, "Cloning image for object buffer failed");
3718
3719           if (ping_width > 250000L || ping_height > 250000L)
3720              png_error(ping,"PNG Image dimensions are too large.");
3721
3722           mng_info->ob[object_id]->width=ping_width;
3723           mng_info->ob[object_id]->height=ping_height;
3724           mng_info->ob[object_id]->color_type=ping_color_type;
3725           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3726           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3727           mng_info->ob[object_id]->compression_method=
3728              ping_compression_method;
3729           mng_info->ob[object_id]->filter_method=ping_filter_method;
3730
3731           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3732             {
3733               png_colorp
3734                 plte;
3735
3736               /*
3737                 Copy the PLTE to the object buffer.
3738               */
3739               png_get_PLTE(ping,ping_info,&plte,&number_colors);
3740               mng_info->ob[object_id]->plte_length=number_colors;
3741
3742               for (i=0; i < number_colors; i++)
3743               {
3744                 mng_info->ob[object_id]->plte[i]=plte[i];
3745               }
3746             }
3747
3748           else
3749               mng_info->ob[object_id]->plte_length=0;
3750         }
3751     }
3752 #endif
3753
3754    /* Set image->alpha_trait to MagickTrue if the input colortype supports
3755     * alpha or if a valid tRNS chunk is present, no matter whether there
3756     * is actual transparency present.
3757     */
3758     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3759         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3760         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3761         BlendPixelTrait : UndefinedPixelTrait;
3762
3763 #if 0  /* I'm not sure what's wrong here but it does not work. */
3764     if (image->alpha_trait != UndefinedPixelTrait)
3765     {
3766       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3767         (void) SetImageType(image,GrayscaleAlphaType,exception);
3768
3769       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3770         (void) SetImageType(image,PaletteAlphaType,exception);
3771
3772       else
3773         (void) SetImageType(image,TrueColorAlphaType,exception);
3774     }
3775
3776     else
3777     {
3778       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3779         (void) SetImageType(image,GrayscaleType,exception);
3780
3781       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3782         (void) SetImageType(image,PaletteType,exception);
3783
3784       else
3785         (void) SetImageType(image,TrueColorType,exception);
3786     }
3787 #endif
3788
3789    /* Set more properties for identify to retrieve */
3790    {
3791      char
3792        msg[MaxTextExtent];
3793
3794      if (num_text_total != 0)
3795        {
3796          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3797          (void) FormatLocaleString(msg,MaxTextExtent,
3798             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3799          (void) SetImageProperty(image,"png:text",msg,
3800                 exception);
3801        }
3802
3803      if (num_raw_profiles != 0)
3804        {
3805          (void) FormatLocaleString(msg,MaxTextExtent,
3806             "%d were found", num_raw_profiles);
3807          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3808                 exception);
3809        }
3810
3811      if (ping_found_cHRM != MagickFalse)
3812        {
3813          (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3814             "chunk was found (see Chromaticity, above)");
3815          (void) SetImageProperty(image,"png:cHRM",msg,
3816                 exception);
3817        }
3818
3819      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3820        {
3821          (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3822             "chunk was found (see Background color, above)");
3823          (void) SetImageProperty(image,"png:bKGD",msg,
3824                 exception);
3825        }
3826
3827      (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3828         "chunk was found");
3829
3830 #if defined(PNG_iCCP_SUPPORTED)
3831      if (ping_found_iCCP != MagickFalse)
3832         (void) SetImageProperty(image,"png:iCCP",msg,
3833                 exception);
3834 #endif
3835
3836      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3837         (void) SetImageProperty(image,"png:tRNS",msg,
3838                 exception);
3839
3840 #if defined(PNG_sRGB_SUPPORTED)
3841      if (ping_found_sRGB != MagickFalse)
3842        {
3843          (void) FormatLocaleString(msg,MaxTextExtent,
3844             "intent=%d (%s)",
3845             (int) intent,
3846             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3847          (void) SetImageProperty(image,"png:sRGB",msg,
3848                  exception);
3849        }
3850 #endif
3851
3852      if (ping_found_gAMA != MagickFalse)
3853        {
3854          (void) FormatLocaleString(msg,MaxTextExtent,
3855             "gamma=%.8g (See Gamma, above)",
3856             file_gamma);
3857          (void) SetImageProperty(image,"png:gAMA",msg,
3858                 exception);
3859        }
3860
3861 #if defined(PNG_pHYs_SUPPORTED)
3862      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3863        {
3864          (void) FormatLocaleString(msg,MaxTextExtent,
3865             "x_res=%.10g, y_res=%.10g, units=%d",
3866             (double) x_resolution,(double) y_resolution, unit_type);
3867          (void) SetImageProperty(image,"png:pHYs",msg,
3868                 exception);
3869        }
3870 #endif
3871
3872 #if defined(PNG_oFFs_SUPPORTED)
3873      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3874        {
3875          (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3876             (double) image->page.x,(double) image->page.y);
3877          (void) SetImageProperty(image,"png:oFFs",msg,
3878                 exception);
3879        }
3880 #endif
3881
3882 #if defined(PNG_tIME_SUPPORTED)
3883      read_tIME_chunk(image,ping,end_info,exception);
3884 #endif
3885
3886      if ((image->page.width != 0 && image->page.width != image->columns) ||
3887          (image->page.height != 0 && image->page.height != image->rows))
3888        {
3889          (void) FormatLocaleString(msg,MaxTextExtent,
3890             "width=%.20g, height=%.20g",
3891             (double) image->page.width,(double) image->page.height);
3892          (void) SetImageProperty(image,"png:vpAg",msg,
3893                 exception);
3894        }
3895    }
3896
3897   /*
3898     Relinquish resources.
3899   */
3900   png_destroy_read_struct(&ping,&ping_info,&end_info);
3901
3902   pixel_info=RelinquishVirtualMemory(pixel_info);
3903
3904   if (logging != MagickFalse)
3905     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3906       "  exit ReadOnePNGImage()");
3907
3908 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3909   UnlockSemaphoreInfo(ping_semaphore);
3910 #endif
3911
3912   /* }  for navigation to beginning of SETJMP-protected block, revert to
3913    *    Throwing an Exception when an error occurs.
3914    */
3915
3916   return(image);
3917
3918 /* end of reading one PNG image */
3919 }
3920
3921 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3922 {
3923   Image
3924     *image;
3925
3926   MagickBooleanType
3927     have_mng_structure,
3928     logging,
3929     status;
3930
3931   MngInfo
3932     *mng_info;
3933
3934   char
3935     magic_number[MaxTextExtent];
3936
3937   ssize_t
3938     count;
3939
3940   /*
3941     Open image file.
3942   */
3943   assert(image_info != (const ImageInfo *) NULL);
3944   assert(image_info->signature == MagickSignature);
3945
3946   if (image_info->debug != MagickFalse)
3947     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3948       image_info->filename);
3949
3950   assert(exception != (ExceptionInfo *) NULL);
3951   assert(exception->signature == MagickSignature);
3952   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3953   image=AcquireImage(image_info,exception);
3954   mng_info=(MngInfo *) NULL;
3955   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3956
3957   if (status == MagickFalse)
3958     ThrowReaderException(FileOpenError,"UnableToOpenFile");
3959
3960   /*
3961     Verify PNG signature.
3962   */
3963   count=ReadBlob(image,8,(unsigned char *) magic_number);
3964
3965   if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3966     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3967
3968   /*
3969     Allocate a MngInfo structure.
3970   */
3971   have_mng_structure=MagickFalse;
3972   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3973
3974   if (mng_info == (MngInfo *) NULL)
3975     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3976
3977   /*
3978     Initialize members of the MngInfo structure.
3979   */
3980   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3981   mng_info->image=image;
3982   have_mng_structure=MagickTrue;
3983
3984   image=ReadOnePNGImage(mng_info,image_info,exception);
3985   MngInfoFreeStruct(mng_info,&have_mng_structure);
3986
3987   if (image == (Image *) NULL)
3988     {
3989       if (logging != MagickFalse)
3990         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3991           "exit ReadPNGImage() with error");
3992
3993       return((Image *) NULL);
3994     }
3995
3996   (void) CloseBlob(image);
3997
3998   if ((image->columns == 0) || (image->rows == 0))
3999     {
4000       if (logging != MagickFalse)
4001         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4002           "exit ReadPNGImage() with error.");
4003
4004       ThrowReaderException(CorruptImageError,"CorruptImage");
4005     }
4006
4007   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4008       ((image->gamma < .45) || (image->gamma > .46)) &&
4009            !(image->chromaticity.red_primary.x>0.6399f &&
4010            image->chromaticity.red_primary.x<0.6401f &&
4011            image->chromaticity.red_primary.y>0.3299f &&
4012            image->chromaticity.red_primary.y<0.3301f &&
4013            image->chromaticity.green_primary.x>0.2999f &&
4014            image->chromaticity.green_primary.x<0.3001f &&
4015            image->chromaticity.green_primary.y>0.5999f &&
4016            image->chromaticity.green_primary.y<0.6001f &&
4017            image->chromaticity.blue_primary.x>0.1499f &&
4018            image->chromaticity.blue_primary.x<0.1501f &&
4019            image->chromaticity.blue_primary.y>0.0599f &&
4020            image->chromaticity.blue_primary.y<0.0601f &&
4021            image->chromaticity.white_point.x>0.3126f &&
4022            image->chromaticity.white_point.x<0.3128f &&
4023            image->chromaticity.white_point.y>0.3289f &&
4024            image->chromaticity.white_point.y<0.3291f))
4025     {
4026        SetImageColorspace(image,RGBColorspace,exception);
4027     }
4028
4029   if (logging != MagickFalse)
4030     {
4031        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4032            "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4033                (double) image->page.width,(double) image->page.height,
4034                (double) image->page.x,(double) image->page.y);
4035        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4036            "  image->colorspace: %d", (int) image->colorspace);
4037     }
4038
4039   if (logging != MagickFalse)
4040     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4041
4042   return(image);
4043 }
4044
4045
4046
4047 #if defined(JNG_SUPPORTED)
4048 /*
4049 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4050 %                                                                             %
4051 %                                                                             %
4052 %                                                                             %
4053 %   R e a d O n e J N G I m a g e                                             %
4054 %                                                                             %
4055 %                                                                             %
4056 %                                                                             %
4057 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4058 %
4059 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4060 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
4061 %  necessary for the new Image structure and returns a pointer to the new
4062 %  image.
4063 %
4064 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4065 %
4066 %  The format of the ReadOneJNGImage method is:
4067 %
4068 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4069 %         ExceptionInfo *exception)
4070 %
4071 %  A description of each parameter follows:
4072 %
4073 %    o mng_info: Specifies a pointer to a MngInfo structure.
4074 %
4075 %    o image_info: the image info.
4076 %
4077 %    o exception: return any errors or warnings in this structure.
4078 %
4079 */
4080 static Image *ReadOneJNGImage(MngInfo *mng_info,
4081     const ImageInfo *image_info, ExceptionInfo *exception)
4082 {
4083   Image
4084     *alpha_image,
4085     *color_image,
4086     *image,
4087     *jng_image;
4088
4089   ImageInfo
4090     *alpha_image_info,
4091     *color_image_info;
4092
4093   MagickBooleanType
4094     logging;
4095
4096   ssize_t
4097     y;
4098
4099   MagickBooleanType
4100     status;
4101
4102   png_uint_32
4103     jng_height,
4104     jng_width;
4105
4106   png_byte
4107     jng_color_type,
4108     jng_image_sample_depth,
4109     jng_image_compression_method,
4110     jng_image_interlace_method,
4111     jng_alpha_sample_depth,
4112     jng_alpha_compression_method,
4113     jng_alpha_filter_method,
4114     jng_alpha_interlace_method;
4115
4116   register const Quantum
4117     *s;
4118
4119   register ssize_t
4120     i,
4121     x;
4122
4123   register Quantum
4124     *q;
4125
4126   register unsigned char
4127     *p;
4128
4129   unsigned int
4130     read_JSEP,
4131     reading_idat;
4132
4133   size_t
4134     length;
4135
4136   jng_alpha_compression_method=0;
4137   jng_alpha_sample_depth=8;
4138   jng_color_type=0;
4139   jng_height=0;
4140   jng_width=0;
4141   alpha_image=(Image *) NULL;
4142   color_image=(Image *) NULL;
4143   alpha_image_info=(ImageInfo *) NULL;
4144   color_image_info=(ImageInfo *) NULL;
4145
4146   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4147     "  Enter ReadOneJNGImage()");
4148
4149   image=mng_info->image;
4150
4151   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4152     {
4153       /*
4154         Allocate next image structure.
4155       */
4156       if (logging != MagickFalse)
4157         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4158            "  AcquireNextImage()");
4159
4160       AcquireNextImage(image_info,image,exception);
4161
4162       if (GetNextImageInList(image) == (Image *) NULL)
4163         return((Image *) NULL);
4164
4165       image=SyncNextImageInList(image);
4166     }
4167   mng_info->image=image;
4168
4169   /*
4170     Signature bytes have already been read.
4171   */
4172
4173   read_JSEP=MagickFalse;
4174   reading_idat=MagickFalse;
4175   for (;;)
4176   {
4177     char
4178       type[MaxTextExtent];
4179
4180     unsigned char
4181       *chunk;
4182
4183     unsigned int
4184       count;
4185
4186     /*
4187       Read a new JNG chunk.
4188     */
4189     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4190       2*GetBlobSize(image));
4191
4192     if (status == MagickFalse)
4193       break;
4194
4195     type[0]='\0';
4196     (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4197     length=ReadBlobMSBLong(image);
4198     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4199
4200     if (logging != MagickFalse)
4201       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4202         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4203         type[0],type[1],type[2],type[3],(double) length);
4204
4205     if (length > PNG_UINT_31_MAX || count == 0)
4206       ThrowReaderException(CorruptImageError,"CorruptImage");
4207
4208     p=NULL;
4209     chunk=(unsigned char *) NULL;
4210
4211     if (length != 0)
4212       {
4213         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4214
4215         if (chunk == (unsigned char *) NULL)
4216           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4217
4218         for (i=0; i < (ssize_t) length; i++)
4219           chunk[i]=(unsigned char) ReadBlobByte(image);
4220
4221         p=chunk;
4222       }
4223
4224     (void) ReadBlobMSBLong(image);  /* read crc word */
4225
4226     if (memcmp(type,mng_JHDR,4) == 0)
4227       {
4228         if (length == 16)
4229           {
4230             jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4231               (p[2] << 8) | p[3]);
4232             jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4233               (p[6] << 8) | p[7]);
4234             if ((jng_width == 0) || (jng_height == 0))
4235               ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
4236             jng_color_type=p[8];
4237             jng_image_sample_depth=p[9];
4238             jng_image_compression_method=p[10];
4239             jng_image_interlace_method=p[11];
4240
4241             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4242               NoInterlace;
4243
4244             jng_alpha_sample_depth=p[12];
4245             jng_alpha_compression_method=p[13];
4246             jng_alpha_filter_method=p[14];
4247             jng_alpha_interlace_method=p[15];
4248
4249             if (logging != MagickFalse)
4250               {
4251                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4252                   "    jng_width:      %16lu,    jng_height:     %16lu\n"
4253                   "    jng_color_type: %16d,     jng_image_sample_depth: %3d\n"
4254                   "    jng_image_compression_method:%3d",
4255                   (unsigned long) jng_width, (unsigned long) jng_height,
4256                   jng_color_type, jng_image_sample_depth,
4257                   jng_image_compression_method);
4258
4259                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4260                   "    jng_image_interlace_method:  %3d"
4261                   "    jng_alpha_sample_depth:      %3d",
4262                   jng_image_interlace_method,
4263                   jng_alpha_sample_depth);
4264
4265                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4266                   "    jng_alpha_compression_method:%3d\n"
4267                   "    jng_alpha_filter_method:     %3d\n"
4268                   "    jng_alpha_interlace_method:  %3d",
4269                   jng_alpha_compression_method,
4270                   jng_alpha_filter_method,
4271                   jng_alpha_interlace_method);
4272               }
4273           }
4274
4275         if (length != 0)
4276           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4277
4278         continue;
4279       }
4280
4281
4282     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4283         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4284          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4285       {
4286         /*
4287            o create color_image
4288            o open color_blob, attached to color_image
4289            o if (color type has alpha)
4290                open alpha_blob, attached to alpha_image
4291         */
4292
4293         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4294
4295         if (color_image_info == (ImageInfo *) NULL)
4296           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4297
4298         GetImageInfo(color_image_info);
4299         color_image=AcquireImage(color_image_info,exception);
4300
4301         if (color_image == (Image *) NULL)
4302           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4303
4304         if (logging != MagickFalse)
4305           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4306             "    Creating color_blob.");
4307
4308         (void) AcquireUniqueFilename(color_image->filename);
4309         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4310           exception);
4311
4312         if (status == MagickFalse)
4313           return((Image *) NULL);
4314
4315         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4316           {
4317             alpha_image_info=(ImageInfo *)
4318               AcquireMagickMemory(sizeof(ImageInfo));
4319
4320             if (alpha_image_info == (ImageInfo *) NULL)
4321               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4322
4323             GetImageInfo(alpha_image_info);
4324             alpha_image=AcquireImage(alpha_image_info,exception);
4325
4326             if (alpha_image == (Image *) NULL)
4327               {
4328                 alpha_image=DestroyImage(alpha_image);
4329                 ThrowReaderException(ResourceLimitError,
4330                   "MemoryAllocationFailed");
4331               }
4332
4333             if (logging != MagickFalse)
4334               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4335                 "    Creating alpha_blob.");
4336
4337             (void) AcquireUniqueFilename(alpha_image->filename);
4338             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4339               exception);
4340
4341             if (status == MagickFalse)
4342               return((Image *) NULL);
4343
4344             if (jng_alpha_compression_method == 0)
4345               {
4346                 unsigned char
4347                   data[18];
4348
4349                 if (logging != MagickFalse)
4350                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4351                     "    Writing IHDR chunk to alpha_blob.");
4352
4353                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4354                   "\211PNG\r\n\032\n");
4355
4356                 (void) WriteBlobMSBULong(alpha_image,13L);
4357                 PNGType(data,mng_IHDR);
4358                 LogPNGChunk(logging,mng_IHDR,13L);
4359                 PNGLong(data+4,jng_width);
4360                 PNGLong(data+8,jng_height);
4361                 data[12]=jng_alpha_sample_depth;
4362                 data[13]=0; /* color_type gray */
4363                 data[14]=0; /* compression method 0 */
4364                 data[15]=0; /* filter_method 0 */
4365                 data[16]=0; /* interlace_method 0 */
4366                 (void) WriteBlob(alpha_image,17,data);
4367                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4368               }
4369           }
4370         reading_idat=MagickTrue;
4371       }
4372
4373     if (memcmp(type,mng_JDAT,4) == 0)
4374       {
4375         /* Copy chunk to color_image->blob */
4376
4377         if (logging != MagickFalse)
4378           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4379             "    Copying JDAT chunk data to color_blob.");
4380
4381         (void) WriteBlob(color_image,length,chunk);
4382
4383         if (length != 0)
4384           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4385
4386         continue;
4387       }
4388
4389     if (memcmp(type,mng_IDAT,4) == 0)
4390       {
4391         png_byte
4392            data[5];
4393
4394         /* Copy IDAT header and chunk data to alpha_image->blob */
4395
4396         if (alpha_image != NULL && image_info->ping == MagickFalse)
4397           {
4398             if (logging != MagickFalse)
4399               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4400                 "    Copying IDAT chunk data to alpha_blob.");
4401
4402             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4403             PNGType(data,mng_IDAT);
4404             LogPNGChunk(logging,mng_IDAT,length);
4405             (void) WriteBlob(alpha_image,4,data);
4406             (void) WriteBlob(alpha_image,length,chunk);
4407             (void) WriteBlobMSBULong(alpha_image,
4408               crc32(crc32(0,data,4),chunk,(uInt) length));
4409           }
4410
4411         if (length != 0)
4412           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4413
4414         continue;
4415       }
4416
4417     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4418       {
4419         /* Copy chunk data to alpha_image->blob */
4420
4421         if (alpha_image != NULL && image_info->ping == MagickFalse)
4422           {
4423             if (logging != MagickFalse)
4424               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4425                 "    Copying JDAA chunk data to alpha_blob.");
4426
4427             (void) WriteBlob(alpha_image,length,chunk);
4428           }
4429
4430         if (length != 0)
4431           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4432
4433         continue;
4434       }
4435
4436     if (memcmp(type,mng_JSEP,4) == 0)
4437       {
4438         read_JSEP=MagickTrue;
4439
4440         if (length != 0)
4441           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4442
4443         continue;
4444       }
4445
4446     if (memcmp(type,mng_bKGD,4) == 0)
4447       {
4448         if (length == 2)
4449           {
4450             image->background_color.red=ScaleCharToQuantum(p[1]);
4451             image->background_color.green=image->background_color.red;
4452             image->background_color.blue=image->background_color.red;
4453           }
4454
4455         if (length == 6)
4456           {
4457             image->background_color.red=ScaleCharToQuantum(p[1]);
4458             image->background_color.green=ScaleCharToQuantum(p[3]);
4459             image->background_color.blue=ScaleCharToQuantum(p[5]);
4460           }
4461
4462         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4463         continue;
4464       }
4465
4466     if (memcmp(type,mng_gAMA,4) == 0)
4467       {
4468         if (length == 4)
4469           image->gamma=((float) mng_get_long(p))*0.00001;
4470
4471         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4472         continue;
4473       }
4474
4475     if (memcmp(type,mng_cHRM,4) == 0)
4476       {
4477         if (length == 32)
4478           {
4479             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4480             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4481             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4482             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4483             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4484             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4485             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4486             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4487           }
4488
4489         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4490         continue;
4491       }
4492
4493     if (memcmp(type,mng_sRGB,4) == 0)
4494       {
4495         if (length == 1)
4496           {
4497             image->rendering_intent=
4498               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4499             image->gamma=1.000f/2.200f;
4500             image->chromaticity.red_primary.x=0.6400f;
4501             image->chromaticity.red_primary.y=0.3300f;
4502             image->chromaticity.green_primary.x=0.3000f;
4503             image->chromaticity.green_primary.y=0.6000f;
4504             image->chromaticity.blue_primary.x=0.1500f;
4505             image->chromaticity.blue_primary.y=0.0600f;
4506             image->chromaticity.white_point.x=0.3127f;
4507             image->chromaticity.white_point.y=0.3290f;
4508           }
4509
4510         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4511         continue;
4512       }
4513
4514     if (memcmp(type,mng_oFFs,4) == 0)
4515       {
4516         if (length > 8)
4517           {
4518             image->page.x=(ssize_t) mng_get_long(p);
4519             image->page.y=(ssize_t) mng_get_long(&p[4]);
4520
4521             if ((int) p[8] != 0)
4522               {
4523                 image->page.x/=10000;
4524                 image->page.y/=10000;
4525               }
4526           }
4527
4528         if (length != 0)
4529           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4530
4531         continue;
4532       }
4533
4534     if (memcmp(type,mng_pHYs,4) == 0)
4535       {
4536         if (length > 8)
4537           {
4538             image->resolution.x=(double) mng_get_long(p);
4539             image->resolution.y=(double) mng_get_long(&p[4]);
4540             if ((int) p[8] == PNG_RESOLUTION_METER)
4541               {
4542                 image->units=PixelsPerCentimeterResolution;
4543                 image->resolution.x=image->resolution.x/100.0f;
4544                 image->resolution.y=image->resolution.y/100.0f;
4545               }
4546           }
4547
4548         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4549         continue;
4550       }
4551
4552 #if 0
4553     if (memcmp(type,mng_iCCP,4) == 0)
4554       {
4555         /* To do: */
4556         if (length != 0)
4557           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4558
4559         continue;
4560       }
4561 #endif
4562
4563     if (length != 0)
4564       chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4565
4566     if (memcmp(type,mng_IEND,4))
4567       continue;
4568
4569     break;
4570   }
4571
4572
4573   /* IEND found */
4574
4575   /*
4576     Finish up reading image data:
4577
4578        o read main image from color_blob.
4579
4580        o close color_blob.
4581
4582        o if (color_type has alpha)
4583             if alpha_encoding is PNG
4584                read secondary image from alpha_blob via ReadPNG
4585             if alpha_encoding is JPEG
4586                read secondary image from alpha_blob via ReadJPEG
4587
4588        o close alpha_blob.
4589
4590        o copy intensity of secondary image into
4591          alpha samples of main image.
4592
4593        o destroy the secondary image.
4594   */
4595
4596   (void) CloseBlob(color_image);
4597
4598   if (logging != MagickFalse)
4599     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4600       "    Reading jng_image from color_blob.");
4601
4602   assert(color_image_info != (ImageInfo *) NULL);
4603   (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4604     color_image->filename);
4605
4606   color_image_info->ping=MagickFalse;   /* To do: avoid this */
4607   jng_image=ReadImage(color_image_info,exception);
4608
4609   if (jng_image == (Image *) NULL)
4610     return((Image *) NULL);
4611
4612   (void) RelinquishUniqueFileResource(color_image->filename);
4613   color_image=DestroyImage(color_image);
4614   color_image_info=DestroyImageInfo(color_image_info);
4615
4616   if (jng_image == (Image *) NULL)
4617     return((Image *) NULL);
4618
4619   if (logging != MagickFalse)
4620     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4621       "    Copying jng_image pixels to main image.");
4622
4623   image->rows=jng_height;
4624   image->columns=jng_width;
4625
4626   for (y=0; y < (ssize_t) image->rows; y++)
4627   {
4628     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4629     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4630     for (x=(ssize_t) image->columns; x != 0; x--)
4631     {
4632       SetPixelRed(image,GetPixelRed(jng_image,s),q);
4633       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4634       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4635       q+=GetPixelChannels(image);
4636       s+=GetPixelChannels(jng_image);
4637     }
4638
4639     if (SyncAuthenticPixels(image,exception) == MagickFalse)
4640       break;
4641   }
4642
4643   jng_image=DestroyImage(jng_image);
4644
4645   if (image_info->ping == MagickFalse)
4646     {
4647      if (jng_color_type >= 12)
4648        {
4649          if (jng_alpha_compression_method == 0)
4650            {
4651              png_byte
4652                data[5];
4653              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4654              PNGType(data,mng_IEND);
4655              LogPNGChunk(logging,mng_IEND,0L);
4656              (void) WriteBlob(alpha_image,4,data);
4657              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4658            }
4659
4660          (void) CloseBlob(alpha_image);
4661
4662          if (logging != MagickFalse)
4663            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4664              "    Reading alpha from alpha_blob.");
4665
4666          (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4667            "%s",alpha_image->filename);
4668
4669          jng_image=ReadImage(alpha_image_info,exception);
4670
4671          if (jng_image != (Image *) NULL)
4672            for (y=0; y < (ssize_t) image->rows; y++)
4673            {
4674              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4675                exception);
4676              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4677
4678              if (image->alpha_trait != UndefinedPixelTrait)
4679                for (x=(ssize_t) image->columns; x != 0; x--)
4680                {
4681                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4682                   q+=GetPixelChannels(image);
4683                   s+=GetPixelChannels(jng_image);
4684                }
4685
4686              else
4687                for (x=(ssize_t) image->columns; x != 0; x--)
4688                {
4689                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4690                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
4691                     image->alpha_trait=BlendPixelTrait;
4692                   q+=GetPixelChannels(image);
4693                   s+=GetPixelChannels(jng_image);
4694                }
4695
4696              if (SyncAuthenticPixels(image,exception) == MagickFalse)
4697                break;
4698            }
4699          (void) RelinquishUniqueFileResource(alpha_image->filename);
4700          alpha_image=DestroyImage(alpha_image);
4701          alpha_image_info=DestroyImageInfo(alpha_image_info);
4702          if (jng_image != (Image *) NULL)
4703            jng_image=DestroyImage(jng_image);
4704        }
4705     }
4706
4707   /* Read the JNG image.  */
4708
4709   if (mng_info->mng_type == 0)
4710     {
4711       mng_info->mng_width=jng_width;
4712       mng_info->mng_height=jng_height;
4713     }
4714
4715   if (image->page.width == 0 && image->page.height == 0)
4716     {
4717       image->page.width=jng_width;
4718       image->page.height=jng_height;
4719     }
4720
4721   if (image->page.x == 0 && image->page.y == 0)
4722     {
4723       image->page.x=mng_info->x_off[mng_info->object_id];
4724       image->page.y=mng_info->y_off[mng_info->object_id];
4725     }
4726
4727   else
4728     {
4729       image->page.y=mng_info->y_off[mng_info->object_id];
4730     }
4731
4732   mng_info->image_found++;
4733   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4734     2*GetBlobSize(image));
4735
4736   if (status == MagickFalse)
4737     return((Image *) NULL);
4738
4739   if (logging != MagickFalse)
4740     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4741       "  exit ReadOneJNGImage()");
4742
4743   return(image);
4744 }
4745
4746 /*
4747 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4748 %                                                                             %
4749 %                                                                             %
4750 %                                                                             %
4751 %   R e a d J N G I m a g e                                                   %
4752 %                                                                             %
4753 %                                                                             %
4754 %                                                                             %
4755 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4756 %
4757 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4758 %  (including the 8-byte signature)  and returns it.  It allocates the memory
4759 %  necessary for the new Image structure and returns a pointer to the new
4760 %  image.
4761 %
4762 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4763 %
4764 %  The format of the ReadJNGImage method is:
4765 %
4766 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4767 %         *exception)
4768 %
4769 %  A description of each parameter follows:
4770 %
4771 %    o image_info: the image info.
4772 %
4773 %    o exception: return any errors or warnings in this structure.
4774 %
4775 */
4776
4777 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4778 {
4779   Image
4780     *image;
4781
4782   MagickBooleanType
4783     have_mng_structure,
4784     logging,
4785     status;
4786
4787   MngInfo
4788     *mng_info;
4789
4790   char
4791     magic_number[MaxTextExtent];
4792
4793   size_t
4794     count;
4795
4796   /*
4797     Open image file.
4798   */
4799   assert(image_info != (const ImageInfo *) NULL);
4800   assert(image_info->signature == MagickSignature);
4801   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4802   assert(exception != (ExceptionInfo *) NULL);
4803   assert(exception->signature == MagickSignature);
4804   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4805   image=AcquireImage(image_info,exception);
4806   mng_info=(MngInfo *) NULL;
4807   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4808
4809   if (status == MagickFalse)
4810     return((Image *) NULL);
4811
4812   if (LocaleCompare(image_info->magick,"JNG") != 0)
4813     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4814
4815   /* Verify JNG signature.  */
4816
4817   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4818
4819   if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4820     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4821
4822   /* Allocate a MngInfo structure.  */
4823
4824   have_mng_structure=MagickFalse;
4825   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4826
4827   if (mng_info == (MngInfo *) NULL)
4828     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4829
4830   /* Initialize members of the MngInfo structure.  */
4831
4832   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4833   have_mng_structure=MagickTrue;
4834
4835   mng_info->image=image;
4836   image=ReadOneJNGImage(mng_info,image_info,exception);
4837   MngInfoFreeStruct(mng_info,&have_mng_structure);
4838
4839   if (image == (Image *) NULL)
4840     {
4841       if (logging != MagickFalse)
4842         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4843           "exit ReadJNGImage() with error");
4844
4845       return((Image *) NULL);
4846     }
4847   (void) CloseBlob(image);
4848
4849   if (image->columns == 0 || image->rows == 0)
4850     {
4851       if (logging != MagickFalse)
4852         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4853           "exit ReadJNGImage() with error");
4854
4855       ThrowReaderException(CorruptImageError,"CorruptImage");
4856     }
4857
4858   if (logging != MagickFalse)
4859     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4860
4861   return(image);
4862 }
4863 #endif
4864
4865 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4866 {
4867   char
4868     page_geometry[MaxTextExtent];
4869
4870   Image
4871     *image;
4872
4873   MagickBooleanType
4874     logging,
4875     have_mng_structure;
4876
4877   volatile int
4878     first_mng_object,
4879     object_id,
4880     term_chunk_found,
4881     skip_to_iend;
4882
4883   volatile ssize_t
4884     image_count=0;
4885
4886   MagickBooleanType
4887     status;
4888
4889   MagickOffsetType
4890     offset;
4891
4892   MngInfo
4893     *mng_info;
4894
4895   MngBox
4896     default_fb,
4897     fb,
4898     previous_fb;
4899
4900 #if defined(MNG_INSERT_LAYERS)
4901   PixelInfo
4902     mng_background_color;
4903 #endif
4904
4905   register unsigned char
4906     *p;
4907
4908   register ssize_t
4909     i;
4910
4911   size_t
4912     count;
4913
4914   ssize_t
4915     loop_level;
4916
4917   volatile short
4918     skipping_loop;
4919
4920 #if defined(MNG_INSERT_LAYERS)
4921   unsigned int
4922     mandatory_back=0;
4923 #endif
4924
4925   volatile unsigned int
4926 #ifdef MNG_OBJECT_BUFFERS
4927     mng_background_object=0,
4928 #endif
4929     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4930
4931   size_t
4932     default_frame_timeout,
4933     frame_timeout,
4934 #if defined(MNG_INSERT_LAYERS)
4935     image_height,
4936     image_width,
4937 #endif
4938     length;
4939
4940   /* These delays are all measured in image ticks_per_second,
4941    * not in MNG ticks_per_second
4942    */
4943   volatile size_t
4944     default_frame_delay,
4945     final_delay,
4946     final_image_delay,
4947     frame_delay,
4948 #if defined(MNG_INSERT_LAYERS)
4949     insert_layers,
4950 #endif
4951     mng_iterations=1,
4952     simplicity=0,
4953     subframe_height=0,
4954     subframe_width=0;
4955
4956   previous_fb.top=0;
4957   previous_fb.bottom=0;
4958   previous_fb.left=0;
4959   previous_fb.right=0;
4960   default_fb.top=0;
4961   default_fb.bottom=0;
4962   default_fb.left=0;
4963   default_fb.right=0;
4964
4965   /* Open image file.  */
4966
4967   assert(image_info != (const ImageInfo *) NULL);
4968   assert(image_info->signature == MagickSignature);
4969   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4970   assert(exception != (ExceptionInfo *) NULL);
4971   assert(exception->signature == MagickSignature);
4972   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4973   image=AcquireImage(image_info,exception);
4974   mng_info=(MngInfo *) NULL;
4975   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4976
4977   if (status == MagickFalse)
4978     return((Image *) NULL);
4979
4980   first_mng_object=MagickFalse;
4981   skipping_loop=(-1);
4982   have_mng_structure=MagickFalse;
4983
4984   /* Allocate a MngInfo structure.  */
4985
4986   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4987
4988   if (mng_info == (MngInfo *) NULL)
4989     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4990
4991   /* Initialize members of the MngInfo structure.  */
4992
4993   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4994   mng_info->image=image;
4995   have_mng_structure=MagickTrue;
4996
4997   if (LocaleCompare(image_info->magick,"MNG") == 0)
4998     {
4999       char
5000         magic_number[MaxTextExtent];
5001
5002       /* Verify MNG signature.  */
5003       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5004       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5005         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5006
5007       /* Initialize some nonzero members of the MngInfo structure.  */
5008       for (i=0; i < MNG_MAX_OBJECTS; i++)
5009       {
5010         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5011         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5012       }
5013       mng_info->exists[0]=MagickTrue;
5014     }
5015
5016   first_mng_object=MagickTrue;
5017   mng_type=0;
5018 #if defined(MNG_INSERT_LAYERS)
5019   insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5020 #endif
5021   default_frame_delay=0;
5022   default_frame_timeout=0;
5023   frame_delay=0;
5024   final_delay=1;
5025   mng_info->ticks_per_second=1UL*image->ticks_per_second;
5026   object_id=0;
5027   skip_to_iend=MagickFalse;
5028   term_chunk_found=MagickFalse;
5029   mng_info->framing_mode=1;
5030 #if defined(MNG_INSERT_LAYERS)
5031   mandatory_back=MagickFalse;
5032 #endif
5033 #if defined(MNG_INSERT_LAYERS)
5034   mng_background_color=image->background_color;
5035 #endif
5036   default_fb=mng_info->frame;
5037   previous_fb=mng_info->frame;
5038   do
5039   {
5040     char
5041       type[MaxTextExtent];
5042
5043     if (LocaleCompare(image_info->magick,"MNG") == 0)
5044       {
5045         unsigned char
5046           *chunk;
5047
5048         /*
5049           Read a new chunk.
5050         */
5051         type[0]='\0';
5052         (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
5053         length=ReadBlobMSBLong(image);
5054         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5055
5056         if (logging != MagickFalse)
5057           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5058            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
5059            type[0],type[1],type[2],type[3],(double) length);
5060
5061         if (length > PNG_UINT_31_MAX)
5062           {
5063             status=MagickFalse;
5064             break;
5065           }
5066
5067         if (count == 0)
5068           ThrowReaderException(CorruptImageError,"CorruptImage");
5069
5070         p=NULL;
5071         chunk=(unsigned char *) NULL;
5072
5073         if (length != 0)
5074           {
5075             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5076
5077             if (chunk == (unsigned char *) NULL)
5078               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5079
5080             for (i=0; i < (ssize_t) length; i++)
5081               chunk[i]=(unsigned char) ReadBlobByte(image);
5082
5083             p=chunk;
5084           }
5085
5086         (void) ReadBlobMSBLong(image);  /* read crc word */
5087
5088 #if !defined(JNG_SUPPORTED)
5089         if (memcmp(type,mng_JHDR,4) == 0)
5090           {
5091             skip_to_iend=MagickTrue;
5092
5093             if (mng_info->jhdr_warning == 0)
5094               (void) ThrowMagickException(exception,GetMagickModule(),
5095                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5096
5097             mng_info->jhdr_warning++;
5098           }
5099 #endif
5100         if (memcmp(type,mng_DHDR,4) == 0)
5101           {
5102             skip_to_iend=MagickTrue;
5103
5104             if (mng_info->dhdr_warning == 0)
5105               (void) ThrowMagickException(exception,GetMagickModule(),
5106                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5107
5108             mng_info->dhdr_warning++;
5109           }
5110         if (memcmp(type,mng_MEND,4) == 0)
5111           break;
5112
5113         if (skip_to_iend)
5114           {
5115             if (memcmp(type,mng_IEND,4) == 0)
5116               skip_to_iend=MagickFalse;
5117
5118             if (length != 0)
5119               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5120
5121             if (logging != MagickFalse)
5122               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5123                 "  Skip to IEND.");
5124
5125             continue;
5126           }
5127
5128         if (memcmp(type,mng_MHDR,4) == 0)
5129           {
5130             mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5131                 (p[2] << 8) | p[3]);
5132
5133             mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5134                 (p[6] << 8) | p[7]);
5135
5136             if (logging != MagickFalse)
5137               {
5138                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5139                   "  MNG width: %.20g",(double) mng_info->mng_width);
5140                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5141                   "  MNG height: %.20g",(double) mng_info->mng_height);
5142               }
5143
5144             p+=8;
5145             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5146
5147             if (mng_info->ticks_per_second == 0)
5148               default_frame_delay=0;
5149
5150             else
5151               default_frame_delay=1UL*image->ticks_per_second/
5152                 mng_info->ticks_per_second;
5153
5154             frame_delay=default_frame_delay;
5155             simplicity=0;
5156
5157             if (length > 16)
5158               {
5159                 p+=16;
5160                 simplicity=(size_t) mng_get_long(p);
5161               }
5162
5163             mng_type=1;    /* Full MNG */
5164
5165             if ((simplicity != 0) && ((simplicity | 11) == 11))
5166               mng_type=2; /* LC */
5167
5168             if ((simplicity != 0) && ((simplicity | 9) == 9))
5169               mng_type=3; /* VLC */
5170
5171 #if defined(MNG_INSERT_LAYERS)
5172             if (mng_type != 3)
5173               insert_layers=MagickTrue;
5174 #endif
5175             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5176               {
5177                 /* Allocate next image structure.  */
5178                 AcquireNextImage(image_info,image,exception);
5179
5180                 if (GetNextImageInList(image) == (Image *) NULL)
5181                   return((Image *) NULL);
5182
5183                 image=SyncNextImageInList(image);
5184                 mng_info->image=image;
5185               }
5186
5187             if ((mng_info->mng_width > 65535L) ||
5188                 (mng_info->mng_height > 65535L))
5189               ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5190
5191             (void) FormatLocaleString(page_geometry,MaxTextExtent,
5192               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5193               mng_info->mng_height);
5194
5195             mng_info->frame.left=0;
5196             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5197             mng_info->frame.top=0;
5198             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5199             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5200
5201             for (i=0; i < MNG_MAX_OBJECTS; i++)
5202               mng_info->object_clip[i]=mng_info->frame;
5203
5204             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5205             continue;
5206           }
5207
5208         if (memcmp(type,mng_TERM,4) == 0)
5209           {
5210             int
5211               repeat=0;
5212
5213
5214             if (length != 0)
5215               repeat=p[0];
5216
5217             if (repeat == 3)
5218               {
5219                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5220                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5221
5222                 if (mng_iterations == PNG_UINT_31_MAX)
5223                   mng_iterations=0;
5224
5225                 image->iterations=mng_iterations;
5226                 term_chunk_found=MagickTrue;
5227               }
5228
5229             if (logging != MagickFalse)
5230               {
5231                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5232                   "    repeat=%d,  final_delay=%.20g,  iterations=%.20g",
5233                   repeat,(double) final_delay, (double) image->iterations);
5234               }
5235
5236             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5237             continue;
5238           }
5239         if (memcmp(type,mng_DEFI,4) == 0)
5240           {
5241             if (mng_type == 3)
5242               (void) ThrowMagickException(exception,GetMagickModule(),
5243                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5244                 image->filename);
5245
5246             object_id=(p[0] << 8) | p[1];
5247
5248             if (mng_type == 2 && object_id != 0)
5249               (void) ThrowMagickException(exception,GetMagickModule(),
5250                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5251                 image->filename);
5252
5253             if (object_id > MNG_MAX_OBJECTS)
5254               {
5255                 /*
5256                   Instead of using a warning we should allocate a larger
5257                   MngInfo structure and continue.
5258                 */
5259                 (void) ThrowMagickException(exception,GetMagickModule(),
5260                   CoderError,"object id too large","`%s'",image->filename);
5261                 object_id=MNG_MAX_OBJECTS;
5262               }
5263
5264             if (mng_info->exists[object_id])
5265               if (mng_info->frozen[object_id])
5266                 {
5267                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5268                   (void) ThrowMagickException(exception,
5269                     GetMagickModule(),CoderError,
5270                     "DEFI cannot redefine a frozen MNG object","`%s'",
5271                     image->filename);
5272                   continue;
5273                 }
5274
5275             mng_info->exists[object_id]=MagickTrue;
5276
5277             if (length > 2)
5278               mng_info->invisible[object_id]=p[2];
5279
5280             /*
5281               Extract object offset info.
5282             */
5283             if (length > 11)
5284               {
5285                 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5286                     (p[5] << 16) | (p[6] << 8) | p[7]);
5287
5288                 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5289                     (p[9] << 16) | (p[10] << 8) | p[11]);
5290
5291                 if (logging != MagickFalse)
5292                   {
5293                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5294                       "  x_off[%d]: %.20g,  y_off[%d]: %.20g",
5295                       object_id,(double) mng_info->x_off[object_id],
5296                       object_id,(double) mng_info->y_off[object_id]);
5297                   }
5298               }
5299
5300             /*
5301               Extract object clipping info.
5302             */
5303             if (length > 27)
5304               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5305                 &p[12]);
5306
5307             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5308             continue;
5309           }
5310         if (memcmp(type,mng_bKGD,4) == 0)
5311           {
5312             mng_info->have_global_bkgd=MagickFalse;
5313
5314             if (length > 5)
5315               {
5316                 mng_info->mng_global_bkgd.red=
5317                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5318
5319                 mng_info->mng_global_bkgd.green=
5320                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5321
5322                 mng_info->mng_global_bkgd.blue=
5323                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5324
5325                 mng_info->have_global_bkgd=MagickTrue;
5326               }
5327
5328             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5329             continue;
5330           }
5331         if (memcmp(type,mng_BACK,4) == 0)
5332           {
5333 #if defined(MNG_INSERT_LAYERS)
5334             if (length > 6)
5335               mandatory_back=p[6];
5336
5337             else
5338               mandatory_back=0;
5339
5340             if (mandatory_back && length > 5)
5341               {
5342                 mng_background_color.red=
5343                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5344
5345                 mng_background_color.green=
5346                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5347
5348                 mng_background_color.blue=
5349                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5350
5351                 mng_background_color.alpha=OpaqueAlpha;
5352               }
5353
5354 #ifdef MNG_OBJECT_BUFFERS
5355             if (length > 8)
5356               mng_background_object=(p[7] << 8) | p[8];
5357 #endif
5358 #endif
5359             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5360             continue;
5361           }
5362
5363         if (memcmp(type,mng_PLTE,4) == 0)
5364           {
5365             /* Read global PLTE.  */
5366
5367             if (length && (length < 769))
5368               {
5369                 if (mng_info->global_plte == (png_colorp) NULL)
5370                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5371                     sizeof(*mng_info->global_plte));
5372
5373                 for (i=0; i < (ssize_t) (length/3); i++)
5374                 {
5375                   mng_info->global_plte[i].red=p[3*i];
5376                   mng_info->global_plte[i].green=p[3*i+1];
5377                   mng_info->global_plte[i].blue=p[3*i+2];
5378                 }
5379
5380                 mng_info->global_plte_length=(unsigned int) (length/3);
5381               }
5382 #ifdef MNG_LOOSE
5383             for ( ; i < 256; i++)
5384             {
5385               mng_info->global_plte[i].red=i;
5386               mng_info->global_plte[i].green=i;
5387               mng_info->global_plte[i].blue=i;
5388             }
5389
5390             if (length != 0)
5391               mng_info->global_plte_length=256;
5392 #endif
5393             else
5394               mng_info->global_plte_length=0;
5395
5396             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5397             continue;
5398           }
5399
5400         if (memcmp(type,mng_tRNS,4) == 0)
5401           {
5402             /* read global tRNS */
5403
5404             if (length < 257)
5405               for (i=0; i < (ssize_t) length; i++)
5406                 mng_info->global_trns[i]=p[i];
5407
5408 #ifdef MNG_LOOSE
5409             for ( ; i < 256; i++)
5410               mng_info->global_trns[i]=255;
5411 #endif
5412             mng_info->global_trns_length=(unsigned int) length;
5413             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5414             continue;
5415           }
5416         if (memcmp(type,mng_gAMA,4) == 0)
5417           {
5418             if (length == 4)
5419               {
5420                 ssize_t
5421                   igamma;
5422
5423                 igamma=mng_get_long(p);
5424                 mng_info->global_gamma=((float) igamma)*0.00001;
5425                 mng_info->have_global_gama=MagickTrue;
5426               }
5427
5428             else
5429               mng_info->have_global_gama=MagickFalse;
5430
5431             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5432             continue;
5433           }
5434
5435         if (memcmp(type,mng_cHRM,4) == 0)
5436           {
5437             /* Read global cHRM */
5438
5439             if (length == 32)
5440               {
5441                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5442                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5443                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5444                 mng_info->global_chrm.red_primary.y=0.00001*
5445                   mng_get_long(&p[12]);
5446                 mng_info->global_chrm.green_primary.x=0.00001*
5447                   mng_get_long(&p[16]);
5448                 mng_info->global_chrm.green_primary.y=0.00001*
5449                   mng_get_long(&p[20]);
5450                 mng_info->global_chrm.blue_primary.x=0.00001*
5451                   mng_get_long(&p[24]);
5452                 mng_info->global_chrm.blue_primary.y=0.00001*
5453                   mng_get_long(&p[28]);
5454                 mng_info->have_global_chrm=MagickTrue;
5455               }
5456             else
5457               mng_info->have_global_chrm=MagickFalse;
5458
5459             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5460             continue;
5461           }
5462
5463         if (memcmp(type,mng_sRGB,4) == 0)
5464           {
5465             /*
5466               Read global sRGB.
5467             */
5468             if (length != 0)
5469               {
5470                 mng_info->global_srgb_intent=
5471                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5472                 mng_info->have_global_srgb=MagickTrue;
5473               }
5474             else
5475               mng_info->have_global_srgb=MagickFalse;
5476
5477             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5478             continue;
5479           }
5480
5481         if (memcmp(type,mng_iCCP,4) == 0)
5482           {
5483             /* To do: */
5484
5485             /*
5486               Read global iCCP.
5487             */
5488             if (length != 0)
5489               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5490
5491             continue;
5492           }
5493
5494         if (memcmp(type,mng_FRAM,4) == 0)
5495           {
5496             if (mng_type == 3)
5497               (void) ThrowMagickException(exception,GetMagickModule(),
5498                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5499                 image->filename);
5500
5501             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5502               image->delay=frame_delay;
5503
5504             frame_delay=default_frame_delay;
5505             frame_timeout=default_frame_timeout;
5506             fb=default_fb;
5507
5508             if (length != 0)
5509               if (p[0])
5510                 mng_info->framing_mode=p[0];
5511
5512             if (logging != MagickFalse)
5513               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5514                 "    Framing_mode=%d",mng_info->framing_mode);
5515
5516             if (length > 6)
5517               {
5518                 /* Note the delay and frame clipping boundaries.  */
5519
5520                 p++; /* framing mode */
5521
5522                 while (*p && ((p-chunk) < (ssize_t) length))
5523                   p++;  /* frame name */
5524
5525                 p++;  /* frame name terminator */
5526
5527                 if ((p-chunk) < (ssize_t) (length-4))
5528                   {
5529                     int
5530                       change_delay,
5531                       change_timeout,
5532                       change_clipping;
5533
5534                     change_delay=(*p++);
5535                     change_timeout=(*p++);
5536                     change_clipping=(*p++);
5537                     p++; /* change_sync */
5538
5539                     if (change_delay)
5540                       {
5541                         frame_delay=1UL*image->ticks_per_second*
5542                           mng_get_long(p);
5543
5544                         if (mng_info->ticks_per_second != 0)
5545                           frame_delay/=mng_info->ticks_per_second;
5546
5547                         else
5548                           frame_delay=PNG_UINT_31_MAX;
5549
5550                         if (change_delay == 2)
5551                           default_frame_delay=frame_delay;
5552
5553                         p+=4;
5554
5555                         if (logging != MagickFalse)
5556                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5557                             "    Framing_delay=%.20g",(double) frame_delay);
5558                       }
5559
5560                     if (change_timeout)
5561                       {
5562                         frame_timeout=1UL*image->ticks_per_second*
5563                           mng_get_long(p);
5564
5565                         if (mng_info->ticks_per_second != 0)
5566                           frame_timeout/=mng_info->ticks_per_second;
5567
5568                         else
5569                           frame_timeout=PNG_UINT_31_MAX;
5570
5571                         if (change_timeout == 2)
5572                           default_frame_timeout=frame_timeout;
5573
5574                         p+=4;
5575
5576                         if (logging != MagickFalse)
5577                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5578                             "    Framing_timeout=%.20g",(double) frame_timeout);
5579                       }
5580
5581                     if (change_clipping)
5582                       {
5583                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5584                         p+=17;
5585                         previous_fb=fb;
5586
5587                         if (logging != MagickFalse)
5588                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5589                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5590                             (double) fb.left,(double) fb.right,(double) fb.top,
5591                             (double) fb.bottom);
5592
5593                         if (change_clipping == 2)
5594                           default_fb=fb;
5595                       }
5596                   }
5597               }
5598             mng_info->clip=fb;
5599             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5600
5601             subframe_width=(size_t) (mng_info->clip.right
5602                -mng_info->clip.left);
5603
5604             subframe_height=(size_t) (mng_info->clip.bottom
5605                -mng_info->clip.top);
5606             /*
5607               Insert a background layer behind the frame if framing_mode is 4.
5608             */
5609 #if defined(MNG_INSERT_LAYERS)
5610             if (logging != MagickFalse)
5611               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5612                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
5613                 subframe_width,(double) subframe_height);
5614
5615             if (insert_layers && (mng_info->framing_mode == 4) &&
5616                 (subframe_width) && (subframe_height))
5617               {
5618                 /* Allocate next image structure.  */
5619                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5620                   {
5621                     AcquireNextImage(image_info,image,exception);
5622
5623                     if (GetNextImageInList(image) == (Image *) NULL)
5624                       {
5625                         image=DestroyImageList(image);
5626                         MngInfoFreeStruct(mng_info,&have_mng_structure);
5627                         return((Image *) NULL);
5628                       }
5629
5630                     image=SyncNextImageInList(image);
5631                   }
5632
5633                 mng_info->image=image;
5634
5635                 if (term_chunk_found)
5636                   {
5637                     image->start_loop=MagickTrue;
5638                     image->iterations=mng_iterations;
5639                     term_chunk_found=MagickFalse;
5640                   }
5641
5642                 else
5643                     image->start_loop=MagickFalse;
5644
5645                 image->columns=subframe_width;
5646                 image->rows=subframe_height;
5647                 image->page.width=subframe_width;
5648                 image->page.height=subframe_height;
5649                 image->page.x=mng_info->clip.left;
5650                 image->page.y=mng_info->clip.top;
5651                 image->background_color=mng_background_color;
5652                 image->alpha_trait=UndefinedPixelTrait;
5653                 image->delay=0;
5654                 (void) SetImageBackgroundColor(image,exception);
5655
5656                 if (logging != MagickFalse)
5657                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5658                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5659                     (double) mng_info->clip.left,(double) mng_info->clip.right,
5660                     (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5661               }
5662 #endif
5663             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5664             continue;
5665           }
5666
5667         if (memcmp(type,mng_CLIP,4) == 0)
5668           {
5669             unsigned int
5670               first_object,
5671               last_object;
5672
5673             /*
5674               Read CLIP.
5675             */
5676             if (length > 3)
5677               {
5678                 first_object=(p[0] << 8) | p[1];
5679                 last_object=(p[2] << 8) | p[3];
5680                 p+=4;
5681
5682                 for (i=(int) first_object; i <= (int) last_object; i++)
5683                 {
5684                   if (mng_info->exists[i] && !mng_info->frozen[i])
5685                     {
5686                       MngBox
5687                         box;
5688
5689                       box=mng_info->object_clip[i];
5690                       if ((p-chunk) < (ssize_t) (length-17))
5691                         mng_info->object_clip[i]=
5692                            mng_read_box(box,(char) p[0],&p[1]);
5693                     }
5694                 }
5695
5696               }
5697             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5698             continue;
5699           }
5700
5701         if (memcmp(type,mng_SAVE,4) == 0)
5702           {
5703             for (i=1; i < MNG_MAX_OBJECTS; i++)
5704               if (mng_info->exists[i])
5705                 {
5706                  mng_info->frozen[i]=MagickTrue;
5707 #ifdef MNG_OBJECT_BUFFERS
5708                  if (mng_info->ob[i] != (MngBuffer *) NULL)
5709                     mng_info->ob[i]->frozen=MagickTrue;
5710 #endif
5711                 }
5712
5713             if (length != 0)
5714               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5715
5716             continue;
5717           }
5718
5719         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5720           {
5721             /* Read DISC or SEEK.  */
5722
5723             if ((length == 0) || !memcmp(type,mng_SEEK,4))
5724               {
5725                 for (i=1; i < MNG_MAX_OBJECTS; i++)
5726                   MngInfoDiscardObject(mng_info,i);
5727               }
5728
5729             else
5730               {
5731                 register ssize_t
5732                   j;
5733
5734                 for (j=0; j < (ssize_t) length; j+=2)
5735                 {
5736                   i=p[j] << 8 | p[j+1];
5737                   MngInfoDiscardObject(mng_info,i);
5738                 }
5739               }
5740
5741             if (length != 0)
5742               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5743
5744             continue;
5745           }
5746
5747         if (memcmp(type,mng_MOVE,4) == 0)
5748           {
5749             size_t
5750               first_object,
5751               last_object;
5752
5753             /* read MOVE */
5754
5755             if (length > 3)
5756             {
5757               first_object=(p[0] << 8) | p[1];
5758               last_object=(p[2] << 8) | p[3];
5759               p+=4;
5760
5761               for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5762               {
5763                 if (mng_info->exists[i] && !mng_info->frozen[i] &&
5764                     (p-chunk) < (ssize_t) (length-8))
5765                   {
5766                     MngPair
5767                       new_pair;
5768
5769                     MngPair
5770                       old_pair;
5771
5772                     old_pair.a=mng_info->x_off[i];
5773                     old_pair.b=mng_info->y_off[i];
5774                     new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
5775                     mng_info->x_off[i]=new_pair.a;
5776                     mng_info->y_off[i]=new_pair.b;
5777                   }
5778               }
5779             }
5780
5781             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5782             continue;
5783           }
5784
5785         if (memcmp(type,mng_LOOP,4) == 0)
5786           {
5787             ssize_t loop_iters=1;
5788             if (length > 4)
5789               {
5790                 loop_level=chunk[0];
5791                 mng_info->loop_active[loop_level]=1;  /* mark loop active */
5792
5793                 /* Record starting point.  */
5794                 loop_iters=mng_get_long(&chunk[1]);
5795
5796                 if (logging != MagickFalse)
5797                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5798                     "  LOOP level %.20g has %.20g iterations ",
5799                     (double) loop_level, (double) loop_iters);
5800
5801                 if (loop_iters == 0)
5802                   skipping_loop=loop_level;
5803
5804                 else
5805                   {
5806                     mng_info->loop_jump[loop_level]=TellBlob(image);
5807                     mng_info->loop_count[loop_level]=loop_iters;
5808                   }
5809
5810                 mng_info->loop_iteration[loop_level]=0;
5811               }
5812             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5813             continue;
5814           }
5815
5816         if (memcmp(type,mng_ENDL,4) == 0)
5817           {
5818             if (length > 0)
5819               {
5820                 loop_level=chunk[0];
5821
5822                 if (skipping_loop > 0)
5823                   {
5824                     if (skipping_loop == loop_level)
5825                       {
5826                         /*
5827                           Found end of zero-iteration loop.
5828                         */
5829                         skipping_loop=(-1);
5830                         mng_info->loop_active[loop_level]=0;
5831                       }
5832                   }
5833
5834                 else
5835                   {
5836                     if (mng_info->loop_active[loop_level] == 1)
5837                       {
5838                         mng_info->loop_count[loop_level]--;
5839                         mng_info->loop_iteration[loop_level]++;
5840
5841                         if (logging != MagickFalse)
5842                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5843                           "  ENDL: LOOP level %.20g has %.20g remaining iters ",
5844                             (double) loop_level,(double)
5845                             mng_info->loop_count[loop_level]);
5846
5847                         if (mng_info->loop_count[loop_level] != 0)
5848                           {
5849                             offset=
5850                               SeekBlob(image,mng_info->loop_jump[loop_level],
5851                               SEEK_SET);
5852
5853                             if (offset < 0)
5854                               ThrowReaderException(CorruptImageError,
5855                                 "ImproperImageHeader");
5856                           }
5857
5858                         else
5859                           {
5860                             short
5861                               last_level;
5862
5863                             /*
5864                               Finished loop.
5865                             */
5866                             mng_info->loop_active[loop_level]=0;
5867                             last_level=(-1);
5868                             for (i=0; i < loop_level; i++)
5869                               if (mng_info->loop_active[i] == 1)
5870                                 last_level=(short) i;
5871                             loop_level=last_level;
5872                           }
5873                       }
5874                   }
5875               }
5876
5877             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5878             continue;
5879           }
5880
5881         if (memcmp(type,mng_CLON,4) == 0)
5882           {
5883             if (mng_info->clon_warning == 0)
5884               (void) ThrowMagickException(exception,GetMagickModule(),
5885                 CoderError,"CLON is not implemented yet","`%s'",
5886                 image->filename);
5887
5888             mng_info->clon_warning++;
5889           }
5890
5891         if (memcmp(type,mng_MAGN,4) == 0)
5892           {
5893             png_uint_16
5894               magn_first,
5895               magn_last,
5896               magn_mb,
5897               magn_ml,
5898               magn_mr,
5899               magn_mt,
5900               magn_mx,
5901               magn_my,
5902               magn_methx,
5903               magn_methy;
5904
5905             if (length > 1)
5906               magn_first=(p[0] << 8) | p[1];
5907
5908             else
5909               magn_first=0;
5910
5911             if (length > 3)
5912               magn_last=(p[2] << 8) | p[3];
5913
5914             else
5915               magn_last=magn_first;
5916 #ifndef MNG_OBJECT_BUFFERS
5917             if (magn_first || magn_last)
5918               if (mng_info->magn_warning == 0)
5919                 {
5920                   (void) ThrowMagickException(exception,
5921                      GetMagickModule(),CoderError,
5922                      "MAGN is not implemented yet for nonzero objects",
5923                      "`%s'",image->filename);
5924
5925                    mng_info->magn_warning++;
5926                 }
5927 #endif
5928             if (length > 4)
5929               magn_methx=p[4];
5930
5931             else
5932               magn_methx=0;
5933
5934             if (length > 6)
5935               magn_mx=(p[5] << 8) | p[6];
5936
5937             else
5938               magn_mx=1;
5939
5940             if (magn_mx == 0)
5941               magn_mx=1;
5942
5943             if (length > 8)
5944               magn_my=(p[7] << 8) | p[8];
5945
5946             else
5947               magn_my=magn_mx;
5948
5949             if (magn_my == 0)
5950               magn_my=1;
5951
5952             if (length > 10)
5953               magn_ml=(p[9] << 8) | p[10];
5954
5955             else
5956               magn_ml=magn_mx;
5957
5958             if (magn_ml == 0)
5959               magn_ml=1;
5960
5961             if (length > 12)
5962               magn_mr=(p[11] << 8) | p[12];
5963
5964             else
5965               magn_mr=magn_mx;
5966
5967             if (magn_mr == 0)
5968               magn_mr=1;
5969
5970             if (length > 14)
5971               magn_mt=(p[13] << 8) | p[14];
5972
5973             else
5974               magn_mt=magn_my;
5975
5976             if (magn_mt == 0)
5977               magn_mt=1;
5978
5979             if (length > 16)
5980               magn_mb=(p[15] << 8) | p[16];
5981
5982             else
5983               magn_mb=magn_my;
5984
5985             if (magn_mb == 0)
5986               magn_mb=1;
5987
5988             if (length > 17)
5989               magn_methy=p[17];
5990
5991             else
5992               magn_methy=magn_methx;
5993
5994
5995             if (magn_methx > 5 || magn_methy > 5)
5996               if (mng_info->magn_warning == 0)
5997                 {
5998                   (void) ThrowMagickException(exception,
5999                      GetMagickModule(),CoderError,
6000                      "Unknown MAGN method in MNG datastream","`%s'",
6001                      image->filename);
6002
6003                    mng_info->magn_warning++;
6004                 }
6005 #ifdef MNG_OBJECT_BUFFERS
6006           /* Magnify existing objects in the range magn_first to magn_last */
6007 #endif
6008             if (magn_first == 0 || magn_last == 0)
6009               {
6010                 /* Save the magnification factors for object 0 */
6011                 mng_info->magn_mb=magn_mb;
6012                 mng_info->magn_ml=magn_ml;
6013                 mng_info->magn_mr=magn_mr;
6014                 mng_info->magn_mt=magn_mt;
6015                 mng_info->magn_mx=magn_mx;
6016                 mng_info->magn_my=magn_my;
6017                 mng_info->magn_methx=magn_methx;
6018                 mng_info->magn_methy=magn_methy;
6019               }
6020           }
6021
6022         if (memcmp(type,mng_PAST,4) == 0)
6023           {
6024             if (mng_info->past_warning == 0)
6025               (void) ThrowMagickException(exception,GetMagickModule(),
6026                 CoderError,"PAST is not implemented yet","`%s'",
6027                 image->filename);
6028
6029             mng_info->past_warning++;
6030           }
6031
6032         if (memcmp(type,mng_SHOW,4) == 0)
6033           {
6034             if (mng_info->show_warning == 0)
6035               (void) ThrowMagickException(exception,GetMagickModule(),
6036                 CoderError,"SHOW is not implemented yet","`%s'",
6037                 image->filename);
6038
6039             mng_info->show_warning++;
6040           }
6041
6042         if (memcmp(type,mng_sBIT,4) == 0)
6043           {
6044             if (length < 4)
6045               mng_info->have_global_sbit=MagickFalse;
6046
6047             else
6048               {
6049                 mng_info->global_sbit.gray=p[0];
6050                 mng_info->global_sbit.red=p[0];
6051                 mng_info->global_sbit.green=p[1];
6052                 mng_info->global_sbit.blue=p[2];
6053                 mng_info->global_sbit.alpha=p[3];
6054                 mng_info->have_global_sbit=MagickTrue;
6055              }
6056           }
6057         if (memcmp(type,mng_pHYs,4) == 0)
6058           {
6059             if (length > 8)
6060               {
6061                 mng_info->global_x_pixels_per_unit=
6062                     (size_t) mng_get_long(p);
6063                 mng_info->global_y_pixels_per_unit=
6064                     (size_t) mng_get_long(&p[4]);
6065                 mng_info->global_phys_unit_type=p[8];
6066                 mng_info->have_global_phys=MagickTrue;
6067               }
6068
6069             else
6070               mng_info->have_global_phys=MagickFalse;
6071           }
6072         if (memcmp(type,mng_pHYg,4) == 0)
6073           {
6074             if (mng_info->phyg_warning == 0)
6075               (void) ThrowMagickException(exception,GetMagickModule(),
6076                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6077
6078             mng_info->phyg_warning++;
6079           }
6080         if (memcmp(type,mng_BASI,4) == 0)
6081           {
6082             skip_to_iend=MagickTrue;
6083
6084             if (mng_info->basi_warning == 0)
6085               (void) ThrowMagickException(exception,GetMagickModule(),
6086                 CoderError,"BASI is not implemented yet","`%s'",
6087                 image->filename);
6088
6089             mng_info->basi_warning++;
6090 #ifdef MNG_BASI_SUPPORTED
6091             basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6092                (p[2] << 8) | p[3]);
6093             basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6094                (p[6] << 8) | p[7]);
6095             basi_color_type=p[8];
6096             basi_compression_method=p[9];
6097             basi_filter_type=p[10];
6098             basi_interlace_method=p[11];
6099             if (length > 11)
6100               basi_red=(p[12] << 8) & p[13];
6101
6102             else
6103               basi_red=0;
6104
6105             if (length > 13)
6106               basi_green=(p[14] << 8) & p[15];
6107
6108             else
6109               basi_green=0;
6110
6111             if (length > 15)
6112               basi_blue=(p[16] << 8) & p[17];
6113
6114             else
6115               basi_blue=0;
6116
6117             if (length > 17)
6118               basi_alpha=(p[18] << 8) & p[19];
6119
6120             else
6121               {
6122                 if (basi_sample_depth == 16)
6123                   basi_alpha=65535L;
6124                 else
6125                   basi_alpha=255;
6126               }
6127
6128             if (length > 19)
6129               basi_viewable=p[20];
6130
6131             else
6132               basi_viewable=0;
6133
6134 #endif
6135             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6136             continue;
6137           }
6138
6139         if (memcmp(type,mng_IHDR,4)
6140 #if defined(JNG_SUPPORTED)
6141             && memcmp(type,mng_JHDR,4)
6142 #endif
6143             )
6144           {
6145             /* Not an IHDR or JHDR chunk */
6146             if (length != 0)
6147               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6148
6149             continue;
6150           }
6151 /* Process IHDR */
6152         if (logging != MagickFalse)
6153           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6154             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6155
6156         mng_info->exists[object_id]=MagickTrue;
6157         mng_info->viewable[object_id]=MagickTrue;
6158
6159         if (mng_info->invisible[object_id])
6160           {
6161             if (logging != MagickFalse)
6162               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6163                 "  Skipping invisible object");
6164
6165             skip_to_iend=MagickTrue;
6166             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6167             continue;
6168           }
6169 #if defined(MNG_INSERT_LAYERS)
6170         if (length < 8)
6171           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6172
6173         image_width=(size_t) mng_get_long(p);
6174         image_height=(size_t) mng_get_long(&p[4]);
6175 #endif
6176         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6177
6178         /*
6179           Insert a transparent background layer behind the entire animation
6180           if it is not full screen.
6181         */
6182 #if defined(MNG_INSERT_LAYERS)
6183         if (insert_layers && mng_type && first_mng_object)
6184           {
6185             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6186                 (image_width < mng_info->mng_width) ||
6187                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6188                 (image_height < mng_info->mng_height) ||
6189                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6190               {
6191                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6192                   {
6193                     /*
6194                       Allocate next image structure.
6195                     */
6196                     AcquireNextImage(image_info,image,exception);
6197
6198                     if (GetNextImageInList(image) == (Image *) NULL)
6199                       {
6200                         image=DestroyImageList(image);
6201                         MngInfoFreeStruct(mng_info,&have_mng_structure);
6202                         return((Image *) NULL);
6203                       }
6204
6205                     image=SyncNextImageInList(image);
6206                   }
6207                 mng_info->image=image;
6208
6209                 if (term_chunk_found)
6210                   {
6211                     image->start_loop=MagickTrue;
6212                     image->iterations=mng_iterations;
6213                     term_chunk_found=MagickFalse;
6214                   }
6215
6216                 else
6217                     image->start_loop=MagickFalse;
6218
6219                 /* Make a background rectangle.  */
6220
6221                 image->delay=0;
6222                 image->columns=mng_info->mng_width;
6223                 image->rows=mng_info->mng_height;
6224                 image->page.width=mng_info->mng_width;
6225                 image->page.height=mng_info->mng_height;
6226                 image->page.x=0;
6227                 image->page.y=0;
6228                 image->background_color=mng_background_color;
6229                 (void) SetImageBackgroundColor(image,exception);
6230                 if (logging != MagickFalse)
6231                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6232                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6233                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6234               }
6235           }
6236         /*
6237           Insert a background layer behind the upcoming image if
6238           framing_mode is 3, and we haven't already inserted one.
6239         */
6240         if (insert_layers && (mng_info->framing_mode == 3) &&
6241                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6242                 (simplicity & 0x08)))
6243           {
6244             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6245             {
6246               /*
6247                 Allocate next image structure.
6248               */
6249               AcquireNextImage(image_info,image,exception);
6250
6251               if (GetNextImageInList(image) == (Image *) NULL)
6252                 {
6253                   image=DestroyImageList(image);
6254                   MngInfoFreeStruct(mng_info,&have_mng_structure);
6255                   return((Image *) NULL);
6256                 }
6257
6258               image=SyncNextImageInList(image);
6259             }
6260
6261             mng_info->image=image;
6262
6263             if (term_chunk_found)
6264               {
6265                 image->start_loop=MagickTrue;
6266                 image->iterations=mng_iterations;
6267                 term_chunk_found=MagickFalse;
6268               }
6269
6270             else
6271                 image->start_loop=MagickFalse;
6272
6273             image->delay=0;
6274             image->columns=subframe_width;
6275             image->rows=subframe_height;
6276             image->page.width=subframe_width;
6277             image->page.height=subframe_height;
6278             image->page.x=mng_info->clip.left;
6279             image->page.y=mng_info->clip.top;
6280             image->background_color=mng_background_color;
6281             image->alpha_trait=UndefinedPixelTrait;
6282             (void) SetImageBackgroundColor(image,exception);
6283
6284             if (logging != MagickFalse)
6285               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6286                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6287                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6288                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6289           }
6290 #endif /* MNG_INSERT_LAYERS */
6291         first_mng_object=MagickFalse;
6292
6293         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6294           {
6295             /*
6296               Allocate next image structure.
6297             */
6298             AcquireNextImage(image_info,image,exception);
6299
6300             if (GetNextImageInList(image) == (Image *) NULL)
6301               {
6302                 image=DestroyImageList(image);
6303                 MngInfoFreeStruct(mng_info,&have_mng_structure);
6304                 return((Image *) NULL);
6305               }
6306
6307             image=SyncNextImageInList(image);
6308           }
6309         mng_info->image=image;
6310         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6311           GetBlobSize(image));
6312
6313         if (status == MagickFalse)
6314           break;
6315
6316         if (term_chunk_found)
6317           {
6318             image->start_loop=MagickTrue;
6319             term_chunk_found=MagickFalse;
6320           }
6321
6322         else
6323             image->start_loop=MagickFalse;
6324
6325         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6326           {
6327             image->delay=frame_delay;
6328             frame_delay=default_frame_delay;
6329           }
6330
6331         else
6332           image->delay=0;
6333
6334         image->page.width=mng_info->mng_width;
6335         image->page.height=mng_info->mng_height;
6336         image->page.x=mng_info->x_off[object_id];
6337         image->page.y=mng_info->y_off[object_id];
6338         image->iterations=mng_iterations;
6339
6340         /*
6341           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6342         */
6343
6344         if (logging != MagickFalse)
6345           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6346             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6347             type[2],type[3]);
6348
6349         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6350
6351         if (offset < 0)
6352           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6353       }
6354
6355     mng_info->image=image;
6356     mng_info->mng_type=mng_type;
6357     mng_info->object_id=object_id;
6358
6359     if (memcmp(type,mng_IHDR,4) == 0)
6360       image=ReadOnePNGImage(mng_info,image_info,exception);
6361
6362 #if defined(JNG_SUPPORTED)
6363     else
6364       image=ReadOneJNGImage(mng_info,image_info,exception);
6365 #endif
6366
6367     if (image == (Image *) NULL)
6368       {
6369         if (logging != MagickFalse)
6370           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6371             "exit ReadJNGImage() with error");
6372
6373         MngInfoFreeStruct(mng_info,&have_mng_structure);
6374         return((Image *) NULL);
6375       }
6376
6377     if (image->columns == 0 || image->rows == 0)
6378       {
6379         (void) CloseBlob(image);
6380         image=DestroyImageList(image);
6381         MngInfoFreeStruct(mng_info,&have_mng_structure);
6382         return((Image *) NULL);
6383       }
6384
6385     mng_info->image=image;
6386
6387     if (mng_type)
6388       {
6389         MngBox
6390           crop_box;
6391
6392         if (mng_info->magn_methx || mng_info->magn_methy)
6393           {
6394             png_uint_32
6395                magnified_height,
6396                magnified_width;
6397
6398             if (logging != MagickFalse)
6399               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6400                 "  Processing MNG MAGN chunk");
6401
6402             if (mng_info->magn_methx == 1)
6403               {
6404                 magnified_width=mng_info->magn_ml;
6405
6406                 if (image->columns > 1)
6407                    magnified_width += mng_info->magn_mr;
6408
6409                 if (image->columns > 2)
6410                    magnified_width += (png_uint_32)
6411                       ((image->columns-2)*(mng_info->magn_mx));
6412               }
6413
6414             else
6415               {
6416                 magnified_width=(png_uint_32) image->columns;
6417
6418                 if (image->columns > 1)
6419                    magnified_width += mng_info->magn_ml-1;
6420
6421                 if (image->columns > 2)
6422                    magnified_width += mng_info->magn_mr-1;
6423
6424                 if (image->columns > 3)
6425                    magnified_width += (png_uint_32)
6426                       ((image->columns-3)*(mng_info->magn_mx-1));
6427               }
6428
6429             if (mng_info->magn_methy == 1)
6430               {
6431                 magnified_height=mng_info->magn_mt;
6432
6433                 if (image->rows > 1)
6434                    magnified_height += mng_info->magn_mb;
6435
6436                 if (image->rows > 2)
6437                    magnified_height += (png_uint_32)
6438                       ((image->rows-2)*(mng_info->magn_my));
6439               }
6440
6441             else
6442               {
6443                 magnified_height=(png_uint_32) image->rows;
6444
6445                 if (image->rows > 1)
6446                    magnified_height += mng_info->magn_mt-1;
6447
6448                 if (image->rows > 2)
6449                    magnified_height += mng_info->magn_mb-1;
6450
6451                 if (image->rows > 3)
6452                    magnified_height += (png_uint_32)
6453                       ((image->rows-3)*(mng_info->magn_my-1));
6454               }
6455
6456             if (magnified_height > image->rows ||
6457                 magnified_width > image->columns)
6458               {
6459                 Image
6460                   *large_image;
6461
6462                 int
6463                   yy;
6464
6465                 Quantum
6466                   *next,
6467                   *prev;
6468
6469                 png_uint_16
6470                   magn_methx,
6471                   magn_methy;
6472
6473                 ssize_t
6474                   m,
6475                   y;
6476
6477                 register Quantum
6478                   *n,
6479                   *q;
6480
6481                 register ssize_t
6482                   x;
6483
6484                 /* Allocate next image structure.  */
6485
6486                 if (logging != MagickFalse)
6487                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6488                     "    Allocate magnified image");
6489
6490                 AcquireNextImage(image_info,image,exception);
6491
6492                 if (GetNextImageInList(image) == (Image *) NULL)
6493                   {
6494                     image=DestroyImageList(image);
6495                     MngInfoFreeStruct(mng_info,&have_mng_structure);
6496                     return((Image *) NULL);
6497                   }
6498
6499                 large_image=SyncNextImageInList(image);
6500
6501                 large_image->columns=magnified_width;
6502                 large_image->rows=magnified_height;
6503
6504                 magn_methx=mng_info->magn_methx;
6505                 magn_methy=mng_info->magn_methy;
6506
6507 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6508 #define QM unsigned short
6509                 if (magn_methx != 1 || magn_methy != 1)
6510                   {
6511                   /*
6512                      Scale pixels to unsigned shorts to prevent
6513                      overflow of intermediate values of interpolations
6514                   */
6515                      for (y=0; y < (ssize_t) image->rows; y++)
6516                      {
6517                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6518                           exception);
6519
6520                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6521                        {
6522                           SetPixelRed(image,ScaleQuantumToShort(
6523                             GetPixelRed(image,q)),q);
6524                           SetPixelGreen(image,ScaleQuantumToShort(
6525                             GetPixelGreen(image,q)),q);
6526                           SetPixelBlue(image,ScaleQuantumToShort(
6527                             GetPixelBlue(image,q)),q);
6528                           SetPixelAlpha(image,ScaleQuantumToShort(
6529                             GetPixelAlpha(image,q)),q);
6530                           q+=GetPixelChannels(image);
6531                        }
6532
6533                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6534                          break;
6535                      }
6536                   }
6537 #else
6538 #define QM Quantum
6539 #endif
6540
6541                 if (image->alpha_trait != UndefinedPixelTrait)
6542                    (void) SetImageBackgroundColor(large_image,exception);
6543
6544                 else
6545                   {
6546                     large_image->background_color.alpha=OpaqueAlpha;
6547                     (void) SetImageBackgroundColor(large_image,exception);
6548
6549                     if (magn_methx == 4)
6550                       magn_methx=2;
6551
6552                     if (magn_methx == 5)
6553                       magn_methx=3;
6554
6555                     if (magn_methy == 4)
6556                       magn_methy=2;
6557
6558                     if (magn_methy == 5)
6559                       magn_methy=3;
6560                   }
6561
6562                 /* magnify the rows into the right side of the large image */
6563
6564                 if (logging != MagickFalse)
6565                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6566                     "    Magnify the rows to %.20g",(double) large_image->rows);
6567                 m=(ssize_t) mng_info->magn_mt;
6568                 yy=0;
6569                 length=(size_t) GetPixelChannels(image)*image->columns;
6570                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6571                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6572
6573                 if ((prev == (Quantum *) NULL) ||
6574                     (next == (Quantum *) NULL))
6575                   {
6576                      image=DestroyImageList(image);
6577                      MngInfoFreeStruct(mng_info,&have_mng_structure);
6578                      ThrowReaderException(ResourceLimitError,
6579                        "MemoryAllocationFailed");
6580                   }
6581
6582                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6583                 (void) CopyMagickMemory(next,n,length);
6584
6585                 for (y=0; y < (ssize_t) image->rows; y++)
6586                 {
6587                   if (y == 0)
6588                     m=(ssize_t) mng_info->magn_mt;
6589
6590                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6591                     m=(ssize_t) mng_info->magn_mb;
6592
6593                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6594                     m=(ssize_t) mng_info->magn_mb;
6595
6596                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6597                     m=1;
6598
6599                   else
6600                     m=(ssize_t) mng_info->magn_my;
6601
6602                   n=prev;
6603                   prev=next;
6604                   next=n;
6605
6606                   if (y < (ssize_t) image->rows-1)
6607                     {
6608                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6609                           exception);
6610                       (void) CopyMagickMemory(next,n,length);
6611                     }
6612
6613                   for (i=0; i < m; i++, yy++)
6614                   {
6615                     register Quantum
6616                       *pixels;
6617
6618                     assert(yy < (ssize_t) large_image->rows);
6619                     pixels=prev;
6620                     n=next;
6621                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6622                       1,exception);
6623                     q+=(large_image->columns-image->columns)*
6624                       GetPixelChannels(large_image);
6625
6626                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
6627                     {
6628                       /* To do: get color as function of indexes[x] */
6629                       /*
6630                       if (image->storage_class == PseudoClass)
6631                         {
6632                         }
6633                       */
6634
6635                       if (magn_methy <= 1)
6636                         {
6637                           /* replicate previous */
6638                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6639                           SetPixelGreen(large_image,GetPixelGreen(image,
6640                              pixels),q);
6641                           SetPixelBlue(large_image,GetPixelBlue(image,
6642                              pixels),q);
6643                           SetPixelAlpha(large_image,GetPixelAlpha(image,
6644                              pixels),q);
6645                         }
6646
6647                       else if (magn_methy == 2 || magn_methy == 4)
6648                         {
6649                           if (i == 0)
6650                             {
6651                               SetPixelRed(large_image,GetPixelRed(image,
6652                                  pixels),q);
6653                               SetPixelGreen(large_image,GetPixelGreen(image,
6654                                  pixels),q);
6655                               SetPixelBlue(large_image,GetPixelBlue(image,
6656                                  pixels),q);
6657                               SetPixelAlpha(large_image,GetPixelAlpha(image,
6658                                  pixels),q);
6659                             }
6660
6661                           else
6662                             {
6663                               /* Interpolate */
6664                               SetPixelRed(large_image,((QM) (((ssize_t)
6665                                  (2*i*(GetPixelRed(image,n)
6666                                  -GetPixelRed(image,pixels)+m))/
6667                                  ((ssize_t) (m*2))
6668                                  +GetPixelRed(image,pixels)))),q);
6669                               SetPixelGreen(large_image,((QM) (((ssize_t)
6670                                  (2*i*(GetPixelGreen(image,n)
6671                                  -GetPixelGreen(image,pixels)+m))/
6672                                  ((ssize_t) (m*2))
6673                                  +GetPixelGreen(image,pixels)))),q);
6674                               SetPixelBlue(large_image,((QM) (((ssize_t)
6675                                  (2*i*(GetPixelBlue(image,n)
6676                                  -GetPixelBlue(image,pixels)+m))/
6677                                  ((ssize_t) (m*2))
6678                                  +GetPixelBlue(image,pixels)))),q);
6679
6680                               if (image->alpha_trait != UndefinedPixelTrait)
6681                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
6682                                     (2*i*(GetPixelAlpha(image,n)
6683                                     -GetPixelAlpha(image,pixels)+m))
6684                                     /((ssize_t) (m*2))+
6685                                    GetPixelAlpha(image,pixels)))),q);
6686                             }
6687
6688                           if (magn_methy == 4)
6689                             {
6690                               /* Replicate nearest */
6691                               if (i <= ((m+1) << 1))
6692                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6693                                     pixels),q);
6694                               else
6695                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6696                                     n),q);
6697                             }
6698                         }
6699
6700                       else /* if (magn_methy == 3 || magn_methy == 5) */
6701                         {
6702                           /* Replicate nearest */
6703                           if (i <= ((m+1) << 1))
6704                           {
6705                              SetPixelRed(large_image,GetPixelRed(image,
6706                                     pixels),q);
6707                              SetPixelGreen(large_image,GetPixelGreen(image,
6708                                     pixels),q);
6709                              SetPixelBlue(large_image,GetPixelBlue(image,
6710                                     pixels),q);
6711                              SetPixelAlpha(large_image,GetPixelAlpha(image,
6712                                     pixels),q);
6713                           }
6714
6715                           else
6716                           {
6717                              SetPixelRed(large_image,GetPixelRed(image,n),q);
6718                              SetPixelGreen(large_image,GetPixelGreen(image,n),
6719                                     q);
6720                              SetPixelBlue(large_image,GetPixelBlue(image,n),
6721                                     q);
6722                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6723                                     q);
6724                           }
6725
6726                           if (magn_methy == 5)
6727                             {
6728                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6729                                  (GetPixelAlpha(image,n)
6730                                  -GetPixelAlpha(image,pixels))
6731                                  +m))/((ssize_t) (m*2))
6732                                  +GetPixelAlpha(image,pixels)),q);
6733                             }
6734                         }
6735                       n+=GetPixelChannels(image);
6736                       q+=GetPixelChannels(large_image);
6737                       pixels+=GetPixelChannels(image);
6738                     } /* x */
6739
6740                     if (SyncAuthenticPixels(large_image,exception) == 0)
6741                       break;
6742
6743                   } /* i */
6744                 } /* y */
6745
6746                 prev=(Quantum *) RelinquishMagickMemory(prev);
6747                 next=(Quantum *) RelinquishMagickMemory(next);
6748
6749                 length=image->columns;
6750
6751                 if (logging != MagickFalse)
6752                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6753                     "    Delete original image");
6754
6755                 DeleteImageFromList(&image);
6756
6757                 image=large_image;
6758
6759                 mng_info->image=image;
6760
6761                 /* magnify the columns */
6762                 if (logging != MagickFalse)
6763                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6764                     "    Magnify the columns to %.20g",(double) image->columns);
6765
6766                 for (y=0; y < (ssize_t) image->rows; y++)
6767                 {
6768                   register Quantum
6769                     *pixels;
6770
6771                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6772                   pixels=q+(image->columns-length)*GetPixelChannels(image);
6773                   n=pixels+GetPixelChannels(image);
6774
6775                   for (x=(ssize_t) (image->columns-length);
6776                     x < (ssize_t) image->columns; x++)
6777                   {
6778                     /* To do: Rewrite using Get/Set***PixelChannel() */
6779
6780                     if (x == (ssize_t) (image->columns-length))
6781                       m=(ssize_t) mng_info->magn_ml;
6782
6783                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6784                       m=(ssize_t) mng_info->magn_mr;
6785
6786                     else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6787                       m=(ssize_t) mng_info->magn_mr;
6788
6789                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6790                       m=1;
6791
6792                     else
6793                       m=(ssize_t) mng_info->magn_mx;
6794
6795                     for (i=0; i < m; i++)
6796                     {
6797                       if (magn_methx <= 1)
6798                         {
6799                           /* replicate previous */
6800                           SetPixelRed(image,GetPixelRed(image,pixels),q);
6801                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6802                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6803                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6804                         }
6805
6806                       else if (magn_methx == 2 || magn_methx == 4)
6807                         {
6808                           if (i == 0)
6809                           {
6810                             SetPixelRed(image,GetPixelRed(image,pixels),q);
6811                             SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6812                             SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6813                             SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6814                           }
6815
6816                           /* To do: Rewrite using Get/Set***PixelChannel() */
6817                           else
6818                             {
6819                               /* Interpolate */
6820                               SetPixelRed(image,(QM) ((2*i*(
6821                                  GetPixelRed(image,n)
6822                                  -GetPixelRed(image,pixels))+m)
6823                                  /((ssize_t) (m*2))+
6824                                  GetPixelRed(image,pixels)),q);
6825
6826                               SetPixelGreen(image,(QM) ((2*i*(
6827                                  GetPixelGreen(image,n)
6828                                  -GetPixelGreen(image,pixels))+m)
6829                                  /((ssize_t) (m*2))+
6830                                  GetPixelGreen(image,pixels)),q);
6831
6832                               SetPixelBlue(image,(QM) ((2*i*(
6833                                  GetPixelBlue(image,n)
6834                                  -GetPixelBlue(image,pixels))+m)
6835                                  /((ssize_t) (m*2))+
6836                                  GetPixelBlue(image,pixels)),q);
6837                               if (image->alpha_trait != UndefinedPixelTrait)
6838                                  SetPixelAlpha(image,(QM) ((2*i*(
6839                                    GetPixelAlpha(image,n)
6840                                    -GetPixelAlpha(image,pixels))+m)
6841                                    /((ssize_t) (m*2))+
6842                                    GetPixelAlpha(image,pixels)),q);
6843                             }
6844
6845                           if (magn_methx == 4)
6846                             {
6847                               /* Replicate nearest */
6848                               if (i <= ((m+1) << 1))
6849                               {
6850                                  SetPixelAlpha(image,
6851                                    GetPixelAlpha(image,pixels)+0,q);
6852                               }
6853                               else
6854                               {
6855                                  SetPixelAlpha(image,
6856                                    GetPixelAlpha(image,n)+0,q);
6857                               }
6858                             }
6859                         }
6860
6861                       else /* if (magn_methx == 3 || magn_methx == 5) */
6862                         {
6863                           /* Replicate nearest */
6864                           if (i <= ((m+1) << 1))
6865                           {
6866                              SetPixelRed(image,GetPixelRed(image,pixels),q);
6867                              SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6868                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6869                              SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6870                           }
6871
6872                           else
6873                           {
6874                              SetPixelRed(image,GetPixelRed(image,n),q);
6875                              SetPixelGreen(image,GetPixelGreen(image,n),q);
6876                              SetPixelBlue(image,GetPixelBlue(image,n),q);
6877                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6878                           }
6879
6880                           if (magn_methx == 5)
6881                             {
6882                               /* Interpolate */
6883                               SetPixelAlpha(image,
6884                                  (QM) ((2*i*( GetPixelAlpha(image,n)
6885                                  -GetPixelAlpha(image,pixels))+m)/
6886                                  ((ssize_t) (m*2))
6887                                  +GetPixelAlpha(image,pixels)),q);
6888                             }
6889                         }
6890                       q+=GetPixelChannels(image);
6891                     }
6892                     n+=GetPixelChannels(image);
6893                   }
6894
6895                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
6896                     break;
6897                 }
6898 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6899               if (magn_methx != 1 || magn_methy != 1)
6900                 {
6901                 /*
6902                    Rescale pixels to Quantum
6903                 */
6904                    for (y=0; y < (ssize_t) image->rows; y++)
6905                    {
6906                      q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6907
6908                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
6909                      {
6910                         SetPixelRed(image,ScaleShortToQuantum(
6911                           GetPixelRed(image,q)),q);
6912                         SetPixelGreen(image,ScaleShortToQuantum(
6913                           GetPixelGreen(image,q)),q);
6914                         SetPixelBlue(image,ScaleShortToQuantum(
6915                           GetPixelBlue(image,q)),q);
6916                         SetPixelAlpha(image,ScaleShortToQuantum(
6917                           GetPixelAlpha(image,q)),q);
6918                         q+=GetPixelChannels(image);
6919                      }
6920
6921                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
6922                        break;
6923                    }
6924                 }
6925 #endif
6926                 if (logging != MagickFalse)
6927                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6928                     "  Finished MAGN processing");
6929               }
6930           }
6931
6932         /*
6933           Crop_box is with respect to the upper left corner of the MNG.
6934         */
6935         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6936         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6937         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6938         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6939         crop_box=mng_minimum_box(crop_box,mng_info->clip);
6940         crop_box=mng_minimum_box(crop_box,mng_info->frame);
6941         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6942         if ((crop_box.left != (mng_info->image_box.left
6943             +mng_info->x_off[object_id])) ||
6944             (crop_box.right != (mng_info->image_box.right
6945             +mng_info->x_off[object_id])) ||
6946             (crop_box.top != (mng_info->image_box.top
6947             +mng_info->y_off[object_id])) ||
6948             (crop_box.bottom != (mng_info->image_box.bottom
6949             +mng_info->y_off[object_id])))
6950           {
6951             if (logging != MagickFalse)
6952               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6953                 "  Crop the PNG image");
6954
6955             if ((crop_box.left < crop_box.right) &&
6956                 (crop_box.top < crop_box.bottom))
6957               {
6958                 Image
6959                   *im;
6960
6961                 RectangleInfo
6962                   crop_info;
6963
6964                 /*
6965                   Crop_info is with respect to the upper left corner of
6966                   the image.
6967                 */
6968                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6969                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6970                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6971                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6972                 image->page.width=image->columns;
6973                 image->page.height=image->rows;
6974                 image->page.x=0;
6975                 image->page.y=0;
6976                 im=CropImage(image,&crop_info,exception);
6977
6978                 if (im != (Image *) NULL)
6979                   {
6980                     image->columns=im->columns;
6981                     image->rows=im->rows;
6982                     im=DestroyImage(im);
6983                     image->page.width=image->columns;
6984                     image->page.height=image->rows;
6985                     image->page.x=crop_box.left;
6986                     image->page.y=crop_box.top;
6987                   }
6988               }
6989
6990             else
6991               {
6992                 /*
6993                   No pixels in crop area.  The MNG spec still requires
6994                   a layer, though, so make a single transparent pixel in
6995                   the top left corner.
6996                 */
6997                 image->columns=1;
6998                 image->rows=1;
6999                 image->colors=2;
7000                 (void) SetImageBackgroundColor(image,exception);
7001                 image->page.width=1;
7002                 image->page.height=1;
7003                 image->page.x=0;
7004                 image->page.y=0;
7005               }
7006           }
7007 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7008         image=mng_info->image;
7009 #endif
7010       }
7011
7012 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7013       /* PNG does not handle depths greater than 16 so reduce it even
7014        * if lossy.
7015        */
7016       if (image->depth > 16)
7017          image->depth=16;
7018 #endif
7019
7020 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7021       if (image->depth > 8)
7022         {
7023           /* To do: fill low byte properly */
7024           image->depth=16;
7025         }
7026
7027       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7028          image->depth = 8;
7029 #endif
7030
7031       if (image_info->number_scenes != 0)
7032         {
7033           if (mng_info->scenes_found >
7034              (ssize_t) (image_info->first_scene+image_info->number_scenes))
7035             break;
7036         }
7037
7038       if (logging != MagickFalse)
7039         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7040           "  Finished reading image datastream.");
7041
7042   } while (LocaleCompare(image_info->magick,"MNG") == 0);
7043
7044   (void) CloseBlob(image);
7045
7046   if (logging != MagickFalse)
7047     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7048       "  Finished reading all image datastreams.");
7049
7050 #if defined(MNG_INSERT_LAYERS)
7051   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7052        (mng_info->mng_height))
7053     {
7054       /*
7055         Insert a background layer if nothing else was found.
7056       */
7057       if (logging != MagickFalse)
7058         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7059           "  No images found.  Inserting a background layer.");
7060
7061       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7062         {
7063           /*
7064             Allocate next image structure.
7065           */
7066           AcquireNextImage(image_info,image,exception);
7067           if (GetNextImageInList(image) == (Image *) NULL)
7068             {
7069               image=DestroyImageList(image);
7070               MngInfoFreeStruct(mng_info,&have_mng_structure);
7071
7072               if (logging != MagickFalse)
7073                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7074                   "  Allocation failed, returning NULL.");
7075
7076               return((Image *) NULL);
7077             }
7078           image=SyncNextImageInList(image);
7079         }
7080       image->columns=mng_info->mng_width;
7081       image->rows=mng_info->mng_height;
7082       image->page.width=mng_info->mng_width;
7083       image->page.height=mng_info->mng_height;
7084       image->page.x=0;
7085       image->page.y=0;
7086       image->background_color=mng_background_color;
7087       image->alpha_trait=UndefinedPixelTrait;
7088
7089       if (image_info->ping == MagickFalse)
7090         (void) SetImageBackgroundColor(image,exception);
7091
7092       mng_info->image_found++;
7093     }
7094 #endif
7095   image->iterations=mng_iterations;
7096
7097   if (mng_iterations == 1)
7098     image->start_loop=MagickTrue;
7099
7100   while (GetPreviousImageInList(image) != (Image *) NULL)
7101   {
7102     image_count++;
7103     if (image_count > 10*mng_info->image_found)
7104       {
7105         if (logging != MagickFalse)
7106           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7107
7108         (void) ThrowMagickException(exception,GetMagickModule(),
7109           CoderError,"Linked list is corrupted, beginning of list not found",
7110           "`%s'",image_info->filename);
7111
7112         return((Image *) NULL);
7113       }
7114
7115     image=GetPreviousImageInList(image);
7116
7117     if (GetNextImageInList(image) == (Image *) NULL)
7118       {
7119         if (logging != MagickFalse)
7120           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7121
7122         (void) ThrowMagickException(exception,GetMagickModule(),
7123           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7124           image_info->filename);
7125       }
7126   }
7127
7128   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7129              GetNextImageInList(image) ==
7130      (Image *) NULL)
7131     {
7132       if (logging != MagickFalse)
7133         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7134             "  First image null");
7135
7136       (void) ThrowMagickException(exception,GetMagickModule(),
7137         CoderError,"image->next for first image is NULL but shouldn't be.",
7138         "`%s'",image_info->filename);
7139     }
7140
7141   if (mng_info->image_found == 0)
7142     {
7143       if (logging != MagickFalse)
7144         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7145           "  No visible images found.");
7146
7147       (void) ThrowMagickException(exception,GetMagickModule(),
7148         CoderError,"No visible images in file","`%s'",image_info->filename);
7149
7150       if (image != (Image *) NULL)
7151         image=DestroyImageList(image);
7152
7153       MngInfoFreeStruct(mng_info,&have_mng_structure);
7154       return((Image *) NULL);
7155     }
7156
7157   if (mng_info->ticks_per_second)
7158     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7159             final_delay/mng_info->ticks_per_second;
7160
7161   else
7162     image->start_loop=MagickTrue;
7163
7164   /* Find final nonzero image delay */
7165   final_image_delay=0;
7166
7167   while (GetNextImageInList(image) != (Image *) NULL)
7168     {
7169       if (image->delay)
7170         final_image_delay=image->delay;
7171
7172       image=GetNextImageInList(image);
7173     }
7174
7175   if (final_delay < final_image_delay)
7176     final_delay=final_image_delay;
7177
7178   image->delay=final_delay;
7179
7180   if (logging != MagickFalse)
7181       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7182         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7183         (double) final_delay);
7184
7185   if (logging != MagickFalse)
7186     {
7187       int
7188         scene;
7189
7190       scene=0;
7191       image=GetFirstImageInList(image);
7192
7193       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7194         "  Before coalesce:");
7195
7196       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7197         "    scene 0 delay=%.20g",(double) image->delay);
7198
7199       while (GetNextImageInList(image) != (Image *) NULL)
7200       {
7201         image=GetNextImageInList(image);
7202         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7203           "    scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7204       }
7205     }
7206
7207   image=GetFirstImageInList(image);
7208 #ifdef MNG_COALESCE_LAYERS
7209   if (insert_layers)
7210     {
7211       Image
7212         *next_image,
7213         *next;
7214
7215       size_t
7216         scene;
7217
7218       if (logging != MagickFalse)
7219         (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Coalesce Images");
7220
7221       scene=image->scene;
7222       next_image=CoalesceImages(image,exception);
7223
7224       if (next_image == (Image *) NULL)
7225         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7226
7227       image=DestroyImageList(image);
7228       image=next_image;
7229
7230       for (next=image; next != (Image *) NULL; next=next_image)
7231       {
7232          next->page.width=mng_info->mng_width;
7233          next->page.height=mng_info->mng_height;
7234          next->page.x=0;
7235          next->page.y=0;
7236          next->scene=scene++;
7237          next_image=GetNextImageInList(next);
7238
7239          if (next_image == (Image *) NULL)
7240            break;
7241
7242          if (next->delay == 0)
7243            {
7244              scene--;
7245              next_image->previous=GetPreviousImageInList(next);
7246              if (GetPreviousImageInList(next) == (Image *) NULL)
7247                image=next_image;
7248              else
7249                next->previous->next=next_image;
7250              next=DestroyImage(next);
7251            }
7252       }
7253     }
7254 #endif
7255
7256   while (GetNextImageInList(image) != (Image *) NULL)
7257       image=GetNextImageInList(image);
7258
7259   image->dispose=BackgroundDispose;
7260
7261   if (logging != MagickFalse)
7262     {
7263       int
7264         scene;
7265
7266       scene=0;
7267       image=GetFirstImageInList(image);
7268
7269       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7270         "  After coalesce:");
7271
7272       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7273         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7274         (double) image->dispose);
7275
7276       while (GetNextImageInList(image) != (Image *) NULL)
7277       {
7278         image=GetNextImageInList(image);
7279
7280         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7281           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7282           (double) image->delay,(double) image->dispose);
7283       }
7284    }
7285
7286   image=GetFirstImageInList(image);
7287   MngInfoFreeStruct(mng_info,&have_mng_structure);
7288   have_mng_structure=MagickFalse;
7289
7290   if (logging != MagickFalse)
7291     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7292
7293   return(GetFirstImageInList(image));
7294 }
7295 #else /* PNG_LIBPNG_VER > 10011 */
7296 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7297 {
7298   printf("Your PNG library is too old: You have libpng-%s\n",
7299      PNG_LIBPNG_VER_STRING);
7300
7301   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7302     "PNG library is too old","`%s'",image_info->filename);
7303
7304   return(Image *) NULL;
7305 }
7306
7307 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7308 {
7309   return(ReadPNGImage(image_info,exception));
7310 }
7311 #endif /* PNG_LIBPNG_VER > 10011 */
7312 #endif
7313 \f
7314 /*
7315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7316 %                                                                             %
7317 %                                                                             %
7318 %                                                                             %
7319 %   R e g i s t e r P N G I m a g e                                           %
7320 %                                                                             %
7321 %                                                                             %
7322 %                                                                             %
7323 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7324 %
7325 %  RegisterPNGImage() adds properties for the PNG image format to
7326 %  the list of supported formats.  The properties include the image format
7327 %  tag, a method to read and/or write the format, whether the format
7328 %  supports the saving of more than one frame to the same file or blob,
7329 %  whether the format supports native in-memory I/O, and a brief
7330 %  description of the format.
7331 %
7332 %  The format of the RegisterPNGImage method is:
7333 %
7334 %      size_t RegisterPNGImage(void)
7335 %
7336 */
7337 ModuleExport size_t RegisterPNGImage(void)
7338 {
7339   char
7340     version[MaxTextExtent];
7341
7342   MagickInfo
7343     *entry;
7344
7345   static const char
7346     *PNGNote=
7347     {
7348       "See http://www.libpng.org/ for details about the PNG format."
7349     },
7350
7351     *JNGNote=
7352     {
7353       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7354       "format."
7355     },
7356
7357     *MNGNote=
7358     {
7359       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7360       "format."
7361     };
7362
7363   *version='\0';
7364
7365 #if defined(PNG_LIBPNG_VER_STRING)
7366   (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7367   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7368
7369   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7370     {
7371       (void) ConcatenateMagickString(version,",",MaxTextExtent);
7372       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7373             MaxTextExtent);
7374     }
7375 #endif
7376
7377   entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7378   entry->flags|=CoderSeekableStreamFlag;  /* To do: eliminate this. */
7379
7380 #if defined(MAGICKCORE_PNG_DELEGATE)
7381   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7382   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7383 #endif
7384
7385   entry->magick=(IsImageFormatHandler *) IsMNG;
7386
7387   if (*version != '\0')
7388     entry->version=ConstantString(version);
7389
7390   entry->mime_type=ConstantString("video/x-mng");
7391   entry->note=ConstantString(MNGNote);
7392   (void) RegisterMagickInfo(entry);
7393
7394   entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7395
7396 #if defined(MAGICKCORE_PNG_DELEGATE)
7397   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7398   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7399 #endif
7400
7401   entry->magick=(IsImageFormatHandler *) IsPNG;
7402   entry->flags^=CoderAdjoinFlag;
7403   entry->mime_type=ConstantString("image/png");
7404
7405   if (*version != '\0')
7406     entry->version=ConstantString(version);
7407
7408   entry->note=ConstantString(PNGNote);
7409   (void) RegisterMagickInfo(entry);
7410
7411   entry=AcquireMagickInfo("PNG","PNG8",
7412     "8-bit indexed with optional binary transparency");
7413
7414 #if defined(MAGICKCORE_PNG_DELEGATE)
7415   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7416   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7417 #endif
7418
7419   entry->magick=(IsImageFormatHandler *) IsPNG;
7420   entry->flags^=CoderAdjoinFlag;
7421   entry->mime_type=ConstantString("image/png");
7422   (void) RegisterMagickInfo(entry);
7423
7424   entry=AcquireMagickInfo("PNG","PNG24",
7425     "opaque or binary transparent 24-bit RGB");
7426   *version='\0';
7427
7428 #if defined(ZLIB_VERSION)
7429   (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7430   (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7431
7432   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7433     {
7434       (void) ConcatenateMagickString(version,",",MaxTextExtent);
7435       (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7436     }
7437 #endif
7438
7439   if (*version != '\0')
7440     entry->version=ConstantString(version);
7441
7442 #if defined(MAGICKCORE_PNG_DELEGATE)
7443   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7444   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7445 #endif
7446
7447   entry->magick=(IsImageFormatHandler *) IsPNG;
7448   entry->flags^=CoderAdjoinFlag;
7449   entry->mime_type=ConstantString("image/png");
7450   (void) RegisterMagickInfo(entry);
7451
7452   entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7453
7454 #if defined(MAGICKCORE_PNG_DELEGATE)
7455   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7456   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7457 #endif
7458
7459   entry->magick=(IsImageFormatHandler *) IsPNG;
7460   entry->flags^=CoderAdjoinFlag;
7461   entry->mime_type=ConstantString("image/png");
7462   (void) RegisterMagickInfo(entry);
7463
7464   entry=AcquireMagickInfo("PNG","PNG48",
7465     "opaque or binary transparent 48-bit RGB");
7466
7467 #if defined(MAGICKCORE_PNG_DELEGATE)
7468   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7469   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7470 #endif
7471
7472   entry->magick=(IsImageFormatHandler *) IsPNG;
7473   entry->flags^=CoderAdjoinFlag;
7474   entry->mime_type=ConstantString("image/png");
7475   (void) RegisterMagickInfo(entry);
7476
7477   entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7478
7479 #if defined(MAGICKCORE_PNG_DELEGATE)
7480   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7481   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7482 #endif
7483
7484   entry->magick=(IsImageFormatHandler *) IsPNG;
7485   entry->flags^=CoderAdjoinFlag;
7486   entry->mime_type=ConstantString("image/png");
7487   (void) RegisterMagickInfo(entry);
7488
7489   entry=AcquireMagickInfo("PNG","PNG00",
7490     "PNG inheriting bit-depth and color-type from original");
7491
7492 #if defined(MAGICKCORE_PNG_DELEGATE)
7493   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7494   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7495 #endif
7496
7497   entry->magick=(IsImageFormatHandler *) IsPNG;
7498   entry->flags^=CoderAdjoinFlag;
7499   entry->mime_type=ConstantString("image/png");
7500   (void) RegisterMagickInfo(entry);
7501
7502   entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7503
7504 #if defined(JNG_SUPPORTED)
7505 #if defined(MAGICKCORE_PNG_DELEGATE)
7506   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7507   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7508 #endif
7509 #endif
7510
7511   entry->magick=(IsImageFormatHandler *) IsJNG;
7512   entry->flags^=CoderAdjoinFlag;
7513   entry->mime_type=ConstantString("image/x-jng");
7514   entry->note=ConstantString(JNGNote);
7515   (void) RegisterMagickInfo(entry);
7516
7517 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7518   ping_semaphore=AcquireSemaphoreInfo();
7519 #endif
7520
7521   return(MagickImageCoderSignature);
7522 }
7523 \f
7524 /*
7525 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7526 %                                                                             %
7527 %                                                                             %
7528 %                                                                             %
7529 %   U n r e g i s t e r P N G I m a g e                                       %
7530 %                                                                             %
7531 %                                                                             %
7532 %                                                                             %
7533 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7534 %
7535 %  UnregisterPNGImage() removes format registrations made by the
7536 %  PNG module from the list of supported formats.
7537 %
7538 %  The format of the UnregisterPNGImage method is:
7539 %
7540 %      UnregisterPNGImage(void)
7541 %
7542 */
7543 ModuleExport void UnregisterPNGImage(void)
7544 {
7545   (void) UnregisterMagickInfo("MNG");
7546   (void) UnregisterMagickInfo("PNG");
7547   (void) UnregisterMagickInfo("PNG8");
7548   (void) UnregisterMagickInfo("PNG24");
7549   (void) UnregisterMagickInfo("PNG32");
7550   (void) UnregisterMagickInfo("PNG48");
7551   (void) UnregisterMagickInfo("PNG64");
7552   (void) UnregisterMagickInfo("PNG00");
7553   (void) UnregisterMagickInfo("JNG");
7554
7555 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7556   if (ping_semaphore != (SemaphoreInfo *) NULL)
7557     RelinquishSemaphoreInfo(&ping_semaphore);
7558 #endif
7559 }
7560 \f
7561 #if defined(MAGICKCORE_PNG_DELEGATE)
7562 #if PNG_LIBPNG_VER > 10011
7563 /*
7564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7565 %                                                                             %
7566 %                                                                             %
7567 %                                                                             %
7568 %   W r i t e M N G I m a g e                                                 %
7569 %                                                                             %
7570 %                                                                             %
7571 %                                                                             %
7572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7573 %
7574 %  WriteMNGImage() writes an image in the Portable Network Graphics
7575 %  Group's "Multiple-image Network Graphics" encoded image format.
7576 %
7577 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
7578 %
7579 %  The format of the WriteMNGImage method is:
7580 %
7581 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7582 %        Image *image,ExceptionInfo *exception)
7583 %
7584 %  A description of each parameter follows.
7585 %
7586 %    o image_info: the image info.
7587 %
7588 %    o image:  The image.
7589 %
7590 %    o exception: return any errors or warnings in this structure.
7591 %
7592 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7593 %    "To do" under ReadPNGImage):
7594 %
7595 %    Preserve all unknown and not-yet-handled known chunks found in input
7596 %    PNG file and copy them  into output PNG files according to the PNG
7597 %    copying rules.
7598 %
7599 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
7600 %
7601 %    Improve selection of color type (use indexed-colour or indexed-colour
7602 %    with tRNS when 256 or fewer unique RGBA values are present).
7603 %
7604 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7605 %    This will be complicated if we limit ourselves to generating MNG-LC
7606 %    files.  For now we ignore disposal method 3 and simply overlay the next
7607 %    image on it.
7608 %
7609 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
7610 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
7611 %    [mostly done 15 June 1999 but still need to take care of tRNS]
7612 %
7613 %    Check for identical sRGB and replace with a global sRGB (and remove
7614 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7615 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7616 %    local gAMA/cHRM with local sRGB if appropriate).
7617 %
7618 %    Check for identical sBIT chunks and write global ones.
7619 %
7620 %    Provide option to skip writing the signature tEXt chunks.
7621 %
7622 %    Use signatures to detect identical objects and reuse the first
7623 %    instance of such objects instead of writing duplicate objects.
7624 %
7625 %    Use a smaller-than-32k value of compression window size when
7626 %    appropriate.
7627 %
7628 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
7629 %    ancillary text chunks and save profiles.
7630 %
7631 %    Provide an option to force LC files (to ensure exact framing rate)
7632 %    instead of VLC.
7633 %
7634 %    Provide an option to force VLC files instead of LC, even when offsets
7635 %    are present.  This will involve expanding the embedded images with a
7636 %    transparent region at the top and/or left.
7637 */
7638
7639 static void
7640 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7641    png_info *ping_info, unsigned char *profile_type, unsigned char
7642    *profile_description, unsigned char *profile_data, png_uint_32 length)
7643 {
7644    png_textp
7645      text;
7646
7647    register ssize_t
7648      i;
7649
7650    unsigned char
7651      *sp;
7652
7653    png_charp
7654      dp;
7655
7656    png_uint_32
7657      allocated_length,
7658      description_length;
7659
7660    unsigned char
7661      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7662
7663    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7664       return;
7665
7666    if (image_info->verbose)
7667      {
7668        (void) printf("writing raw profile: type=%s, length=%.20g\n",
7669          (char *) profile_type, (double) length);
7670      }
7671
7672 #if PNG_LIBPNG_VER >= 10400
7673    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7674 #else
7675    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7676 #endif
7677    description_length=(png_uint_32) strlen((const char *) profile_description);
7678    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7679       + description_length);
7680 #if PNG_LIBPNG_VER >= 10400
7681    text[0].text=(png_charp) png_malloc(ping,
7682       (png_alloc_size_t) allocated_length);
7683    text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7684 #else
7685    text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7686    text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7687 #endif
7688    text[0].key[0]='\0';
7689    (void) ConcatenateMagickString(text[0].key,
7690       "Raw profile type ",MaxTextExtent);
7691    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7692    sp=profile_data;
7693    dp=text[0].text;
7694    *dp++='\n';
7695    (void) CopyMagickString(dp,(const char *) profile_description,
7696      allocated_length);
7697    dp+=description_length;
7698    *dp++='\n';
7699    (void) FormatLocaleString(dp,allocated_length-
7700      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7701    dp+=8;
7702
7703    for (i=0; i < (ssize_t) length; i++)
7704    {
7705      if (i%36 == 0)
7706        *dp++='\n';
7707      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7708      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7709    }
7710
7711    *dp++='\n';
7712    *dp='\0';
7713    text[0].text_length=(png_size_t) (dp-text[0].text);
7714    text[0].compression=image_info->compression == NoCompression ||
7715      (image_info->compression == UndefinedCompression &&
7716      text[0].text_length < 128) ? -1 : 0;
7717
7718    if (text[0].text_length <= allocated_length)
7719      png_set_text(ping,ping_info,text,1);
7720
7721    png_free(ping,text[0].text);
7722    png_free(ping,text[0].key);
7723    png_free(ping,text);
7724 }
7725
7726 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7727   const char *string, MagickBooleanType logging)
7728 {
7729   char
7730     *name;
7731
7732   const StringInfo
7733     *profile;
7734
7735   unsigned char
7736     *data;
7737
7738   png_uint_32 length;
7739
7740   ResetImageProfileIterator(image);
7741
7742   for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7743   {
7744     profile=GetImageProfile(image,name);
7745
7746     if (profile != (const StringInfo *) NULL)
7747       {
7748         StringInfo
7749           *ping_profile;
7750
7751         if (LocaleNCompare(name,string,11) == 0)
7752           {
7753             if (logging != MagickFalse)
7754                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7755                    "  Found %s profile",name);
7756
7757             ping_profile=CloneStringInfo(profile);
7758             data=GetStringInfoDatum(ping_profile),
7759             length=(png_uint_32) GetStringInfoLength(ping_profile);
7760             data[4]=data[3];
7761             data[3]=data[2];
7762             data[2]=data[1];
7763             data[1]=data[0];
7764             (void) WriteBlobMSBULong(image,length-5);  /* data length */
7765             (void) WriteBlob(image,length-1,data+1);
7766             (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7767             ping_profile=DestroyStringInfo(ping_profile);
7768           }
7769       }
7770
7771       name=GetNextImageProfile(image);
7772    }
7773
7774    return(MagickTrue);
7775 }
7776
7777 #if defined(PNG_tIME_SUPPORTED)
7778 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
7779   const char *date,ExceptionInfo *exception)
7780 {
7781   unsigned int
7782     day,
7783     hour,
7784     minute,
7785     month,
7786     second,
7787     year;
7788
7789   png_time
7790     ptime;
7791
7792   time_t
7793     ttime;
7794
7795   if (date != (const char *) NULL)
7796     {
7797       if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
7798           &second) != 6)
7799         {
7800           (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7801             "Invalid date format specified for png:tIME","`%s'",
7802             image->filename);
7803           return;
7804         }
7805       ptime.year=(png_uint_16) year;
7806       ptime.month=(png_byte) month;
7807       ptime.day=(png_byte) day;
7808       ptime.hour=(png_byte) hour;
7809       ptime.minute=(png_byte) minute;
7810       ptime.second=(png_byte) second;
7811     }
7812   else
7813   {
7814     time(&ttime);
7815     png_convert_from_time_t(&ptime,ttime);
7816   }
7817   png_set_tIME(ping,info,&ptime);
7818 }
7819 #endif
7820
7821 /* Write one PNG image */
7822 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7823   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7824 {
7825   char
7826     im_vers[32],
7827     libpng_runv[32],
7828     libpng_vers[32],
7829     zlib_runv[32],
7830     zlib_vers[32];
7831
7832   Image
7833     *image;
7834
7835   ImageInfo
7836     *image_info;
7837
7838   char
7839     s[2];
7840
7841   const char
7842     *name,
7843     *property,
7844     *value;
7845
7846   const StringInfo
7847     *profile;
7848
7849   int
7850     num_passes,
7851     pass;
7852
7853   png_byte
7854      ping_trans_alpha[256];
7855
7856   png_color
7857      palette[257];
7858
7859   png_color_16
7860     ping_background,
7861     ping_trans_color;
7862
7863   png_info
7864     *ping_info;
7865
7866   png_struct
7867     *ping;
7868
7869   png_uint_32
7870     ping_height,
7871     ping_width;
7872
7873   ssize_t
7874     y;
7875
7876   MagickBooleanType
7877     image_matte,
7878     logging,
7879     matte,
7880
7881     ping_have_blob,
7882     ping_have_cheap_transparency,
7883     ping_have_color,
7884     ping_have_non_bw,
7885     ping_have_PLTE,
7886     ping_have_bKGD,
7887     ping_have_iCCP,
7888     ping_have_pHYs,
7889     ping_have_sRGB,
7890     ping_have_tRNS,
7891
7892     ping_exclude_bKGD,
7893     ping_exclude_cHRM,
7894     ping_exclude_date,
7895     /* ping_exclude_EXIF, */
7896     ping_exclude_gAMA,
7897     ping_exclude_iCCP,
7898     /* ping_exclude_iTXt, */
7899     ping_exclude_oFFs,
7900     ping_exclude_pHYs,
7901     ping_exclude_sRGB,
7902     ping_exclude_tEXt,
7903     ping_exclude_tIME,
7904     /* ping_exclude_tRNS, */
7905     ping_exclude_vpAg,
7906     ping_exclude_zCCP, /* hex-encoded iCCP */
7907     ping_exclude_zTXt,
7908
7909     ping_preserve_colormap,
7910     ping_preserve_iCCP,
7911     ping_need_colortype_warning,
7912
7913     status,
7914     tried_332,
7915     tried_333,
7916     tried_444;
7917
7918   MemoryInfo
7919     *volatile pixel_info;
7920
7921   QuantumInfo
7922     *quantum_info;
7923
7924   PNGErrorInfo
7925     error_info;
7926
7927   register ssize_t
7928     i,
7929     x;
7930
7931   unsigned char
7932     *ping_pixels;
7933
7934   volatile int
7935     image_colors,
7936     ping_bit_depth,
7937     ping_color_type,
7938     ping_interlace_method,
7939     ping_compression_method,
7940     ping_filter_method,
7941     ping_num_trans;
7942
7943   volatile size_t
7944     image_depth,
7945     old_bit_depth;
7946
7947   size_t
7948     quality,
7949     rowbytes,
7950     save_image_depth;
7951
7952   int
7953     j,
7954     number_colors,
7955     number_opaque,
7956     number_semitransparent,
7957     number_transparent,
7958     ping_pHYs_unit_type;
7959
7960   png_uint_32
7961     ping_pHYs_x_resolution,
7962     ping_pHYs_y_resolution;
7963
7964   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7965     "  Enter WriteOnePNGImage()");
7966
7967   image = CloneImage(IMimage,0,0,MagickFalse,exception);
7968   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7969   if (image_info == (ImageInfo *) NULL)
7970      ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7971
7972   /* Define these outside of the following "if logging()" block so they will
7973    * show in debuggers.
7974    */
7975   *im_vers='\0';
7976   (void) ConcatenateMagickString(im_vers,
7977          MagickLibVersionText,MaxTextExtent);
7978   (void) ConcatenateMagickString(im_vers,
7979          MagickLibAddendum,MaxTextExtent);
7980
7981   *libpng_vers='\0';
7982   (void) ConcatenateMagickString(libpng_vers,
7983          PNG_LIBPNG_VER_STRING,32);
7984   *libpng_runv='\0';
7985   (void) ConcatenateMagickString(libpng_runv,
7986          png_get_libpng_ver(NULL),32);
7987
7988   *zlib_vers='\0';
7989   (void) ConcatenateMagickString(zlib_vers,
7990          ZLIB_VERSION,32);
7991   *zlib_runv='\0';
7992   (void) ConcatenateMagickString(zlib_runv,
7993          zlib_version,32);
7994
7995   if (logging != MagickFalse)
7996     {
7997        LogMagickEvent(CoderEvent,GetMagickModule(),"    IM version     = %s",
7998            im_vers);
7999        LogMagickEvent(CoderEvent,GetMagickModule(),"    Libpng version = %s",
8000            libpng_vers);
8001        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8002        {
8003        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8004            libpng_runv);
8005        }
8006        LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
8007            zlib_vers);
8008        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8009        {
8010        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8011            zlib_runv);
8012        }
8013     }
8014
8015   /* Initialize some stuff */
8016   ping_bit_depth=0,
8017   ping_color_type=0,
8018   ping_interlace_method=0,
8019   ping_compression_method=0,
8020   ping_filter_method=0,
8021   ping_num_trans = 0;
8022
8023   ping_background.red = 0;
8024   ping_background.green = 0;
8025   ping_background.blue = 0;
8026   ping_background.gray = 0;
8027   ping_background.index = 0;
8028
8029   ping_trans_color.red=0;
8030   ping_trans_color.green=0;
8031   ping_trans_color.blue=0;
8032   ping_trans_color.gray=0;
8033
8034   ping_pHYs_unit_type = 0;
8035   ping_pHYs_x_resolution = 0;
8036   ping_pHYs_y_resolution = 0;
8037
8038   ping_have_blob=MagickFalse;
8039   ping_have_cheap_transparency=MagickFalse;
8040   ping_have_color=MagickTrue;
8041   ping_have_non_bw=MagickTrue;
8042   ping_have_PLTE=MagickFalse;
8043   ping_have_bKGD=MagickFalse;
8044   ping_have_iCCP=MagickFalse;
8045   ping_have_pHYs=MagickFalse;
8046   ping_have_sRGB=MagickFalse;
8047   ping_have_tRNS=MagickFalse;
8048
8049   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8050   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8051   ping_exclude_date=mng_info->ping_exclude_date;
8052   /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8053   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8054   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8055   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8056   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8057   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8058   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8059   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8060   ping_exclude_tIME=mng_info->ping_exclude_tIME;
8061   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8062   ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8063   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8064   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8065
8066   ping_preserve_colormap = mng_info->ping_preserve_colormap;
8067   ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8068   ping_need_colortype_warning = MagickFalse;
8069
8070   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8071    * i.e., eliminate the ICC profile and set image->rendering_intent.
8072    * Note that this will not involve any changes to the actual pixels
8073    * but merely passes information to applications that read the resulting
8074    * PNG image.
8075    *
8076    * To do: recognize other variants of the sRGB profile, using the CRC to
8077    * verify all recognized variants including the 7 already known.
8078    *
8079    * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8080    *
8081    * Use something other than image->rendering_intent to record the fact
8082    * that the sRGB profile was found.
8083    *
8084    * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8085    * profile.  Record the Blackpoint Compensation, if any.
8086    */
8087    if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8088    {
8089       char
8090         *name;
8091
8092       const StringInfo
8093         *profile;
8094
8095       ResetImageProfileIterator(image);
8096       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8097       {
8098         profile=GetImageProfile(image,name);
8099
8100         if (profile != (StringInfo *) NULL)
8101           {
8102             if ((LocaleCompare(name,"ICC") == 0) ||
8103                 (LocaleCompare(name,"ICM") == 0))
8104
8105              {
8106                  int
8107                    icheck,
8108                    got_crc=0;
8109
8110
8111                  png_uint_32
8112                    length,
8113                    profile_crc=0;
8114
8115                  unsigned char
8116                    *data;
8117
8118                  length=(png_uint_32) GetStringInfoLength(profile);
8119
8120                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8121                  {
8122                    if (length == sRGB_info[icheck].len)
8123                    {
8124                      if (got_crc == 0)
8125                      {
8126                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8127                          "    Got a %lu-byte ICC profile (potentially sRGB)",
8128                          (unsigned long) length);
8129
8130                        data=GetStringInfoDatum(profile);
8131                        profile_crc=crc32(0,data,length);
8132
8133                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8134                            "      with crc=%8x",(unsigned int) profile_crc);
8135                        got_crc++;
8136                      }
8137
8138                      if (profile_crc == sRGB_info[icheck].crc)
8139                      {
8140                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8141                             "      It is sRGB with rendering intent = %s",
8142                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
8143                              sRGB_info[icheck].intent));
8144                         if (image->rendering_intent==UndefinedIntent)
8145                         {
8146                           image->rendering_intent=
8147                           Magick_RenderingIntent_from_PNG_RenderingIntent(
8148                              sRGB_info[icheck].intent);
8149                         }
8150                         ping_exclude_iCCP = MagickTrue;
8151                         ping_exclude_zCCP = MagickTrue;
8152                         ping_have_sRGB = MagickTrue;
8153                         break;
8154                      }
8155                    }
8156                  }
8157                  if (sRGB_info[icheck].len == 0)
8158                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8159                         "    Got a %lu-byte ICC profile not recognized as sRGB",
8160                         (unsigned long) length);
8161               }
8162           }
8163         name=GetNextImageProfile(image);
8164       }
8165   }
8166
8167   number_opaque = 0;
8168   number_semitransparent = 0;
8169   number_transparent = 0;
8170
8171   if (logging != MagickFalse)
8172     {
8173       if (image->storage_class == UndefinedClass)
8174           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8175           "    image->storage_class=UndefinedClass");
8176       if (image->storage_class == DirectClass)
8177           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8178           "    image->storage_class=DirectClass");
8179       if (image->storage_class == PseudoClass)
8180           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8181           "    image->storage_class=PseudoClass");
8182       (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8183           "    image->taint=MagickTrue":
8184           "    image->taint=MagickFalse");
8185     }
8186
8187   if (image->storage_class == PseudoClass &&
8188      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8189      mng_info->write_png48 || mng_info->write_png64 ||
8190      (mng_info->write_png_colortype != 1 &&
8191      mng_info->write_png_colortype != 5)))
8192     {
8193       (void) SyncImage(image,exception);
8194       image->storage_class = DirectClass;
8195     }
8196
8197   if (ping_preserve_colormap == MagickFalse)
8198     {
8199       if (image->storage_class != PseudoClass && image->colormap != NULL)
8200         {
8201           /* Free the bogus colormap; it can cause trouble later */
8202            if (logging != MagickFalse)
8203               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8204               "    Freeing bogus colormap");
8205            (void) RelinquishMagickMemory(image->colormap);
8206            image->colormap=NULL;
8207         }
8208     }
8209
8210   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8211     (void) TransformImageColorspace(image,sRGBColorspace,exception);
8212
8213   /*
8214     Sometimes we get PseudoClass images whose RGB values don't match
8215     the colors in the colormap.  This code syncs the RGB values.
8216   */
8217   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8218      (void) SyncImage(image,exception);
8219
8220 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8221   if (image->depth > 8)
8222     {
8223       if (logging != MagickFalse)
8224         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8225           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8226
8227       image->depth=8;
8228     }
8229 #endif
8230
8231   /* Respect the -depth option */
8232   if (image->depth < 4)
8233     {
8234        register Quantum
8235          *r;
8236
8237        if (image->depth > 2)
8238          {
8239            /* Scale to 4-bit */
8240            LBR04PacketRGBO(image->background_color);
8241
8242            for (y=0; y < (ssize_t) image->rows; y++)
8243            {
8244              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8245
8246              if (r == (Quantum *) NULL)
8247                break;
8248
8249              for (x=0; x < (ssize_t) image->columns; x++)
8250              {
8251                 LBR04PixelRGBA(r);
8252                 r+=GetPixelChannels(image);
8253              }
8254
8255              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8256                 break;
8257            }
8258
8259            if (image->storage_class == PseudoClass && image->colormap != NULL)
8260            {
8261              for (i=0; i < (ssize_t) image->colors; i++)
8262              {
8263                LBR04PacketRGBO(image->colormap[i]);
8264              }
8265            }
8266          }
8267        else if (image->depth > 1)
8268          {
8269            /* Scale to 2-bit */
8270            LBR02PacketRGBO(image->background_color);
8271
8272            for (y=0; y < (ssize_t) image->rows; y++)
8273            {
8274              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8275
8276              if (r == (Quantum *) NULL)
8277                break;
8278
8279              for (x=0; x < (ssize_t) image->columns; x++)
8280              {
8281                 LBR02PixelRGBA(r);
8282                 r+=GetPixelChannels(image);
8283              }
8284
8285              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8286                 break;
8287            }
8288
8289            if (image->storage_class == PseudoClass && image->colormap != NULL)
8290            {
8291              for (i=0; i < (ssize_t) image->colors; i++)
8292              {
8293                LBR02PacketRGBO(image->colormap[i]);
8294              }
8295            }
8296          }
8297        else
8298          {
8299            /* Scale to 1-bit */
8300            LBR01PacketRGBO(image->background_color);
8301
8302            for (y=0; y < (ssize_t) image->rows; y++)
8303            {
8304              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8305
8306              if (r == (Quantum *) NULL)
8307                break;
8308
8309              for (x=0; x < (ssize_t) image->columns; x++)
8310              {
8311                 LBR01PixelRGBA(r);
8312                 r+=GetPixelChannels(image);
8313              }
8314
8315              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8316                 break;
8317            }
8318
8319            if (image->storage_class == PseudoClass && image->colormap != NULL)
8320            {
8321              for (i=0; i < (ssize_t) image->colors; i++)
8322              {
8323                LBR01PacketRGBO(image->colormap[i]);
8324              }
8325            }
8326          }
8327     }
8328
8329   /* To do: set to next higher multiple of 8 */
8330   if (image->depth < 8)
8331      image->depth=8;
8332
8333 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8334   /* PNG does not handle depths greater than 16 so reduce it even
8335    * if lossy
8336    */
8337   if (image->depth > 8)
8338       image->depth=16;
8339 #endif
8340
8341 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8342   if (image->depth > 8)
8343     {
8344       /* To do: fill low byte properly */
8345       image->depth=16;
8346     }
8347
8348   if (image->depth == 16 && mng_info->write_png_depth != 16)
8349     if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8350       image->depth = 8;
8351 #endif
8352
8353   image_colors = (int) image->colors;
8354   number_opaque = (int) image->colors;
8355   number_transparent = 0;
8356   number_semitransparent = 0;
8357
8358   if (mng_info->write_png_colortype &&
8359      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8360      mng_info->write_png_colortype < 4 &&
8361      image->alpha_trait == UndefinedPixelTrait)))
8362   {
8363      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8364       * are not going to need the result.
8365       */
8366      if (mng_info->write_png_colortype == 1 ||
8367         mng_info->write_png_colortype == 5)
8368        ping_have_color=MagickFalse;
8369
8370      if (image->alpha_trait != UndefinedPixelTrait)
8371        {
8372          number_transparent = 2;
8373          number_semitransparent = 1;
8374        }
8375   }
8376
8377   if (mng_info->write_png_colortype < 7)
8378   {
8379   /* BUILD_PALETTE
8380    *
8381    * Normally we run this just once, but in the case of writing PNG8
8382    * we reduce the transparency to binary and run again, then if there
8383    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8384    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8385    * palette.  Then (To do) we take care of a final reduction that is only
8386    * needed if there are still 256 colors present and one of them has both
8387    * transparent and opaque instances.
8388    */
8389
8390   tried_332 = MagickFalse;
8391   tried_333 = MagickFalse;
8392   tried_444 = MagickFalse;
8393
8394   for (j=0; j<6; j++)
8395   {
8396     /*
8397      * Sometimes we get DirectClass images that have 256 colors or fewer.
8398      * This code will build a colormap.
8399      *
8400      * Also, sometimes we get PseudoClass images with an out-of-date
8401      * colormap.  This code will replace the colormap with a new one.
8402      * Sometimes we get PseudoClass images that have more than 256 colors.
8403      * This code will delete the colormap and change the image to
8404      * DirectClass.
8405      *
8406      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8407      * even though it sometimes contains left-over non-opaque values.
8408      *
8409      * Also we gather some information (number of opaque, transparent,
8410      * and semitransparent pixels, and whether the image has any non-gray
8411      * pixels or only black-and-white pixels) that we might need later.
8412      *
8413      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8414      * we need to check for bogus non-opaque values, at least.
8415      */
8416
8417    int
8418      n;
8419
8420    PixelInfo
8421      opaque[260],
8422      semitransparent[260],
8423      transparent[260];
8424
8425    register const Quantum
8426      *s;
8427
8428    register Quantum
8429      *q,
8430      *r;
8431
8432    if (logging != MagickFalse)
8433      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8434          "    Enter BUILD_PALETTE:");
8435
8436    if (logging != MagickFalse)
8437      {
8438        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8439              "      image->columns=%.20g",(double) image->columns);
8440        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8441              "      image->rows=%.20g",(double) image->rows);
8442        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8443              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
8444        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8445              "      image->depth=%.20g",(double) image->depth);
8446
8447        if (image->storage_class == PseudoClass && image->colormap != NULL)
8448        {
8449          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8450              "      Original colormap:");
8451          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8452              "        i    (red,green,blue,alpha)");
8453
8454          for (i=0; i < 256; i++)
8455          {
8456                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8457                    "        %d    (%d,%d,%d,%d)",
8458                     (int) i,
8459                     (int) image->colormap[i].red,
8460                     (int) image->colormap[i].green,
8461                     (int) image->colormap[i].blue,
8462                     (int) image->colormap[i].alpha);
8463          }
8464
8465          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8466          {
8467            if (i > 255)
8468              {
8469                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8470                    "        %d    (%d,%d,%d,%d)",
8471                     (int) i,
8472                     (int) image->colormap[i].red,
8473                     (int) image->colormap[i].green,
8474                     (int) image->colormap[i].blue,
8475                     (int) image->colormap[i].alpha);
8476              }
8477          }
8478        }
8479
8480        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8481            "      image->colors=%d",(int) image->colors);
8482
8483        if (image->colors == 0)
8484          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8485              "        (zero means unknown)");
8486
8487        if (ping_preserve_colormap == MagickFalse)
8488          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8489               "      Regenerate the colormap");
8490      }
8491
8492      image_colors=0;
8493      number_opaque = 0;
8494      number_semitransparent = 0;
8495      number_transparent = 0;
8496
8497      for (y=0; y < (ssize_t) image->rows; y++)
8498      {
8499        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8500
8501        if (q == (Quantum *) NULL)
8502          break;
8503
8504        for (x=0; x < (ssize_t) image->columns; x++)
8505        {
8506            if (image->alpha_trait == UndefinedPixelTrait ||
8507               GetPixelAlpha(image,q) == OpaqueAlpha)
8508              {
8509                if (number_opaque < 259)
8510                  {
8511                    if (number_opaque == 0)
8512                      {
8513                        GetPixelInfoPixel(image, q, opaque);
8514                        opaque[0].alpha=OpaqueAlpha;
8515                        number_opaque=1;
8516                      }
8517
8518                    for (i=0; i< (ssize_t) number_opaque; i++)
8519                      {
8520                        if (IsPixelEquivalent(image,q, opaque+i))
8521                          break;
8522                      }
8523
8524                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
8525                      {
8526                        number_opaque++;
8527                        GetPixelInfoPixel(image, q, opaque+i);
8528                        opaque[i].alpha=OpaqueAlpha;
8529                      }
8530                  }
8531              }
8532            else if (GetPixelAlpha(image,q) == TransparentAlpha)
8533              {
8534                if (number_transparent < 259)
8535                  {
8536                    if (number_transparent == 0)
8537                      {
8538                        GetPixelInfoPixel(image, q, transparent);
8539                        ping_trans_color.red=(unsigned short)
8540                          GetPixelRed(image,q);
8541                        ping_trans_color.green=(unsigned short)
8542                          GetPixelGreen(image,q);
8543                        ping_trans_color.blue=(unsigned short)
8544                          GetPixelBlue(image,q);
8545                        ping_trans_color.gray=(unsigned short)
8546                          GetPixelGray(image,q);
8547                        number_transparent = 1;
8548                      }
8549
8550                    for (i=0; i< (ssize_t) number_transparent; i++)
8551                      {
8552                        if (IsPixelEquivalent(image,q, transparent+i))
8553                          break;
8554                      }
8555
8556                    if (i ==  (ssize_t) number_transparent &&
8557                        number_transparent < 259)
8558                      {
8559                        number_transparent++;
8560                        GetPixelInfoPixel(image,q,transparent+i);
8561                      }
8562                  }
8563              }
8564            else
8565              {
8566                if (number_semitransparent < 259)
8567                  {
8568                    if (number_semitransparent == 0)
8569                      {
8570                        GetPixelInfoPixel(image,q,semitransparent);
8571                        number_semitransparent = 1;
8572                      }
8573
8574                    for (i=0; i< (ssize_t) number_semitransparent; i++)
8575                      {
8576                        if (IsPixelEquivalent(image,q, semitransparent+i)
8577                            && GetPixelAlpha(image,q) ==
8578                            semitransparent[i].alpha)
8579                          break;
8580                      }
8581
8582                    if (i ==  (ssize_t) number_semitransparent &&
8583                        number_semitransparent < 259)
8584                      {
8585                        number_semitransparent++;
8586                        GetPixelInfoPixel(image, q, semitransparent+i);
8587                      }
8588                  }
8589              }
8590            q+=GetPixelChannels(image);
8591         }
8592      }
8593
8594      if (mng_info->write_png8 == MagickFalse &&
8595          ping_exclude_bKGD == MagickFalse)
8596        {
8597          /* Add the background color to the palette, if it
8598           * isn't already there.
8599           */
8600           if (logging != MagickFalse)
8601             {
8602               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8603                   "      Check colormap for background (%d,%d,%d)",
8604                   (int) image->background_color.red,
8605                   (int) image->background_color.green,
8606                   (int) image->background_color.blue);
8607             }
8608           for (i=0; i<number_opaque; i++)
8609           {
8610              if (opaque[i].red == image->background_color.red &&
8611                  opaque[i].green == image->background_color.green &&
8612                  opaque[i].blue == image->background_color.blue)
8613                break;
8614           }
8615           if (number_opaque < 259 && i == number_opaque)
8616             {
8617                opaque[i] = image->background_color;
8618                ping_background.index = i;
8619                number_opaque++;
8620                if (logging != MagickFalse)
8621                  {
8622                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8623                        "      background_color index is %d",(int) i);
8624                  }
8625
8626             }
8627           else if (logging != MagickFalse)
8628               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8629                   "      No room in the colormap to add background color");
8630        }
8631
8632      image_colors=number_opaque+number_transparent+number_semitransparent;
8633
8634      if (logging != MagickFalse)
8635        {
8636          if (image_colors > 256)
8637             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8638                   "      image has more than 256 colors");
8639
8640          else
8641             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8642                   "      image has %d colors",image_colors);
8643        }
8644
8645      if (ping_preserve_colormap != MagickFalse)
8646        break;
8647
8648      if (mng_info->write_png_colortype != 7) /* We won't need this info */
8649        {
8650          ping_have_color=MagickFalse;
8651          ping_have_non_bw=MagickFalse;
8652
8653          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8654          {
8655            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8656               "incompatible colorspace");
8657            ping_have_color=MagickTrue;
8658            ping_have_non_bw=MagickTrue;
8659          }
8660
8661          if(image_colors > 256)
8662            {
8663              for (y=0; y < (ssize_t) image->rows; y++)
8664              {
8665                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8666
8667                if (q == (Quantum *) NULL)
8668                  break;
8669
8670                s=q;
8671                for (x=0; x < (ssize_t) image->columns; x++)
8672                {
8673                  if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8674                      GetPixelRed(image,s) != GetPixelBlue(image,s))
8675                    {
8676                       ping_have_color=MagickTrue;
8677                       ping_have_non_bw=MagickTrue;
8678                       break;
8679                    }
8680                  s+=GetPixelChannels(image);
8681                }
8682
8683                if (ping_have_color != MagickFalse)
8684                  break;
8685
8686                /* Worst case is black-and-white; we are looking at every
8687                 * pixel twice.
8688                 */
8689
8690                if (ping_have_non_bw == MagickFalse)
8691                  {
8692                    s=q;
8693                    for (x=0; x < (ssize_t) image->columns; x++)
8694                    {
8695                      if (GetPixelRed(image,s) != 0 &&
8696                          GetPixelRed(image,s) != QuantumRange)
8697                        {
8698                          ping_have_non_bw=MagickTrue;
8699                          break;
8700                        }
8701                      s+=GetPixelChannels(image);
8702                    }
8703                }
8704              }
8705            }
8706        }
8707
8708      if (image_colors < 257)
8709        {
8710          PixelInfo
8711            colormap[260];
8712
8713          /*
8714           * Initialize image colormap.
8715           */
8716
8717          if (logging != MagickFalse)
8718             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8719                   "      Sort the new colormap");
8720
8721         /* Sort palette, transparent first */;
8722
8723          n = 0;
8724
8725          for (i=0; i<number_transparent; i++)
8726             colormap[n++] = transparent[i];
8727
8728          for (i=0; i<number_semitransparent; i++)
8729             colormap[n++] = semitransparent[i];
8730
8731          for (i=0; i<number_opaque; i++)
8732             colormap[n++] = opaque[i];
8733
8734          ping_background.index +=
8735            (number_transparent + number_semitransparent);
8736
8737          /* image_colors < 257; search the colormap instead of the pixels
8738           * to get ping_have_color and ping_have_non_bw
8739           */
8740          for (i=0; i<n; i++)
8741          {
8742            if (ping_have_color == MagickFalse)
8743              {
8744                 if (colormap[i].red != colormap[i].green ||
8745                     colormap[i].red != colormap[i].blue)
8746                   {
8747                      ping_have_color=MagickTrue;
8748                      ping_have_non_bw=MagickTrue;
8749                      break;
8750                   }
8751               }
8752
8753            if (ping_have_non_bw == MagickFalse)
8754              {
8755                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8756                    ping_have_non_bw=MagickTrue;
8757              }
8758           }
8759
8760         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8761             (number_transparent == 0 && number_semitransparent == 0)) &&
8762             (((mng_info->write_png_colortype-1) ==
8763             PNG_COLOR_TYPE_PALETTE) ||
8764             (mng_info->write_png_colortype == 0)))
8765           {
8766             if (logging != MagickFalse)
8767               {
8768                 if (n !=  (ssize_t) image_colors)
8769                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8770                    "   image_colors (%d) and n (%d)  don't match",
8771                    image_colors, n);
8772
8773                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8774                    "      AcquireImageColormap");
8775               }
8776
8777             image->colors = image_colors;
8778
8779             if (AcquireImageColormap(image,image_colors,exception) ==
8780                 MagickFalse)
8781                ThrowWriterException(ResourceLimitError,
8782                    "MemoryAllocationFailed");
8783
8784             for (i=0; i< (ssize_t) image_colors; i++)
8785                image->colormap[i] = colormap[i];
8786
8787             if (logging != MagickFalse)
8788               {
8789                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8790                       "      image->colors=%d (%d)",
8791                       (int) image->colors, image_colors);
8792
8793                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8794                       "      Update the pixel indexes");
8795               }
8796
8797             /* Sync the pixel indices with the new colormap */
8798
8799             for (y=0; y < (ssize_t) image->rows; y++)
8800             {
8801               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8802
8803               if (q == (Quantum *) NULL)
8804                 break;
8805
8806               for (x=0; x < (ssize_t) image->columns; x++)
8807               {
8808                 for (i=0; i< (ssize_t) image_colors; i++)
8809                 {
8810                   if ((image->alpha_trait == UndefinedPixelTrait ||
8811                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8812                       image->colormap[i].red == GetPixelRed(image,q) &&
8813                       image->colormap[i].green == GetPixelGreen(image,q) &&
8814                       image->colormap[i].blue == GetPixelBlue(image,q))
8815                   {
8816                     SetPixelIndex(image,i,q);
8817                     break;
8818                   }
8819                 }
8820                 q+=GetPixelChannels(image);
8821               }
8822
8823               if (SyncAuthenticPixels(image,exception) == MagickFalse)
8824                  break;
8825             }
8826           }
8827        }
8828
8829      if (logging != MagickFalse)
8830        {
8831          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8832             "      image->colors=%d", (int) image->colors);
8833
8834          if (image->colormap != NULL)
8835            {
8836              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8837                  "       i     (red,green,blue,alpha)");
8838
8839              for (i=0; i < (ssize_t) image->colors; i++)
8840              {
8841                if (i < 300 || i >= (ssize_t) image->colors - 10)
8842                  {
8843                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8844                        "       %d     (%d,%d,%d,%d)",
8845                         (int) i,
8846                         (int) image->colormap[i].red,
8847                         (int) image->colormap[i].green,
8848                         (int) image->colormap[i].blue,
8849                         (int) image->colormap[i].alpha);
8850                  }
8851              }
8852            }
8853
8854            if (number_transparent < 257)
8855              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8856                    "      number_transparent     = %d",
8857                    number_transparent);
8858            else
8859
8860              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8861                    "      number_transparent     > 256");
8862
8863            if (number_opaque < 257)
8864              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8865                    "      number_opaque          = %d",
8866                    number_opaque);
8867
8868            else
8869              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8870                    "      number_opaque          > 256");
8871
8872            if (number_semitransparent < 257)
8873              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8874                    "      number_semitransparent = %d",
8875                    number_semitransparent);
8876
8877            else
8878              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8879                    "      number_semitransparent > 256");
8880
8881            if (ping_have_non_bw == MagickFalse)
8882               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8883                     "      All pixels and the background are black or white");
8884
8885            else if (ping_have_color == MagickFalse)
8886               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8887                     "      All pixels and the background are gray");
8888
8889            else
8890               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8891                     "      At least one pixel or the background is non-gray");
8892
8893            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8894                "    Exit BUILD_PALETTE:");
8895        }
8896
8897    if (mng_info->write_png8 == MagickFalse)
8898       break;
8899
8900    /* Make any reductions necessary for the PNG8 format */
8901     if (image_colors <= 256 &&
8902         image_colors != 0 && image->colormap != NULL &&
8903         number_semitransparent == 0 &&
8904         number_transparent <= 1)
8905       break;
8906
8907     /* PNG8 can't have semitransparent colors so we threshold the
8908      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8909      * transparent color so if more than one is transparent we merge
8910      * them into image->background_color.
8911      */
8912     if (number_semitransparent != 0 || number_transparent > 1)
8913       {
8914         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8915             "    Thresholding the alpha channel to binary");
8916
8917         for (y=0; y < (ssize_t) image->rows; y++)
8918         {
8919           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8920
8921           if (r == (Quantum *) NULL)
8922             break;
8923
8924           for (x=0; x < (ssize_t) image->columns; x++)
8925           {
8926               if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8927                 {
8928                   SetPixelViaPixelInfo(image,&image->background_color,r);
8929                   SetPixelAlpha(image,TransparentAlpha,r);
8930                 }
8931               else
8932                   SetPixelAlpha(image,OpaqueAlpha,r);
8933               r+=GetPixelChannels(image);
8934           }
8935
8936           if (SyncAuthenticPixels(image,exception) == MagickFalse)
8937              break;
8938
8939           if (image_colors != 0 && image_colors <= 256 &&
8940              image->colormap != NULL)
8941             for (i=0; i<image_colors; i++)
8942                 image->colormap[i].alpha =
8943                     (image->colormap[i].alpha > TransparentAlpha/2 ?
8944                     TransparentAlpha : OpaqueAlpha);
8945         }
8946       continue;
8947     }
8948
8949     /* PNG8 can't have more than 256 colors so we quantize the pixels and
8950      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
8951      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8952      * colors or less.
8953      */
8954     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8955       {
8956         if (logging != MagickFalse)
8957            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8958                "    Quantizing the background color to 4-4-4");
8959
8960         tried_444 = MagickTrue;
8961
8962         LBR04PacketRGB(image->background_color);
8963
8964         if (logging != MagickFalse)
8965           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8966               "    Quantizing the pixel colors to 4-4-4");
8967
8968         if (image->colormap == NULL)
8969         {
8970           for (y=0; y < (ssize_t) image->rows; y++)
8971           {
8972             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8973
8974             if (r == (Quantum *) NULL)
8975               break;
8976
8977             for (x=0; x < (ssize_t) image->columns; x++)
8978             {
8979               if (GetPixelAlpha(image,r) == OpaqueAlpha)
8980                   LBR04PixelRGB(r);
8981               r+=GetPixelChannels(image);
8982             }
8983
8984             if (SyncAuthenticPixels(image,exception) == MagickFalse)
8985                break;
8986           }
8987         }
8988
8989         else /* Should not reach this; colormap already exists and
8990                 must be <= 256 */
8991         {
8992           if (logging != MagickFalse)
8993               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8994               "    Quantizing the colormap to 4-4-4");
8995
8996           for (i=0; i<image_colors; i++)
8997           {
8998             LBR04PacketRGB(image->colormap[i]);
8999           }
9000         }
9001         continue;
9002       }
9003
9004     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9005       {
9006         if (logging != MagickFalse)
9007            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9008                "    Quantizing the background color to 3-3-3");
9009
9010         tried_333 = MagickTrue;
9011
9012         LBR03PacketRGB(image->background_color);
9013
9014         if (logging != MagickFalse)
9015           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9016               "    Quantizing the pixel colors to 3-3-3-1");
9017
9018         if (image->colormap == NULL)
9019         {
9020           for (y=0; y < (ssize_t) image->rows; y++)
9021           {
9022             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9023
9024             if (r == (Quantum *) NULL)
9025               break;
9026
9027             for (x=0; x < (ssize_t) image->columns; x++)
9028             {
9029               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9030                   LBR03RGB(r);
9031               r+=GetPixelChannels(image);
9032             }
9033
9034             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9035                break;
9036           }
9037         }
9038
9039         else /* Should not reach this; colormap already exists and
9040                 must be <= 256 */
9041         {
9042           if (logging != MagickFalse)
9043               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9044               "    Quantizing the colormap to 3-3-3-1");
9045           for (i=0; i<image_colors; i++)
9046           {
9047               LBR03PacketRGB(image->colormap[i]);
9048           }
9049         }
9050         continue;
9051       }
9052
9053     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9054       {
9055         if (logging != MagickFalse)
9056            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9057                "    Quantizing the background color to 3-3-2");
9058
9059         tried_332 = MagickTrue;
9060
9061         /* Red and green were already done so we only quantize the blue
9062          * channel
9063          */
9064
9065         LBR02PacketBlue(image->background_color);
9066
9067         if (logging != MagickFalse)
9068           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9069               "    Quantizing the pixel colors to 3-3-2-1");
9070
9071         if (image->colormap == NULL)
9072         {
9073           for (y=0; y < (ssize_t) image->rows; y++)
9074           {
9075             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9076
9077             if (r == (Quantum *) NULL)
9078               break;
9079
9080             for (x=0; x < (ssize_t) image->columns; x++)
9081             {
9082               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9083                   LBR02PixelBlue(r);
9084               r+=GetPixelChannels(image);
9085             }
9086
9087             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9088                break;
9089           }
9090         }
9091
9092         else /* Should not reach this; colormap already exists and
9093                 must be <= 256 */
9094         {
9095           if (logging != MagickFalse)
9096               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9097               "    Quantizing the colormap to 3-3-2-1");
9098           for (i=0; i<image_colors; i++)
9099           {
9100               LBR02PacketBlue(image->colormap[i]);
9101           }
9102       }
9103       continue;
9104     }
9105
9106     if (image_colors == 0 || image_colors > 256)
9107     {
9108       /* Take care of special case with 256 opaque colors + 1 transparent
9109        * color.  We don't need to quantize to 2-3-2-1; we only need to
9110        * eliminate one color, so we'll merge the two darkest red
9111        * colors (0x49, 0, 0) -> (0x24, 0, 0).
9112        */
9113       if (logging != MagickFalse)
9114         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9115             "    Merging two dark red background colors to 3-3-2-1");
9116
9117       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9118           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9119           ScaleQuantumToChar(image->background_color.blue) == 0x00)
9120       {
9121          image->background_color.red=ScaleCharToQuantum(0x24);
9122       }
9123
9124       if (logging != MagickFalse)
9125         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9126             "    Merging two dark red pixel colors to 3-3-2-1");
9127
9128       if (image->colormap == NULL)
9129       {
9130         for (y=0; y < (ssize_t) image->rows; y++)
9131         {
9132           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9133
9134           if (r == (Quantum *) NULL)
9135             break;
9136
9137           for (x=0; x < (ssize_t) image->columns; x++)
9138           {
9139             if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9140                 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9141                 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9142                 GetPixelAlpha(image,r) == OpaqueAlpha)
9143               {
9144                 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9145               }
9146             r+=GetPixelChannels(image);
9147           }
9148
9149           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9150              break;
9151
9152         }
9153       }
9154
9155       else
9156       {
9157          for (i=0; i<image_colors; i++)
9158          {
9159             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9160                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9161                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9162             {
9163                image->colormap[i].red=ScaleCharToQuantum(0x24);
9164             }
9165          }
9166       }
9167     }
9168   }
9169   }
9170   /* END OF BUILD_PALETTE */
9171
9172   /* If we are excluding the tRNS chunk and there is transparency,
9173    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9174    * PNG.
9175    */
9176   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9177      (number_transparent != 0 || number_semitransparent != 0))
9178     {
9179       unsigned int colortype=mng_info->write_png_colortype;
9180
9181       if (ping_have_color == MagickFalse)
9182         mng_info->write_png_colortype = 5;
9183
9184       else
9185         mng_info->write_png_colortype = 7;
9186
9187       if (colortype != 0 &&
9188          mng_info->write_png_colortype != colortype)
9189         ping_need_colortype_warning=MagickTrue;
9190
9191     }
9192
9193   /* See if cheap transparency is possible.  It is only possible
9194    * when there is a single transparent color, no semitransparent
9195    * color, and no opaque color that has the same RGB components
9196    * as the transparent color.  We only need this information if
9197    * we are writing a PNG with colortype 0 or 2, and we have not
9198    * excluded the tRNS chunk.
9199    */
9200   if (number_transparent == 1 &&
9201       mng_info->write_png_colortype < 4)
9202     {
9203        ping_have_cheap_transparency = MagickTrue;
9204
9205        if (number_semitransparent != 0)
9206          ping_have_cheap_transparency = MagickFalse;
9207
9208        else if (image_colors == 0 || image_colors > 256 ||
9209            image->colormap == NULL)
9210          {
9211            register const Quantum
9212              *q;
9213
9214            for (y=0; y < (ssize_t) image->rows; y++)
9215            {
9216              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9217
9218              if (q == (Quantum *) NULL)
9219                break;
9220
9221              for (x=0; x < (ssize_t) image->columns; x++)
9222              {
9223                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9224                      (unsigned short) GetPixelRed(image,q) ==
9225                                      ping_trans_color.red &&
9226                      (unsigned short) GetPixelGreen(image,q) ==
9227                                      ping_trans_color.green &&
9228                      (unsigned short) GetPixelBlue(image,q) ==
9229                                      ping_trans_color.blue)
9230                    {
9231                      ping_have_cheap_transparency = MagickFalse;
9232                      break;
9233                    }
9234
9235                  q+=GetPixelChannels(image);
9236              }
9237
9238              if (ping_have_cheap_transparency == MagickFalse)
9239                 break;
9240            }
9241          }
9242        else
9243          {
9244             /* Assuming that image->colormap[0] is the one transparent color
9245              * and that all others are opaque.
9246              */
9247             if (image_colors > 1)
9248               for (i=1; i<image_colors; i++)
9249                 if (image->colormap[i].red == image->colormap[0].red &&
9250                     image->colormap[i].green == image->colormap[0].green &&
9251                     image->colormap[i].blue == image->colormap[0].blue)
9252                   {
9253                      ping_have_cheap_transparency = MagickFalse;
9254                      break;
9255                   }
9256          }
9257
9258        if (logging != MagickFalse)
9259          {
9260            if (ping_have_cheap_transparency == MagickFalse)
9261              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9262                  "   Cheap transparency is not possible.");
9263
9264            else
9265              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9266                  "   Cheap transparency is possible.");
9267          }
9268      }
9269   else
9270     ping_have_cheap_transparency = MagickFalse;
9271
9272   image_depth=image->depth;
9273
9274   quantum_info = (QuantumInfo *) NULL;
9275   number_colors=0;
9276   image_colors=(int) image->colors;
9277   image_matte=image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse;
9278
9279   if (mng_info->write_png_colortype < 5)
9280     mng_info->IsPalette=image->storage_class == PseudoClass &&
9281       image_colors <= 256 && image->colormap != NULL;
9282   else
9283     mng_info->IsPalette = MagickFalse;
9284
9285   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9286      (image->colors == 0 || image->colormap == NULL))
9287     {
9288       image_info=DestroyImageInfo(image_info);
9289       image=DestroyImage(image);
9290       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9291           "Cannot write PNG8 or color-type 3; colormap is NULL",
9292           "`%s'",IMimage->filename);
9293       return(MagickFalse);
9294     }
9295
9296   /*
9297     Allocate the PNG structures
9298   */
9299 #ifdef PNG_USER_MEM_SUPPORTED
9300  error_info.image=image;
9301  error_info.exception=exception;
9302   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9303     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9304     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9305
9306 #else
9307   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9308     MagickPNGErrorHandler,MagickPNGWarningHandler);
9309
9310 #endif
9311   if (ping == (png_struct *) NULL)
9312     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9313
9314   ping_info=png_create_info_struct(ping);
9315
9316   if (ping_info == (png_info *) NULL)
9317     {
9318       png_destroy_write_struct(&ping,(png_info **) NULL);
9319       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9320     }
9321
9322   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9323   pixel_info=(MemoryInfo *) NULL;
9324
9325   if (setjmp(png_jmpbuf(ping)))
9326     {
9327       /*
9328         PNG write failed.
9329       */
9330 #ifdef PNG_DEBUG
9331      if (image_info->verbose)
9332         (void) printf("PNG write has failed.\n");
9333 #endif
9334       png_destroy_write_struct(&ping,&ping_info);
9335 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9336       UnlockSemaphoreInfo(ping_semaphore);
9337 #endif
9338
9339       if (pixel_info != (MemoryInfo *) NULL)
9340         pixel_info=RelinquishVirtualMemory(pixel_info);
9341
9342       if (quantum_info != (QuantumInfo *) NULL)
9343         quantum_info=DestroyQuantumInfo(quantum_info);
9344
9345       if (ping_have_blob != MagickFalse)
9346           (void) CloseBlob(image);
9347       image_info=DestroyImageInfo(image_info);
9348       image=DestroyImage(image);
9349       return(MagickFalse);
9350     }
9351
9352   /* {  For navigation to end of SETJMP-protected block.  Within this
9353    *    block, use png_error() instead of Throwing an Exception, to ensure
9354    *    that libpng is able to clean up, and that the semaphore is unlocked.
9355    */
9356
9357 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9358   LockSemaphoreInfo(ping_semaphore);
9359 #endif
9360
9361 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9362   /* Allow benign errors */
9363   png_set_benign_errors(ping, 1);
9364 #endif
9365
9366   /*
9367     Prepare PNG for writing.
9368   */
9369
9370 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9371   if (mng_info->write_mng)
9372   {
9373      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9374 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9375      /* Disable new libpng-1.5.10 feature when writing a MNG because
9376       * zero-length PLTE is OK
9377       */
9378      png_set_check_for_invalid_index (ping, 0);
9379 # endif
9380   }
9381
9382 #else
9383 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9384   if (mng_info->write_mng)
9385      png_permit_empty_plte(ping,MagickTrue);
9386
9387 # endif
9388 #endif
9389
9390   x=0;
9391
9392   ping_width=(png_uint_32) image->columns;
9393   ping_height=(png_uint_32) image->rows;
9394
9395   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9396      image_depth=8;
9397
9398   if (mng_info->write_png48 || mng_info->write_png64)
9399      image_depth=16;
9400
9401   if (mng_info->write_png_depth != 0)
9402      image_depth=mng_info->write_png_depth;
9403
9404   /* Adjust requested depth to next higher valid depth if necessary */
9405   if (image_depth > 8)
9406      image_depth=16;
9407
9408   if ((image_depth > 4) && (image_depth < 8))
9409      image_depth=8;
9410
9411   if (image_depth == 3)
9412      image_depth=4;
9413
9414   if (logging != MagickFalse)
9415     {
9416      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9417         "    width=%.20g",(double) ping_width);
9418      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9419         "    height=%.20g",(double) ping_height);
9420      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9421         "    image_matte=%.20g",(double) image->alpha_trait);
9422      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9423         "    image->depth=%.20g",(double) image->depth);
9424      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9425         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
9426     }
9427
9428   save_image_depth=image_depth;
9429   ping_bit_depth=(png_byte) save_image_depth;
9430
9431
9432 #if defined(PNG_pHYs_SUPPORTED)
9433   if (ping_exclude_pHYs == MagickFalse)
9434   {
9435   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9436       (!mng_info->write_mng || !mng_info->equal_physs))
9437     {
9438       if (logging != MagickFalse)
9439         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9440             "    Setting up pHYs chunk");
9441
9442       if (image->units == PixelsPerInchResolution)
9443         {
9444           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9445           ping_pHYs_x_resolution=
9446              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9447           ping_pHYs_y_resolution=
9448              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9449         }
9450
9451       else if (image->units == PixelsPerCentimeterResolution)
9452         {
9453           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9454           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9455           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9456         }
9457
9458       else
9459         {
9460           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9461           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9462           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9463         }
9464
9465       if (logging != MagickFalse)
9466         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9467           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9468           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9469           (int) ping_pHYs_unit_type);
9470        ping_have_pHYs = MagickTrue;
9471     }
9472   }
9473 #endif
9474
9475   if (ping_exclude_bKGD == MagickFalse)
9476   {
9477   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9478     {
9479        unsigned int
9480          mask;
9481
9482        mask=0xffff;
9483        if (ping_bit_depth == 8)
9484           mask=0x00ff;
9485
9486        if (ping_bit_depth == 4)
9487           mask=0x000f;
9488
9489        if (ping_bit_depth == 2)
9490           mask=0x0003;
9491
9492        if (ping_bit_depth == 1)
9493           mask=0x0001;
9494
9495        ping_background.red=(png_uint_16)
9496          (ScaleQuantumToShort(image->background_color.red) & mask);
9497
9498        ping_background.green=(png_uint_16)
9499          (ScaleQuantumToShort(image->background_color.green) & mask);
9500
9501        ping_background.blue=(png_uint_16)
9502          (ScaleQuantumToShort(image->background_color.blue) & mask);
9503
9504        ping_background.gray=(png_uint_16) ping_background.green;
9505     }
9506
9507   if (logging != MagickFalse)
9508     {
9509       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9510           "    Setting up bKGD chunk (1)");
9511       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9512           "      background_color index is %d",
9513           (int) ping_background.index);
9514
9515       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9516           "    ping_bit_depth=%d",ping_bit_depth);
9517     }
9518
9519   ping_have_bKGD = MagickTrue;
9520   }
9521
9522   /*
9523     Select the color type.
9524   */
9525   matte=image_matte;
9526   old_bit_depth=0;
9527
9528   if (mng_info->IsPalette && mng_info->write_png8)
9529     {
9530       /* To do: make this a function cause it's used twice, except
9531          for reducing the sample depth from 8. */
9532
9533       number_colors=image_colors;
9534
9535       ping_have_tRNS=MagickFalse;
9536
9537       /*
9538         Set image palette.
9539       */
9540       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9541
9542       if (logging != MagickFalse)
9543         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9544             "  Setting up PLTE chunk with %d colors (%d)",
9545             number_colors, image_colors);
9546
9547       for (i=0; i < (ssize_t) number_colors; i++)
9548       {
9549         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9550         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9551         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9552         if (logging != MagickFalse)
9553           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9554 #if MAGICKCORE_QUANTUM_DEPTH == 8
9555             "    %3ld (%3d,%3d,%3d)",
9556 #else
9557             "    %5ld (%5d,%5d,%5d)",
9558 #endif
9559             (long) i,palette[i].red,palette[i].green,palette[i].blue);
9560
9561       }
9562
9563       ping_have_PLTE=MagickTrue;
9564       image_depth=ping_bit_depth;
9565       ping_num_trans=0;
9566
9567       if (matte != MagickFalse)
9568       {
9569           /*
9570             Identify which colormap entry is transparent.
9571           */
9572           assert(number_colors <= 256);
9573           assert(image->colormap != NULL);
9574
9575           for (i=0; i < (ssize_t) number_transparent; i++)
9576              ping_trans_alpha[i]=0;
9577
9578
9579           ping_num_trans=(unsigned short) (number_transparent +
9580              number_semitransparent);
9581
9582           if (ping_num_trans == 0)
9583              ping_have_tRNS=MagickFalse;
9584
9585           else
9586              ping_have_tRNS=MagickTrue;
9587       }
9588
9589       if (ping_exclude_bKGD == MagickFalse)
9590       {
9591        /*
9592         * Identify which colormap entry is the background color.
9593         */
9594
9595         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9596           if (IsPNGColorEqual(ping_background,image->colormap[i]))
9597             break;
9598
9599         ping_background.index=(png_byte) i;
9600
9601         if (logging != MagickFalse)
9602           {
9603             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9604                  "      background_color index is %d",
9605                  (int) ping_background.index);
9606           }
9607       }
9608     } /* end of write_png8 */
9609
9610   else if (mng_info->write_png_colortype == 1)
9611     {
9612       image_matte=MagickFalse;
9613       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9614     }
9615
9616   else if (mng_info->write_png24 || mng_info->write_png48 ||
9617       mng_info->write_png_colortype == 3)
9618     {
9619       image_matte=MagickFalse;
9620       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9621     }
9622
9623   else if (mng_info->write_png32 || mng_info->write_png64 ||
9624       mng_info->write_png_colortype == 7)
9625     {
9626       image_matte=MagickTrue;
9627       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9628     }
9629
9630   else /* mng_info->write_pngNN not specified */
9631     {
9632       image_depth=ping_bit_depth;
9633
9634       if (mng_info->write_png_colortype != 0)
9635         {
9636           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9637
9638           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9639               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9640             image_matte=MagickTrue;
9641
9642           else
9643             image_matte=MagickFalse;
9644
9645           if (logging != MagickFalse)
9646              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9647              "   PNG colortype %d was specified:",(int) ping_color_type);
9648         }
9649
9650       else /* write_png_colortype not specified */
9651         {
9652           if (logging != MagickFalse)
9653              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9654              "  Selecting PNG colortype:");
9655
9656           ping_color_type=(png_byte) ((matte != MagickFalse)?
9657             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9658
9659           if (image_info->type == TrueColorType)
9660             {
9661               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9662               image_matte=MagickFalse;
9663             }
9664
9665           if (image_info->type == TrueColorAlphaType)
9666             {
9667               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9668               image_matte=MagickTrue;
9669             }
9670
9671           if (image_info->type == PaletteType ||
9672               image_info->type == PaletteAlphaType)
9673             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9674
9675           if (mng_info->write_png_colortype == 0 &&
9676              image_info->type == UndefinedType)
9677             {
9678               if (ping_have_color == MagickFalse)
9679                 {
9680                   if (image_matte == MagickFalse)
9681                     {
9682                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9683                       image_matte=MagickFalse;
9684                     }
9685
9686                   else
9687                     {
9688                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9689                       image_matte=MagickTrue;
9690                     }
9691                 }
9692               else
9693                 {
9694                   if (image_matte == MagickFalse)
9695                     {
9696                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9697                       image_matte=MagickFalse;
9698                     }
9699
9700                   else
9701                     {
9702                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9703                       image_matte=MagickTrue;
9704                     }
9705                  }
9706             }
9707
9708         }
9709
9710       if (logging != MagickFalse)
9711          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9712          "    Selected PNG colortype=%d",ping_color_type);
9713
9714       if (ping_bit_depth < 8)
9715         {
9716           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9717               ping_color_type == PNG_COLOR_TYPE_RGB ||
9718               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9719             ping_bit_depth=8;
9720         }
9721
9722       old_bit_depth=ping_bit_depth;
9723
9724       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9725         {
9726           if (image->alpha_trait == UndefinedPixelTrait && ping_have_non_bw == MagickFalse)
9727              ping_bit_depth=1;
9728         }
9729
9730       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9731         {
9732            size_t one = 1;
9733            ping_bit_depth=1;
9734
9735            if (image->colors == 0)
9736            {
9737               /* DO SOMETHING */
9738                 png_error(ping,"image has 0 colors");
9739            }
9740
9741            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9742              ping_bit_depth <<= 1;
9743         }
9744
9745       if (logging != MagickFalse)
9746          {
9747            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9748             "    Number of colors: %.20g",(double) image_colors);
9749
9750            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9751             "    Tentative PNG bit depth: %d",ping_bit_depth);
9752          }
9753
9754       if (ping_bit_depth < (int) mng_info->write_png_depth)
9755          ping_bit_depth = mng_info->write_png_depth;
9756     }
9757
9758   image_depth=ping_bit_depth;
9759
9760   if (logging != MagickFalse)
9761     {
9762       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9763         "    Tentative PNG color type: %s (%.20g)",
9764         PngColorTypeToString(ping_color_type),
9765         (double) ping_color_type);
9766
9767       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9768         "    image_info->type: %.20g",(double) image_info->type);
9769
9770       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9771         "    image_depth: %.20g",(double) image_depth);
9772
9773       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9774
9775         "    image->depth: %.20g",(double) image->depth);
9776
9777       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9778         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
9779     }
9780
9781   if (matte != MagickFalse)
9782     {
9783       if (mng_info->IsPalette)
9784         {
9785           if (mng_info->write_png_colortype == 0)
9786             {
9787               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9788
9789               if (ping_have_color != MagickFalse)
9790                  ping_color_type=PNG_COLOR_TYPE_RGBA;
9791             }
9792
9793           /*
9794            * Determine if there is any transparent color.
9795           */
9796           if (number_transparent + number_semitransparent == 0)
9797             {
9798               /*
9799                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
9800               */
9801
9802               image_matte=MagickFalse;
9803
9804               if (mng_info->write_png_colortype == 0)
9805                 ping_color_type&=0x03;
9806             }
9807
9808           else
9809             {
9810               unsigned int
9811                 mask;
9812
9813               mask=0xffff;
9814
9815               if (ping_bit_depth == 8)
9816                  mask=0x00ff;
9817
9818               if (ping_bit_depth == 4)
9819                  mask=0x000f;
9820
9821               if (ping_bit_depth == 2)
9822                  mask=0x0003;
9823
9824               if (ping_bit_depth == 1)
9825                  mask=0x0001;
9826
9827               ping_trans_color.red=(png_uint_16)
9828                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9829
9830               ping_trans_color.green=(png_uint_16)
9831                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9832
9833               ping_trans_color.blue=(png_uint_16)
9834                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9835
9836               ping_trans_color.gray=(png_uint_16)
9837                 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
9838                    image->colormap)) & mask);
9839
9840               ping_trans_color.index=(png_byte) 0;
9841
9842               ping_have_tRNS=MagickTrue;
9843             }
9844
9845           if (ping_have_tRNS != MagickFalse)
9846             {
9847               /*
9848                * Determine if there is one and only one transparent color
9849                * and if so if it is fully transparent.
9850                */
9851               if (ping_have_cheap_transparency == MagickFalse)
9852                 ping_have_tRNS=MagickFalse;
9853             }
9854
9855           if (ping_have_tRNS != MagickFalse)
9856             {
9857               if (mng_info->write_png_colortype == 0)
9858                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
9859
9860               if (image_depth == 8)
9861                 {
9862                   ping_trans_color.red&=0xff;
9863                   ping_trans_color.green&=0xff;
9864                   ping_trans_color.blue&=0xff;
9865                   ping_trans_color.gray&=0xff;
9866                 }
9867             }
9868         }
9869       else
9870         {
9871           if (image_depth == 8)
9872             {
9873               ping_trans_color.red&=0xff;
9874               ping_trans_color.green&=0xff;
9875               ping_trans_color.blue&=0xff;
9876               ping_trans_color.gray&=0xff;
9877             }
9878         }
9879     }
9880
9881     matte=image_matte;
9882
9883     if (ping_have_tRNS != MagickFalse)
9884       image_matte=MagickFalse;
9885
9886     if ((mng_info->IsPalette) &&
9887         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9888         ping_have_color == MagickFalse &&
9889         (image_matte == MagickFalse || image_depth >= 8))
9890       {
9891         size_t one=1;
9892
9893         if (image_matte != MagickFalse)
9894           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9895
9896         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9897           {
9898             ping_color_type=PNG_COLOR_TYPE_GRAY;
9899
9900             if (save_image_depth == 16 && image_depth == 8)
9901               {
9902                 if (logging != MagickFalse)
9903                   {
9904                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9905                         "  Scaling ping_trans_color (0)");
9906                   }
9907                     ping_trans_color.gray*=0x0101;
9908               }
9909           }
9910
9911         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9912           image_depth=MAGICKCORE_QUANTUM_DEPTH;
9913
9914         if ((image_colors == 0) ||
9915              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9916           image_colors=(int) (one << image_depth);
9917
9918         if (image_depth > 8)
9919           ping_bit_depth=16;
9920
9921         else
9922           {
9923             ping_bit_depth=8;
9924             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9925               {
9926                 if(!mng_info->write_png_depth)
9927                   {
9928                     ping_bit_depth=1;
9929
9930                     while ((int) (one << ping_bit_depth)
9931                         < (ssize_t) image_colors)
9932                       ping_bit_depth <<= 1;
9933                   }
9934               }
9935
9936             else if (ping_color_type ==
9937                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9938                 mng_info->IsPalette)
9939               {
9940               /* Check if grayscale is reducible */
9941
9942                 int
9943                   depth_4_ok=MagickTrue,
9944                   depth_2_ok=MagickTrue,
9945                   depth_1_ok=MagickTrue;
9946
9947                 for (i=0; i < (ssize_t) image_colors; i++)
9948                 {
9949                    unsigned char
9950                      intensity;
9951
9952                    intensity=ScaleQuantumToChar(image->colormap[i].red);
9953
9954                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9955                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9956                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9957                      depth_2_ok=depth_1_ok=MagickFalse;
9958                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9959                      depth_1_ok=MagickFalse;
9960                 }
9961
9962                 if (depth_1_ok && mng_info->write_png_depth <= 1)
9963                   ping_bit_depth=1;
9964
9965                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9966                   ping_bit_depth=2;
9967
9968                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9969                   ping_bit_depth=4;
9970               }
9971           }
9972
9973           image_depth=ping_bit_depth;
9974       }
9975
9976     else
9977
9978       if (mng_info->IsPalette)
9979       {
9980         number_colors=image_colors;
9981
9982         if (image_depth <= 8)
9983           {
9984             /*
9985               Set image palette.
9986             */
9987             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9988
9989             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
9990               {
9991                 for (i=0; i < (ssize_t) number_colors; i++)
9992                 {
9993                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9994                   palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9995                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9996                 }
9997
9998                 if (logging != MagickFalse)
9999                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10000                     "  Setting up PLTE chunk with %d colors",
10001                     number_colors);
10002
10003                 ping_have_PLTE=MagickTrue;
10004               }
10005
10006             /* color_type is PNG_COLOR_TYPE_PALETTE */
10007             if (mng_info->write_png_depth == 0)
10008               {
10009                 size_t
10010                   one;
10011
10012                 ping_bit_depth=1;
10013                 one=1;
10014
10015                 while ((one << ping_bit_depth) < (size_t) number_colors)
10016                   ping_bit_depth <<= 1;
10017               }
10018
10019             ping_num_trans=0;
10020
10021             if (matte != MagickFalse)
10022               {
10023                 /*
10024                  * Set up trans_colors array.
10025                  */
10026                 assert(number_colors <= 256);
10027
10028                 ping_num_trans=(unsigned short) (number_transparent +
10029                   number_semitransparent);
10030
10031                 if (ping_num_trans == 0)
10032                   ping_have_tRNS=MagickFalse;
10033
10034                 else
10035                   {
10036                     if (logging != MagickFalse)
10037                       {
10038                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10039                           "  Scaling ping_trans_color (1)");
10040                       }
10041                     ping_have_tRNS=MagickTrue;
10042
10043                     for (i=0; i < ping_num_trans; i++)
10044                     {
10045                        ping_trans_alpha[i]= (png_byte)
10046                          ScaleQuantumToChar(image->colormap[i].alpha);
10047                     }
10048                   }
10049               }
10050           }
10051       }
10052
10053     else
10054       {
10055
10056         if (image_depth < 8)
10057           image_depth=8;
10058
10059         if ((save_image_depth == 16) && (image_depth == 8))
10060           {
10061             if (logging != MagickFalse)
10062               {
10063                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10064                   "    Scaling ping_trans_color from (%d,%d,%d)",
10065                   (int) ping_trans_color.red,
10066                   (int) ping_trans_color.green,
10067                   (int) ping_trans_color.blue);
10068               }
10069
10070             ping_trans_color.red*=0x0101;
10071             ping_trans_color.green*=0x0101;
10072             ping_trans_color.blue*=0x0101;
10073             ping_trans_color.gray*=0x0101;
10074
10075             if (logging != MagickFalse)
10076               {
10077                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10078                   "    to (%d,%d,%d)",
10079                   (int) ping_trans_color.red,
10080                   (int) ping_trans_color.green,
10081                   (int) ping_trans_color.blue);
10082               }
10083           }
10084       }
10085
10086     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
10087          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
10088
10089     /*
10090       Adjust background and transparency samples in sub-8-bit grayscale files.
10091     */
10092     if (ping_bit_depth < 8 && ping_color_type ==
10093         PNG_COLOR_TYPE_GRAY)
10094       {
10095          png_uint_16
10096            maxval;
10097
10098          size_t
10099            one=1;
10100
10101          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10102
10103          if (ping_exclude_bKGD == MagickFalse)
10104          {
10105
10106          ping_background.gray=(png_uint_16) ((maxval/65535.)*
10107            (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10108            &image->background_color))) +.5)));
10109
10110          if (logging != MagickFalse)
10111            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10112              "  Setting up bKGD chunk (2)");
10113          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10114              "      background_color index is %d",
10115              (int) ping_background.index);
10116
10117          ping_have_bKGD = MagickTrue;
10118          }
10119
10120          if (logging != MagickFalse)
10121            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10122              "  Scaling ping_trans_color.gray from %d",
10123              (int)ping_trans_color.gray);
10124
10125          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10126            ping_trans_color.gray)+.5);
10127
10128          if (logging != MagickFalse)
10129            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10130              "      to %d", (int)ping_trans_color.gray);
10131       }
10132
10133   if (ping_exclude_bKGD == MagickFalse)
10134   {
10135     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10136       {
10137         /*
10138            Identify which colormap entry is the background color.
10139         */
10140
10141         number_colors=image_colors;
10142
10143         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10144           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10145             break;
10146
10147         ping_background.index=(png_byte) i;
10148
10149         if (logging != MagickFalse)
10150           {
10151             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10152               "  Setting up bKGD chunk with index=%d",(int) i);
10153           }
10154
10155         if (i < (ssize_t) number_colors)
10156           {
10157             ping_have_bKGD = MagickTrue;
10158
10159             if (logging != MagickFalse)
10160               {
10161                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10162                   "     background   =(%d,%d,%d)",
10163                         (int) ping_background.red,
10164                         (int) ping_background.green,
10165                         (int) ping_background.blue);
10166               }
10167           }
10168
10169         else  /* Can't happen */
10170           {
10171             if (logging != MagickFalse)
10172               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10173                   "      No room in PLTE to add bKGD color");
10174             ping_have_bKGD = MagickFalse;
10175           }
10176       }
10177   }
10178
10179   if (logging != MagickFalse)
10180     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10181       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10182       ping_color_type);
10183   /*
10184     Initialize compression level and filtering.
10185   */
10186   if (logging != MagickFalse)
10187     {
10188       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10189         "  Setting up deflate compression");
10190
10191       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10192         "    Compression buffer size: 32768");
10193     }
10194
10195   png_set_compression_buffer_size(ping,32768L);
10196
10197   if (logging != MagickFalse)
10198     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10199       "    Compression mem level: 9");
10200
10201   png_set_compression_mem_level(ping, 9);
10202
10203   /* Untangle the "-quality" setting:
10204
10205      Undefined is 0; the default is used.
10206      Default is 75
10207
10208      10's digit:
10209
10210         0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10211            zlib default compression level
10212
10213         1-9: the zlib compression level
10214
10215      1's digit:
10216
10217         0-4: the PNG filter method
10218
10219         5:   libpng adaptive filtering if compression level > 5
10220              libpng filter type "none" if compression level <= 5
10221                 or if image is grayscale or palette
10222
10223         6:   libpng adaptive filtering
10224
10225         7:   "LOCO" filtering (intrapixel differing) if writing
10226              a MNG, otherwise "none".  Did not work in IM-6.7.0-9
10227              and earlier because of a missing "else".
10228
10229         8:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10230              filtering. Unused prior to IM-6.7.0-10, was same as 6
10231
10232         9:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10233              Unused prior to IM-6.7.0-10, was same as 6
10234
10235     Note that using the -quality option, not all combinations of
10236     PNG filter type, zlib compression level, and zlib compression
10237     strategy are possible.  This will be addressed soon in a
10238     release that accomodates "-define png:compression-strategy", etc.
10239
10240    */
10241
10242   quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10243      image_info->quality;
10244
10245   if (quality <= 9)
10246     {
10247       if (mng_info->write_png_compression_strategy == 0)
10248         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10249     }
10250
10251   else if (mng_info->write_png_compression_level == 0)
10252     {
10253       int
10254         level;
10255
10256       level=(int) MagickMin((ssize_t) quality/10,9);
10257
10258       mng_info->write_png_compression_level = level+1;
10259     }
10260
10261   if (mng_info->write_png_compression_strategy == 0)
10262     {
10263         if ((quality %10) == 8 || (quality %10) == 9)
10264 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10265           mng_info->write_png_compression_strategy=Z_RLE+1;
10266 #else
10267           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10268 #endif
10269     }
10270
10271   if (mng_info->write_png_compression_filter == 0)
10272         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10273
10274   if (logging != MagickFalse)
10275     {
10276      if (mng_info->write_png_compression_level)
10277         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10278           "    Compression level:    %d",
10279             (int) mng_info->write_png_compression_level-1);
10280
10281      if (mng_info->write_png_compression_strategy)
10282         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10283           "    Compression strategy: %d",
10284             (int) mng_info->write_png_compression_strategy-1);
10285
10286         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10287           "  Setting up filtering");
10288
10289         if (mng_info->write_png_compression_filter == 6)
10290           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10291             "    Base filter method: ADAPTIVE");
10292         else if (mng_info->write_png_compression_filter == 0 ||
10293                  mng_info->write_png_compression_filter == 1)
10294           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10295             "    Base filter method: NONE");
10296         else
10297           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10298             "    Base filter method: %d",
10299             (int) mng_info->write_png_compression_filter-1);
10300     }
10301
10302   if (mng_info->write_png_compression_level != 0)
10303     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10304
10305   if (mng_info->write_png_compression_filter == 6)
10306     {
10307       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10308          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10309          (quality < 50))
10310         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10311       else
10312         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10313      }
10314   else if (mng_info->write_png_compression_filter == 7 ||
10315       mng_info->write_png_compression_filter == 10)
10316     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10317
10318   else if (mng_info->write_png_compression_filter == 8)
10319     {
10320 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10321       if (mng_info->write_mng)
10322       {
10323          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10324              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10325         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10326       }
10327 #endif
10328       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10329     }
10330
10331   else if (mng_info->write_png_compression_filter == 9)
10332     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10333
10334   else if (mng_info->write_png_compression_filter != 0)
10335     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10336        mng_info->write_png_compression_filter-1);
10337
10338   if (mng_info->write_png_compression_strategy != 0)
10339     png_set_compression_strategy(ping,
10340        mng_info->write_png_compression_strategy-1);
10341
10342   ping_interlace_method=image_info->interlace != NoInterlace;
10343
10344   if (mng_info->write_mng)
10345     png_set_sig_bytes(ping,8);
10346
10347   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10348
10349   if (mng_info->write_png_colortype != 0)
10350     {
10351      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10352        if (ping_have_color != MagickFalse)
10353          {
10354            ping_color_type = PNG_COLOR_TYPE_RGB;
10355
10356            if (ping_bit_depth < 8)
10357              ping_bit_depth=8;
10358          }
10359
10360      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10361        if (ping_have_color != MagickFalse)
10362          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10363     }
10364
10365   if (ping_need_colortype_warning != MagickFalse ||
10366      ((mng_info->write_png_depth &&
10367      (int) mng_info->write_png_depth != ping_bit_depth) ||
10368      (mng_info->write_png_colortype &&
10369      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10370       mng_info->write_png_colortype != 7 &&
10371       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10372     {
10373       if (logging != MagickFalse)
10374         {
10375           if (ping_need_colortype_warning != MagickFalse)
10376             {
10377               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10378                  "  Image has transparency but tRNS chunk was excluded");
10379             }
10380
10381           if (mng_info->write_png_depth)
10382             {
10383               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10384                   "  Defined png:bit-depth=%u, Computed depth=%u",
10385                   mng_info->write_png_depth,
10386                   ping_bit_depth);
10387             }
10388
10389           if (mng_info->write_png_colortype)
10390             {
10391               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10392                   "  Defined png:color-type=%u, Computed color type=%u",
10393                   mng_info->write_png_colortype-1,
10394                   ping_color_type);
10395             }
10396         }
10397
10398       png_warning(ping,
10399         "Cannot write image with defined png:bit-depth or png:color-type.");
10400     }
10401
10402   if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10403     {
10404       /* Add an opaque matte channel */
10405       image->alpha_trait = BlendPixelTrait;
10406       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10407
10408       if (logging != MagickFalse)
10409         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10410           "  Added an opaque matte channel");
10411     }
10412
10413   if (number_transparent != 0 || number_semitransparent != 0)
10414     {
10415       if (ping_color_type < 4)
10416         {
10417            ping_have_tRNS=MagickTrue;
10418            if (logging != MagickFalse)
10419              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10420                "  Setting ping_have_tRNS=MagickTrue.");
10421         }
10422     }
10423
10424   if (logging != MagickFalse)
10425     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10426       "  Writing PNG header chunks");
10427
10428   png_set_IHDR(ping,ping_info,ping_width,ping_height,
10429                ping_bit_depth,ping_color_type,
10430                ping_interlace_method,ping_compression_method,
10431                ping_filter_method);
10432
10433   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10434     {
10435       png_set_PLTE(ping,ping_info,palette,number_colors);
10436
10437       if (logging != MagickFalse)
10438         {
10439           for (i=0; i< (ssize_t) number_colors; i++)
10440           {
10441             if (i < ping_num_trans)
10442               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10443                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10444                       (int) i,
10445                       (int) palette[i].red,
10446                       (int) palette[i].green,
10447                       (int) palette[i].blue,
10448                       (int) i,
10449                       (int) ping_trans_alpha[i]);
10450              else
10451               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10452                 "     PLTE[%d] = (%d,%d,%d)",
10453                       (int) i,
10454                       (int) palette[i].red,
10455                       (int) palette[i].green,
10456                       (int) palette[i].blue);
10457            }
10458          }
10459     }
10460
10461   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10462   if (ping_exclude_sRGB != MagickFalse ||
10463      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10464   {
10465     if ((ping_exclude_tEXt == MagickFalse ||
10466        ping_exclude_zTXt == MagickFalse) &&
10467        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10468     {
10469       ResetImageProfileIterator(image);
10470       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10471       {
10472         profile=GetImageProfile(image,name);
10473
10474         if (profile != (StringInfo *) NULL)
10475           {
10476 #ifdef PNG_WRITE_iCCP_SUPPORTED
10477             if ((LocaleCompare(name,"ICC") == 0) ||
10478                 (LocaleCompare(name,"ICM") == 0))
10479              {
10480
10481                if (ping_exclude_iCCP == MagickFalse)
10482                  {
10483                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10484                           "  Setting up iCCP chunk");
10485
10486                        png_set_iCCP(ping,ping_info,(png_charp) name,0,
10487 #if (PNG_LIBPNG_VER < 10500)
10488                          (png_charp) GetStringInfoDatum(profile),
10489 #else
10490                          (png_const_bytep) GetStringInfoDatum(profile),
10491 #endif
10492                          (png_uint_32) GetStringInfoLength(profile));
10493                        ping_have_iCCP = MagickTrue;
10494                  }
10495              }
10496
10497             else
10498 #endif
10499               if (ping_exclude_zCCP == MagickFalse)
10500                 {
10501                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10502                       "  Setting up zTXT chunk with uuencoded ICC");
10503                   Magick_png_write_raw_profile(image_info,ping,ping_info,
10504                     (unsigned char *) name,(unsigned char *) name,
10505                     GetStringInfoDatum(profile),
10506                     (png_uint_32) GetStringInfoLength(profile));
10507                   ping_have_iCCP = MagickTrue;
10508                 }
10509           }
10510
10511           if (logging != MagickFalse)
10512             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10513               "  Setting up text chunk with %s profile",name);
10514
10515         name=GetNextImageProfile(image);
10516       }
10517     }
10518   }
10519
10520 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10521   if ((mng_info->have_write_global_srgb == 0) &&
10522       ping_have_iCCP != MagickTrue &&
10523       (ping_have_sRGB != MagickFalse ||
10524       png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10525     {
10526       if (ping_exclude_sRGB == MagickFalse)
10527         {
10528           /*
10529             Note image rendering intent.
10530           */
10531           if (logging != MagickFalse)
10532             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10533                 "  Setting up sRGB chunk");
10534
10535           (void) png_set_sRGB(ping,ping_info,(
10536             Magick_RenderingIntent_to_PNG_RenderingIntent(
10537               image->rendering_intent)));
10538
10539           ping_have_sRGB = MagickTrue;
10540         }
10541     }
10542
10543   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10544 #endif
10545     {
10546       if (ping_exclude_gAMA == MagickFalse &&
10547           ping_have_iCCP == MagickFalse &&
10548           ping_have_sRGB == MagickFalse &&
10549           (ping_exclude_sRGB == MagickFalse ||
10550           (image->gamma < .45 || image->gamma > .46)))
10551       {
10552       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10553         {
10554           /*
10555             Note image gamma.
10556             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10557           */
10558           if (logging != MagickFalse)
10559             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10560               "  Setting up gAMA chunk");
10561
10562           png_set_gAMA(ping,ping_info,image->gamma);
10563         }
10564       }
10565
10566       if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10567         {
10568           if ((mng_info->have_write_global_chrm == 0) &&
10569               (image->chromaticity.red_primary.x != 0.0))
10570             {
10571               /*
10572                 Note image chromaticity.
10573                 Note: if cHRM+gAMA == sRGB write sRGB instead.
10574               */
10575                PrimaryInfo
10576                  bp,
10577                  gp,
10578                  rp,
10579                  wp;
10580
10581                wp=image->chromaticity.white_point;
10582                rp=image->chromaticity.red_primary;
10583                gp=image->chromaticity.green_primary;
10584                bp=image->chromaticity.blue_primary;
10585
10586                if (logging != MagickFalse)
10587                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10588                    "  Setting up cHRM chunk");
10589
10590                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10591                    bp.x,bp.y);
10592            }
10593         }
10594     }
10595
10596   if (ping_exclude_bKGD == MagickFalse)
10597     {
10598       if (ping_have_bKGD != MagickFalse)
10599         {
10600           png_set_bKGD(ping,ping_info,&ping_background);
10601           if (logging != MagickFalse)
10602             {
10603               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10604                    "    Setting up bKGD chunk");
10605               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10606                    "      background color = (%d,%d,%d)",
10607                         (int) ping_background.red,
10608                         (int) ping_background.green,
10609                         (int) ping_background.blue);
10610               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10611                    "      index = %d, gray=%d",
10612                         (int) ping_background.index,
10613                         (int) ping_background.gray);
10614             }
10615          }
10616     }
10617
10618   if (ping_exclude_pHYs == MagickFalse)
10619     {
10620       if (ping_have_pHYs != MagickFalse)
10621         {
10622           png_set_pHYs(ping,ping_info,
10623              ping_pHYs_x_resolution,
10624              ping_pHYs_y_resolution,
10625              ping_pHYs_unit_type);
10626
10627           if (logging != MagickFalse)
10628             {
10629               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10630                    "    Setting up pHYs chunk");
10631               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10632                    "      x_resolution=%lu",
10633                    (unsigned long) ping_pHYs_x_resolution);
10634               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10635                    "      y_resolution=%lu",
10636                    (unsigned long) ping_pHYs_y_resolution);
10637               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10638                    "      unit_type=%lu",
10639                    (unsigned long) ping_pHYs_unit_type);
10640             }
10641         }
10642     }
10643
10644 #if defined(PNG_oFFs_SUPPORTED)
10645   if (ping_exclude_oFFs == MagickFalse)
10646     {
10647       if (image->page.x || image->page.y)
10648         {
10649            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10650               (png_int_32) image->page.y, 0);
10651
10652            if (logging != MagickFalse)
10653              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10654                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
10655                  (int) image->page.x, (int) image->page.y);
10656         }
10657     }
10658 #endif
10659
10660 #if defined(PNG_tIME_SUPPORTED)
10661   if (ping_exclude_tIME == MagickFalse)
10662     {
10663       const char
10664         *timestamp;
10665
10666       if (image->taint == MagickFalse)
10667         {
10668           timestamp=GetImageOption(image_info,"png:tIME");
10669
10670           if (timestamp == (const char *) NULL)
10671             timestamp=GetImageProperty(image,"png:tIME",exception);
10672         }
10673
10674       else
10675         {
10676           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10677              "  Reset tIME in tainted image");
10678
10679           timestamp=GetImageProperty(image,"date:modify",exception);
10680         }
10681
10682       if (timestamp != (const char *) NULL)
10683           write_tIME_chunk(image,ping,ping_info,timestamp,exception);
10684     }
10685 #endif
10686
10687   if (mng_info->need_blob != MagickFalse)
10688   {
10689     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10690        MagickFalse)
10691        png_error(ping,"WriteBlob Failed");
10692
10693      ping_have_blob=MagickTrue;
10694   }
10695
10696   png_write_info_before_PLTE(ping, ping_info);
10697
10698   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10699     {
10700       if (logging != MagickFalse)
10701         {
10702           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10703               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10704         }
10705
10706       if (ping_color_type == 3)
10707          (void) png_set_tRNS(ping, ping_info,
10708                 ping_trans_alpha,
10709                 ping_num_trans,
10710                 NULL);
10711
10712       else
10713         {
10714            (void) png_set_tRNS(ping, ping_info,
10715                   NULL,
10716                   0,
10717                   &ping_trans_color);
10718
10719            if (logging != MagickFalse)
10720              {
10721                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10722                  "     tRNS color   =(%d,%d,%d)",
10723                        (int) ping_trans_color.red,
10724                        (int) ping_trans_color.green,
10725                        (int) ping_trans_color.blue);
10726              }
10727          }
10728     }
10729
10730   /* write any png-chunk-b profiles */
10731   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10732
10733   png_write_info(ping,ping_info);
10734
10735   /* write any PNG-chunk-m profiles */
10736   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10737
10738   if (ping_exclude_vpAg == MagickFalse)
10739     {
10740       if ((image->page.width != 0 && image->page.width != image->columns) ||
10741           (image->page.height != 0 && image->page.height != image->rows))
10742         {
10743           unsigned char
10744             chunk[14];
10745
10746           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
10747           PNGType(chunk,mng_vpAg);
10748           LogPNGChunk(logging,mng_vpAg,9L);
10749           PNGLong(chunk+4,(png_uint_32) image->page.width);
10750           PNGLong(chunk+8,(png_uint_32) image->page.height);
10751           chunk[12]=0;   /* unit = pixels */
10752           (void) WriteBlob(image,13,chunk);
10753           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10754         }
10755     }
10756
10757 #if (PNG_LIBPNG_VER == 10206)
10758     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10759 #define PNG_HAVE_IDAT               0x04
10760     ping->mode |= PNG_HAVE_IDAT;
10761 #undef PNG_HAVE_IDAT
10762 #endif
10763
10764   png_set_packing(ping);
10765   /*
10766     Allocate memory.
10767   */
10768   rowbytes=image->columns;
10769   if (image_depth > 8)
10770     rowbytes*=2;
10771   switch (ping_color_type)
10772     {
10773       case PNG_COLOR_TYPE_RGB:
10774         rowbytes*=3;
10775         break;
10776
10777       case PNG_COLOR_TYPE_GRAY_ALPHA:
10778         rowbytes*=2;
10779         break;
10780
10781       case PNG_COLOR_TYPE_RGBA:
10782         rowbytes*=4;
10783         break;
10784
10785       default:
10786         break;
10787     }
10788
10789   if (logging != MagickFalse)
10790     {
10791       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10792         "  Writing PNG image data");
10793
10794       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10795         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10796     }
10797   pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10798   if (pixel_info == (MemoryInfo *) NULL)
10799     png_error(ping,"Allocation of memory for pixels failed");
10800   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
10801
10802   /*
10803     Initialize image scanlines.
10804   */
10805   quantum_info=AcquireQuantumInfo(image_info,image);
10806   if (quantum_info == (QuantumInfo *) NULL)
10807     png_error(ping,"Memory allocation for quantum_info failed");
10808   quantum_info->format=UndefinedQuantumFormat;
10809   quantum_info->depth=image_depth;
10810   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10811   num_passes=png_set_interlace_handling(ping);
10812
10813   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10814        !mng_info->write_png48 && !mng_info->write_png64 &&
10815        !mng_info->write_png32) &&
10816        (mng_info->IsPalette ||
10817        (image_info->type == BilevelType)) &&
10818        image_matte == MagickFalse &&
10819        ping_have_non_bw == MagickFalse)
10820     {
10821       /* Palette, Bilevel, or Opaque Monochrome */
10822       register const Quantum
10823         *p;
10824
10825       quantum_info->depth=8;
10826       for (pass=0; pass < num_passes; pass++)
10827       {
10828         /*
10829           Convert PseudoClass image to a PNG monochrome image.
10830         */
10831         for (y=0; y < (ssize_t) image->rows; y++)
10832         {
10833           if (logging != MagickFalse && y == 0)
10834              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10835                  "    Writing row of pixels (0)");
10836
10837           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10838
10839           if (p == (const Quantum *) NULL)
10840             break;
10841
10842           if (mng_info->IsPalette)
10843             {
10844               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10845                 quantum_info,GrayQuantum,ping_pixels,exception);
10846               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10847                   mng_info->write_png_depth &&
10848                   mng_info->write_png_depth != old_bit_depth)
10849                 {
10850                   /* Undo pixel scaling */
10851                   for (i=0; i < (ssize_t) image->columns; i++)
10852                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10853                      >> (8-old_bit_depth));
10854                 }
10855             }
10856
10857           else
10858             {
10859               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10860                 quantum_info,RedQuantum,ping_pixels,exception);
10861             }
10862
10863           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10864             for (i=0; i < (ssize_t) image->columns; i++)
10865                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10866                       255 : 0);
10867
10868           if (logging != MagickFalse && y == 0)
10869             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10870                 "    Writing row of pixels (1)");
10871
10872           png_write_row(ping,ping_pixels);
10873
10874           status=SetImageProgress(image,LoadImageTag,
10875               (MagickOffsetType) (pass * image->rows + y),
10876               num_passes * image->rows);
10877
10878           if (status == MagickFalse)
10879             break;
10880         }
10881       }
10882     }
10883
10884   else   /* Not Palette, Bilevel, or Opaque Monochrome */
10885     {
10886       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10887           !mng_info->write_png48 && !mng_info->write_png64 &&
10888           !mng_info->write_png32) && (image_matte != MagickFalse ||
10889           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10890           (mng_info->IsPalette) && ping_have_color == MagickFalse)
10891         {
10892           register const Quantum
10893             *p;
10894
10895           for (pass=0; pass < num_passes; pass++)
10896           {
10897
10898           for (y=0; y < (ssize_t) image->rows; y++)
10899           {
10900             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10901
10902             if (p == (const Quantum *) NULL)
10903               break;
10904
10905             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10906               {
10907                 if (mng_info->IsPalette)
10908                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10909                     quantum_info,GrayQuantum,ping_pixels,exception);
10910
10911                 else
10912                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10913                     quantum_info,RedQuantum,ping_pixels,exception);
10914
10915                 if (logging != MagickFalse && y == 0)
10916                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10917                        "    Writing GRAY PNG pixels (2)");
10918               }
10919
10920             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10921               {
10922                 if (logging != MagickFalse && y == 0)
10923                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10924                          "    Writing GRAY_ALPHA PNG pixels (2)");
10925
10926                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10927                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10928               }
10929
10930             if (logging != MagickFalse && y == 0)
10931               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10932                   "    Writing row of pixels (2)");
10933
10934             png_write_row(ping,ping_pixels);
10935
10936             status=SetImageProgress(image,LoadImageTag,
10937               (MagickOffsetType) (pass * image->rows + y),
10938               num_passes * image->rows);
10939
10940             if (status == MagickFalse)
10941               break;
10942             }
10943           }
10944         }
10945
10946       else
10947         {
10948           register const Quantum
10949             *p;
10950
10951           for (pass=0; pass < num_passes; pass++)
10952           {
10953             if ((image_depth > 8) ||
10954                 mng_info->write_png24 ||
10955                 mng_info->write_png32 ||
10956                 mng_info->write_png48 ||
10957                 mng_info->write_png64 ||
10958                 (!mng_info->write_png8 && !mng_info->IsPalette))
10959             {
10960               for (y=0; y < (ssize_t) image->rows; y++)
10961               {
10962                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10963
10964                 if (p == (const Quantum *) NULL)
10965                   break;
10966
10967                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10968                   {
10969                     if (image->storage_class == DirectClass)
10970                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
10971                         quantum_info,RedQuantum,ping_pixels,exception);
10972
10973                     else
10974                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
10975                         quantum_info,GrayQuantum,ping_pixels,exception);
10976                   }
10977
10978                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10979                   {
10980                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
10981                       quantum_info,GrayAlphaQuantum,ping_pixels,
10982                       exception);
10983
10984                     if (logging != MagickFalse && y == 0)
10985                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10986                            "    Writing GRAY_ALPHA PNG pixels (3)");
10987                   }
10988
10989                 else if (image_matte != MagickFalse)
10990                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10991                     quantum_info,RGBAQuantum,ping_pixels,exception);
10992
10993                 else
10994                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10995                     quantum_info,RGBQuantum,ping_pixels,exception);
10996
10997                 if (logging != MagickFalse && y == 0)
10998                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10999                       "    Writing row of pixels (3)");
11000
11001                 png_write_row(ping,ping_pixels);
11002
11003                 status=SetImageProgress(image,LoadImageTag,
11004                   (MagickOffsetType) (pass * image->rows + y),
11005                   num_passes * image->rows);
11006
11007                 if (status == MagickFalse)
11008                   break;
11009               }
11010             }
11011
11012           else
11013             /* not ((image_depth > 8) ||
11014                 mng_info->write_png24 || mng_info->write_png32 ||
11015                 mng_info->write_png48 || mng_info->write_png64 ||
11016                 (!mng_info->write_png8 && !mng_info->IsPalette))
11017              */
11018             {
11019               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11020                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11021                 {
11022                   if (logging != MagickFalse)
11023                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11024                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11025
11026                   quantum_info->depth=8;
11027                   image_depth=8;
11028                 }
11029
11030               for (y=0; y < (ssize_t) image->rows; y++)
11031               {
11032                 if (logging != MagickFalse && y == 0)
11033                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11034                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
11035
11036                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11037
11038                 if (p == (const Quantum *) NULL)
11039                   break;
11040
11041                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11042                   {
11043                     quantum_info->depth=image->depth;
11044
11045                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11046                        quantum_info,GrayQuantum,ping_pixels,exception);
11047                   }
11048
11049                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11050                   {
11051                     if (logging != MagickFalse && y == 0)
11052                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11053                            "  Writing GRAY_ALPHA PNG pixels (4)");
11054
11055                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11056                          quantum_info,GrayAlphaQuantum,ping_pixels,
11057                          exception);
11058                   }
11059
11060                 else
11061                   {
11062                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11063                       quantum_info,IndexQuantum,ping_pixels,exception);
11064
11065                     if (logging != MagickFalse && y <= 2)
11066                     {
11067                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11068                           "  Writing row of non-gray pixels (4)");
11069
11070                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11071                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
11072                           (int)ping_pixels[0],(int)ping_pixels[1]);
11073                     }
11074                   }
11075                 png_write_row(ping,ping_pixels);
11076
11077                 status=SetImageProgress(image,LoadImageTag,
11078                   (MagickOffsetType) (pass * image->rows + y),
11079                   num_passes * image->rows);
11080
11081                 if (status == MagickFalse)
11082                   break;
11083               }
11084             }
11085           }
11086         }
11087     }
11088
11089   if (quantum_info != (QuantumInfo *) NULL)
11090     quantum_info=DestroyQuantumInfo(quantum_info);
11091
11092   if (logging != MagickFalse)
11093     {
11094       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11095         "  Wrote PNG image data");
11096
11097       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11098         "    Width: %.20g",(double) ping_width);
11099
11100       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11101         "    Height: %.20g",(double) ping_height);
11102
11103       if (mng_info->write_png_depth)
11104         {
11105           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11106             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
11107         }
11108
11109       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11110         "    PNG bit-depth written: %d",ping_bit_depth);
11111
11112       if (mng_info->write_png_colortype)
11113         {
11114           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11115             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
11116         }
11117
11118       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11119         "    PNG color-type written: %d",ping_color_type);
11120
11121       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11122         "    PNG Interlace method: %d",ping_interlace_method);
11123     }
11124   /*
11125     Generate text chunks after IDAT.
11126   */
11127   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11128   {
11129     ResetImagePropertyIterator(image);
11130     property=GetNextImageProperty(image);
11131     while (property != (const char *) NULL)
11132     {
11133       png_textp
11134         text;
11135
11136       value=GetImageProperty(image,property,exception);
11137
11138       /* Don't write any "png:" or "jpeg:" properties; those are just for
11139        * "identify" or for passing through to another JPEG
11140        */
11141       if ((LocaleNCompare(property,"png:",4) != 0 &&
11142            LocaleNCompare(property,"jpeg:",5) != 0) &&
11143
11144
11145           /* Suppress density and units if we wrote a pHYs chunk */
11146           (ping_exclude_pHYs != MagickFalse      ||
11147           LocaleCompare(property,"density") != 0 ||
11148           LocaleCompare(property,"units") != 0) &&
11149
11150           /* Suppress the IM-generated Date:create and Date:modify */
11151           (ping_exclude_date == MagickFalse      ||
11152           LocaleNCompare(property, "Date:",5) != 0))
11153         {
11154         if (value != (const char *) NULL)
11155           {
11156
11157 #if PNG_LIBPNG_VER >= 10400
11158             text=(png_textp) png_malloc(ping,
11159                  (png_alloc_size_t) sizeof(png_text));
11160 #else
11161             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11162 #endif
11163             text[0].key=(char *) property;
11164             text[0].text=(char *) value;
11165             text[0].text_length=strlen(value);
11166
11167             if (ping_exclude_tEXt != MagickFalse)
11168                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11169
11170             else if (ping_exclude_zTXt != MagickFalse)
11171                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11172
11173             else
11174             {
11175                text[0].compression=image_info->compression == NoCompression ||
11176                  (image_info->compression == UndefinedCompression &&
11177                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11178                  PNG_TEXT_COMPRESSION_zTXt ;
11179             }
11180
11181             if (logging != MagickFalse)
11182               {
11183                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11184                   "  Setting up text chunk");
11185
11186                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11187                   "    keyword: '%s'",text[0].key);
11188               }
11189
11190             png_set_text(ping,ping_info,text,1);
11191             png_free(ping,text);
11192           }
11193         }
11194       property=GetNextImageProperty(image);
11195     }
11196   }
11197
11198   /* write any PNG-chunk-e profiles */
11199   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11200
11201   if (logging != MagickFalse)
11202     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11203       "  Writing PNG end info");
11204
11205   png_write_end(ping,ping_info);
11206
11207   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11208     {
11209       if (mng_info->page.x || mng_info->page.y ||
11210           (ping_width != mng_info->page.width) ||
11211           (ping_height != mng_info->page.height))
11212         {
11213           unsigned char
11214             chunk[32];
11215
11216           /*
11217             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11218           */
11219           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11220           PNGType(chunk,mng_FRAM);
11221           LogPNGChunk(logging,mng_FRAM,27L);
11222           chunk[4]=4;
11223           chunk[5]=0;  /* frame name separator (no name) */
11224           chunk[6]=1;  /* flag for changing delay, for next frame only */
11225           chunk[7]=0;  /* flag for changing frame timeout */
11226           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11227           chunk[9]=0;  /* flag for changing frame sync_id */
11228           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11229           chunk[14]=0; /* clipping boundaries delta type */
11230           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11231           PNGLong(chunk+19,
11232              (png_uint_32) (mng_info->page.x + ping_width));
11233           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11234           PNGLong(chunk+27,
11235              (png_uint_32) (mng_info->page.y + ping_height));
11236           (void) WriteBlob(image,31,chunk);
11237           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11238           mng_info->old_framing_mode=4;
11239           mng_info->framing_mode=1;
11240         }
11241
11242       else
11243         mng_info->framing_mode=3;
11244     }
11245   if (mng_info->write_mng && !mng_info->need_fram &&
11246       ((int) image->dispose == 3))
11247      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11248
11249   /*
11250     Free PNG resources.
11251   */
11252
11253   png_destroy_write_struct(&ping,&ping_info);
11254
11255   pixel_info=RelinquishVirtualMemory(pixel_info);
11256
11257   if (ping_have_blob != MagickFalse)
11258      (void) CloseBlob(image);
11259
11260   image_info=DestroyImageInfo(image_info);
11261   image=DestroyImage(image);
11262
11263   /* Store bit depth actually written */
11264   s[0]=(char) ping_bit_depth;
11265   s[1]='\0';
11266
11267   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11268
11269   if (logging != MagickFalse)
11270     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11271       "  exit WriteOnePNGImage()");
11272
11273 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11274   UnlockSemaphoreInfo(ping_semaphore);
11275 #endif
11276
11277    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11278     *    Throwing an Exception when an error occurs.
11279     */
11280
11281   return(MagickTrue);
11282 /*  End write one PNG image */
11283
11284 }
11285
11286 /*
11287 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11288 %                                                                             %
11289 %                                                                             %
11290 %                                                                             %
11291 %   W r i t e P N G I m a g e                                                 %
11292 %                                                                             %
11293 %                                                                             %
11294 %                                                                             %
11295 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11296 %
11297 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11298 %  Multiple-image Network Graphics (MNG) image file.
11299 %
11300 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11301 %
11302 %  The format of the WritePNGImage method is:
11303 %
11304 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11305 %        Image *image,ExceptionInfo *exception)
11306 %
11307 %  A description of each parameter follows:
11308 %
11309 %    o image_info: the image info.
11310 %
11311 %    o image:  The image.
11312 %
11313 %    o exception: return any errors or warnings in this structure.
11314 %
11315 %  Returns MagickTrue on success, MagickFalse on failure.
11316 %
11317 %  Communicating with the PNG encoder:
11318 %
11319 %  While the datastream written is always in PNG format and normally would
11320 %  be given the "png" file extension, this method also writes the following
11321 %  pseudo-formats which are subsets of png:
11322 %
11323 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11324 %               a depth greater than 8, the depth is reduced. If transparency
11325 %               is present, the tRNS chunk must only have values 0 and 255
11326 %               (i.e., transparency is binary: fully opaque or fully
11327 %               transparent).  If other values are present they will be
11328 %               50%-thresholded to binary transparency.  If more than 256
11329 %               colors are present, they will be quantized to the 4-4-4-1,
11330 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11331 %               of any resulting fully-transparent pixels is changed to
11332 %               the image's background color.
11333 %
11334 %               If you want better quantization or dithering of the colors
11335 %               or alpha than that, you need to do it before calling the
11336 %               PNG encoder. The pixels contain 8-bit indices even if
11337 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11338 %               images will be written as indexed PNG files even though the
11339 %               PNG grayscale type might be slightly more efficient.  Please
11340 %               note that writing to the PNG8 format may result in loss
11341 %               of color and alpha data.
11342 %
11343 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11344 %               chunk can be present to convey binary transparency by naming
11345 %               one of the colors as transparent.  The only loss incurred
11346 %               is reduction of sample depth to 8.  If the image has more
11347 %               than one transparent color, has semitransparent pixels, or
11348 %               has an opaque pixel with the same RGB components as the
11349 %               transparent color, an image is not written.
11350 %
11351 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
11352 %               transparency is permitted, i.e., the alpha sample for
11353 %               each pixel can have any value from 0 to 255. The alpha
11354 %               channel is present even if the image is fully opaque.
11355 %               The only loss in data is the reduction of the sample depth
11356 %               to 8.
11357 %
11358 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
11359 %               chunk can be present to convey binary transparency by naming
11360 %               one of the colors as transparent.  If the image has more
11361 %               than one transparent color, has semitransparent pixels, or
11362 %               has an opaque pixel with the same RGB components as the
11363 %               transparent color, an image is not written.
11364 %
11365 %    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
11366 %               transparency is permitted, i.e., the alpha sample for
11367 %               each pixel can have any value from 0 to 65535. The alpha
11368 %               channel is present even if the image is fully opaque.
11369 %
11370 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
11371 %               image, if the input was a PNG, is written.  If these values
11372 %               cannot be found, then "PNG00" falls back to the regular "PNG"
11373 %               format.
11374 %
11375 %    o -define: For more precise control of the PNG output, you can use the
11376 %               Image options "png:bit-depth" and "png:color-type".  These
11377 %               can be set from the commandline with "-define" and also
11378 %               from the application programming interfaces.  The options
11379 %               are case-independent and are converted to lowercase before
11380 %               being passed to this encoder.
11381 %
11382 %               png:color-type can be 0, 2, 3, 4, or 6.
11383 %
11384 %               When png:color-type is 0 (Grayscale), png:bit-depth can
11385 %               be 1, 2, 4, 8, or 16.
11386 %
11387 %               When png:color-type is 2 (RGB), png:bit-depth can
11388 %               be 8 or 16.
11389 %
11390 %               When png:color-type is 3 (Indexed), png:bit-depth can
11391 %               be 1, 2, 4, or 8.  This refers to the number of bits
11392 %               used to store the index.  The color samples always have
11393 %               bit-depth 8 in indexed PNG files.
11394 %
11395 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11396 %               png:bit-depth can be 8 or 16.
11397 %
11398 %               If the image cannot be written without loss with the
11399 %               requested bit-depth and color-type, a PNG file will not
11400 %               be written, a warning will be issued, and the encoder will
11401 %               return MagickFalse.
11402 %
11403 %  Since image encoders should not be responsible for the "heavy lifting",
11404 %  the user should make sure that ImageMagick has already reduced the
11405 %  image depth and number of colors and limit transparency to binary
11406 %  transparency prior to attempting to write the image with depth, color,
11407 %  or transparency limitations.
11408 %
11409 %  Note that another definition, "png:bit-depth-written" exists, but it
11410 %  is not intended for external use.  It is only used internally by the
11411 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11412 %
11413 %  It is possible to request that the PNG encoder write previously-formatted
11414 %  ancillary chunks in the output PNG file, using the "-profile" commandline
11415 %  option as shown below or by setting the profile via a programming
11416 %  interface:
11417 %
11418 %     -profile PNG-chunk-x:<file>
11419 %
11420 %  where x is a location flag and <file> is a file containing the chunk
11421 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11422 %  This encoder will compute the chunk length and CRC, so those must not
11423 %  be included in the file.
11424 %
11425 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11426 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
11427 %  of the same type, then add a short unique string after the "x" to prevent
11428 %  subsequent profiles from overwriting the preceding ones, e.g.,
11429 %
11430 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11431 %
11432 %  As of version 6.6.6 the following optimizations are always done:
11433 %
11434 %   o  32-bit depth is reduced to 16.
11435 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
11436 %      high byte and low byte are identical.
11437 %   o  Palette is sorted to remove unused entries and to put a
11438 %      transparent color first, if BUILD_PNG_PALETTE is defined.
11439 %   o  Opaque matte channel is removed when writing an indexed PNG.
11440 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
11441 %      this can be done without loss and a larger bit depth N was not
11442 %      requested via the "-define png:bit-depth=N" option.
11443 %   o  If matte channel is present but only one transparent color is
11444 %      present, RGB+tRNS is written instead of RGBA
11445 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
11446 %      was requested when converting an opaque image).
11447 %
11448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11449 */
11450 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11451   Image *image,ExceptionInfo *exception)
11452 {
11453   MagickBooleanType
11454     excluding,
11455     logging,
11456     have_mng_structure,
11457     status;
11458
11459   MngInfo
11460     *mng_info;
11461
11462   const char
11463     *value;
11464
11465   int
11466     source;
11467
11468   /*
11469     Open image file.
11470   */
11471   assert(image_info != (const ImageInfo *) NULL);
11472   assert(image_info->signature == MagickSignature);
11473   assert(image != (Image *) NULL);
11474   assert(image->signature == MagickSignature);
11475   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11476   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11477   /*
11478     Allocate a MngInfo structure.
11479   */
11480   have_mng_structure=MagickFalse;
11481   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11482
11483   if (mng_info == (MngInfo *) NULL)
11484     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11485
11486   /*
11487     Initialize members of the MngInfo structure.
11488   */
11489   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11490   mng_info->image=image;
11491   mng_info->equal_backgrounds=MagickTrue;
11492   have_mng_structure=MagickTrue;
11493
11494   /* See if user has requested a specific PNG subformat */
11495
11496   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11497   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11498   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11499   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11500   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11501
11502   value=GetImageOption(image_info,"png:format");
11503   if (value == (char *) NULL)
11504     if (LocaleCompare(image_info->magick,"PNG00") == 0)
11505
11506   if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
11507     {
11508       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11509          "  Format=%s",value);
11510
11511       mng_info->write_png8 = MagickFalse;
11512       mng_info->write_png24 = MagickFalse;
11513       mng_info->write_png32 = MagickFalse;
11514       mng_info->write_png48 = MagickFalse;
11515       mng_info->write_png64 = MagickFalse;
11516
11517       if (LocaleCompare(value,"png8") == 0)
11518         mng_info->write_png8 = MagickTrue;
11519
11520       else if (LocaleCompare(value,"png24") == 0)
11521         mng_info->write_png24 = MagickTrue;
11522
11523       else if (LocaleCompare(value,"png32") == 0)
11524         mng_info->write_png32 = MagickTrue;
11525
11526       else if (LocaleCompare(value,"png48") == 0)
11527         mng_info->write_png48 = MagickTrue;
11528
11529       else if (LocaleCompare(value,"png64") == 0)
11530         mng_info->write_png64 = MagickTrue;
11531
11532       else if ((LocaleCompare(value,"png00") == 0) ||
11533          LocaleCompare(image_info->magick,"PNG00") == 0)
11534         {
11535           /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11536           value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11537
11538           if (value != (char *) NULL)
11539             {
11540               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11541                  "  png00 inherited bit depth=%s",value);
11542
11543               if (LocaleCompare(value,"1") == 0)
11544                 mng_info->write_png_depth = 1;
11545
11546               else if (LocaleCompare(value,"2") == 0)
11547                 mng_info->write_png_depth = 2;
11548
11549               else if (LocaleCompare(value,"4") == 0)
11550                 mng_info->write_png_depth = 4;
11551
11552               else if (LocaleCompare(value,"8") == 0)
11553                 mng_info->write_png_depth = 8;
11554
11555               else if (LocaleCompare(value,"16") == 0)
11556                 mng_info->write_png_depth = 16;
11557             }
11558
11559           value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11560
11561           if (value != (char *) NULL)
11562             {
11563               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11564                  "  png00 inherited color type=%s",value);
11565
11566               if (LocaleCompare(value,"0") == 0)
11567                 mng_info->write_png_colortype = 1;
11568
11569               else if (LocaleCompare(value,"2") == 0)
11570                 mng_info->write_png_colortype = 3;
11571
11572               else if (LocaleCompare(value,"3") == 0)
11573                 mng_info->write_png_colortype = 4;
11574
11575               else if (LocaleCompare(value,"4") == 0)
11576                 mng_info->write_png_colortype = 5;
11577
11578               else if (LocaleCompare(value,"6") == 0)
11579                 mng_info->write_png_colortype = 7;
11580             }
11581         }
11582     }
11583
11584   if (mng_info->write_png8)
11585     {
11586       mng_info->write_png_colortype = /* 3 */ 4;
11587       mng_info->write_png_depth = 8;
11588       image->depth = 8;
11589     }
11590
11591   if (mng_info->write_png24)
11592     {
11593       mng_info->write_png_colortype = /* 2 */ 3;
11594       mng_info->write_png_depth = 8;
11595       image->depth = 8;
11596
11597       if (image->alpha_trait != UndefinedPixelTrait)
11598         (void) SetImageType(image,TrueColorAlphaType,exception);
11599
11600       else
11601         (void) SetImageType(image,TrueColorType,exception);
11602
11603       (void) SyncImage(image,exception);
11604     }
11605
11606   if (mng_info->write_png32)
11607     {
11608       mng_info->write_png_colortype = /* 6 */  7;
11609       mng_info->write_png_depth = 8;
11610       image->depth = 8;
11611       image->alpha_trait = BlendPixelTrait;
11612
11613       (void) SetImageType(image,TrueColorAlphaType,exception);
11614       (void) SyncImage(image,exception);
11615     }
11616
11617   if (mng_info->write_png48)
11618     {
11619       mng_info->write_png_colortype = /* 2 */ 3;
11620       mng_info->write_png_depth = 16;
11621       image->depth = 16;
11622
11623       if (image->alpha_trait != UndefinedPixelTrait)
11624         (void) SetImageType(image,TrueColorAlphaType,exception);
11625
11626       else
11627         (void) SetImageType(image,TrueColorType,exception);
11628
11629       (void) SyncImage(image,exception);
11630     }
11631
11632   if (mng_info->write_png64)
11633     {
11634       mng_info->write_png_colortype = /* 6 */  7;
11635       mng_info->write_png_depth = 16;
11636       image->depth = 16;
11637       image->alpha_trait = BlendPixelTrait;
11638
11639       (void) SetImageType(image,TrueColorAlphaType,exception);
11640       (void) SyncImage(image,exception);
11641     }
11642
11643   value=GetImageOption(image_info,"png:bit-depth");
11644
11645   if (value != (char *) NULL)
11646     {
11647       if (LocaleCompare(value,"1") == 0)
11648         mng_info->write_png_depth = 1;
11649
11650       else if (LocaleCompare(value,"2") == 0)
11651         mng_info->write_png_depth = 2;
11652
11653       else if (LocaleCompare(value,"4") == 0)
11654         mng_info->write_png_depth = 4;
11655
11656       else if (LocaleCompare(value,"8") == 0)
11657         mng_info->write_png_depth = 8;
11658
11659       else if (LocaleCompare(value,"16") == 0)
11660         mng_info->write_png_depth = 16;
11661
11662       else
11663         (void) ThrowMagickException(exception,
11664              GetMagickModule(),CoderWarning,
11665              "ignoring invalid defined png:bit-depth",
11666              "=%s",value);
11667
11668       if (logging != MagickFalse)
11669         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11670           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11671     }
11672
11673   value=GetImageOption(image_info,"png:color-type");
11674
11675   if (value != (char *) NULL)
11676     {
11677       /* We must store colortype+1 because 0 is a valid colortype */
11678       if (LocaleCompare(value,"0") == 0)
11679         mng_info->write_png_colortype = 1;
11680
11681       else if (LocaleCompare(value,"1") == 0)
11682         mng_info->write_png_colortype = 2;
11683
11684       else if (LocaleCompare(value,"2") == 0)
11685         mng_info->write_png_colortype = 3;
11686
11687       else if (LocaleCompare(value,"3") == 0)
11688         mng_info->write_png_colortype = 4;
11689
11690       else if (LocaleCompare(value,"4") == 0)
11691         mng_info->write_png_colortype = 5;
11692
11693       else if (LocaleCompare(value,"6") == 0)
11694         mng_info->write_png_colortype = 7;
11695
11696       else
11697         (void) ThrowMagickException(exception,
11698              GetMagickModule(),CoderWarning,
11699              "ignoring invalid defined png:color-type",
11700              "=%s",value);
11701
11702       if (logging != MagickFalse)
11703         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11704           "  png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11705     }
11706
11707   /* Check for chunks to be excluded:
11708    *
11709    * The default is to not exclude any known chunks except for any
11710    * listed in the "unused_chunks" array, above.
11711    *
11712    * Chunks can be listed for exclusion via a "png:exclude-chunk"
11713    * define (in the image properties or in the image artifacts)
11714    * or via a mng_info member.  For convenience, in addition
11715    * to or instead of a comma-separated list of chunks, the
11716    * "exclude-chunk" string can be simply "all" or "none".
11717    *
11718    * The exclude-chunk define takes priority over the mng_info.
11719    *
11720    * A "png:include-chunk" define takes  priority over both the
11721    * mng_info and the "png:exclude-chunk" define.  Like the
11722    * "exclude-chunk" string, it can define "all" or "none" as
11723    * well as a comma-separated list.  Chunks that are unknown to
11724    * ImageMagick are always excluded, regardless of their "copy-safe"
11725    * status according to the PNG specification, and even if they
11726    * appear in the "include-chunk" list. Such defines appearing among
11727    * the image options take priority over those found among the image
11728    * artifacts.
11729    *
11730    * Finally, all chunks listed in the "unused_chunks" array are
11731    * automatically excluded, regardless of the other instructions
11732    * or lack thereof.
11733    *
11734    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11735    * will not be written and the gAMA chunk will only be written if it
11736    * is not between .45 and .46, or approximately (1.0/2.2).
11737    *
11738    * If you exclude tRNS and the image has transparency, the colortype
11739    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11740    *
11741    * The -strip option causes StripImage() to set the png:include-chunk
11742    * artifact to "none,trns,gama".
11743    */
11744
11745   mng_info->ping_exclude_bKGD=MagickFalse;
11746   mng_info->ping_exclude_cHRM=MagickFalse;
11747   mng_info->ping_exclude_date=MagickFalse;
11748   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11749   mng_info->ping_exclude_gAMA=MagickFalse;
11750   mng_info->ping_exclude_iCCP=MagickFalse;
11751   /* mng_info->ping_exclude_iTXt=MagickFalse; */
11752   mng_info->ping_exclude_oFFs=MagickFalse;
11753   mng_info->ping_exclude_pHYs=MagickFalse;
11754   mng_info->ping_exclude_sRGB=MagickFalse;
11755   mng_info->ping_exclude_tEXt=MagickFalse;
11756   mng_info->ping_exclude_tIME=MagickFalse;
11757   mng_info->ping_exclude_tRNS=MagickFalse;
11758   mng_info->ping_exclude_vpAg=MagickFalse;
11759   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11760   mng_info->ping_exclude_zTXt=MagickFalse;
11761
11762   mng_info->ping_preserve_colormap=MagickFalse;
11763
11764   value=GetImageOption(image_info,"png:preserve-colormap");
11765   if (value == NULL)
11766      value=GetImageArtifact(image,"png:preserve-colormap");
11767   if (value != NULL)
11768      mng_info->ping_preserve_colormap=MagickTrue;
11769
11770   mng_info->ping_preserve_iCCP=MagickFalse;
11771
11772   value=GetImageOption(image_info,"png:preserve-iCCP");
11773   if (value == NULL)
11774      value=GetImageArtifact(image,"png:preserve-iCCP");
11775   if (value != NULL)
11776      mng_info->ping_preserve_iCCP=MagickTrue;
11777
11778   /* These compression-level, compression-strategy, and compression-filter
11779    * defines take precedence over values from the -quality option.
11780    */
11781   value=GetImageOption(image_info,"png:compression-level");
11782   if (value == NULL)
11783      value=GetImageArtifact(image,"png:compression-level");
11784   if (value != NULL)
11785   {
11786       /* We have to add 1 to everything because 0 is a valid input,
11787        * and we want to use 0 (the default) to mean undefined.
11788        */
11789       if (LocaleCompare(value,"0") == 0)
11790         mng_info->write_png_compression_level = 1;
11791
11792       else if (LocaleCompare(value,"1") == 0)
11793         mng_info->write_png_compression_level = 2;
11794
11795       else if (LocaleCompare(value,"2") == 0)
11796         mng_info->write_png_compression_level = 3;
11797
11798       else if (LocaleCompare(value,"3") == 0)
11799         mng_info->write_png_compression_level = 4;
11800
11801       else if (LocaleCompare(value,"4") == 0)
11802         mng_info->write_png_compression_level = 5;
11803
11804       else if (LocaleCompare(value,"5") == 0)
11805         mng_info->write_png_compression_level = 6;
11806
11807       else if (LocaleCompare(value,"6") == 0)
11808         mng_info->write_png_compression_level = 7;
11809
11810       else if (LocaleCompare(value,"7") == 0)
11811         mng_info->write_png_compression_level = 8;
11812
11813       else if (LocaleCompare(value,"8") == 0)
11814         mng_info->write_png_compression_level = 9;
11815
11816       else if (LocaleCompare(value,"9") == 0)
11817         mng_info->write_png_compression_level = 10;
11818
11819       else
11820         (void) ThrowMagickException(exception,
11821              GetMagickModule(),CoderWarning,
11822              "ignoring invalid defined png:compression-level",
11823              "=%s",value);
11824     }
11825
11826   value=GetImageOption(image_info,"png:compression-strategy");
11827   if (value == NULL)
11828      value=GetImageArtifact(image,"png:compression-strategy");
11829   if (value != NULL)
11830   {
11831       if (LocaleCompare(value,"0") == 0)
11832         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11833
11834       else if (LocaleCompare(value,"1") == 0)
11835         mng_info->write_png_compression_strategy = Z_FILTERED+1;
11836
11837       else if (LocaleCompare(value,"2") == 0)
11838         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11839
11840       else if (LocaleCompare(value,"3") == 0)
11841 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
11842         mng_info->write_png_compression_strategy = Z_RLE+1;
11843 #else
11844         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11845 #endif
11846
11847       else if (LocaleCompare(value,"4") == 0)
11848 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
11849         mng_info->write_png_compression_strategy = Z_FIXED+1;
11850 #else
11851         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11852 #endif
11853
11854       else
11855         (void) ThrowMagickException(exception,
11856              GetMagickModule(),CoderWarning,
11857              "ignoring invalid defined png:compression-strategy",
11858              "=%s",value);
11859     }
11860
11861   value=GetImageOption(image_info,"png:compression-filter");
11862   if (value == NULL)
11863      value=GetImageArtifact(image,"png:compression-filter");
11864   if (value != NULL)
11865   {
11866       /* To do: combinations of filters allowed by libpng
11867        * masks 0x08 through 0xf8
11868        *
11869        * Implement this as a comma-separated list of 0,1,2,3,4,5
11870        * where 5 is a special case meaning PNG_ALL_FILTERS.
11871        */
11872
11873       if (LocaleCompare(value,"0") == 0)
11874         mng_info->write_png_compression_filter = 1;
11875
11876       else if (LocaleCompare(value,"1") == 0)
11877         mng_info->write_png_compression_filter = 2;
11878
11879       else if (LocaleCompare(value,"2") == 0)
11880         mng_info->write_png_compression_filter = 3;
11881
11882       else if (LocaleCompare(value,"3") == 0)
11883         mng_info->write_png_compression_filter = 4;
11884
11885       else if (LocaleCompare(value,"4") == 0)
11886         mng_info->write_png_compression_filter = 5;
11887
11888       else if (LocaleCompare(value,"5") == 0)
11889         mng_info->write_png_compression_filter = 6;
11890
11891       else
11892         (void) ThrowMagickException(exception,
11893              GetMagickModule(),CoderWarning,
11894              "ignoring invalid defined png:compression-filter",
11895              "=%s",value);
11896   }
11897
11898   for (source=0; source<8; source++)
11899   {
11900     value = NULL;
11901
11902     if (source == 0)
11903       value=GetImageOption(image_info,"png:exclude-chunks");
11904
11905     if (source == 1)
11906       value=GetImageArtifact(image,"png:exclude-chunks");
11907
11908     if (source == 2)
11909       value=GetImageOption(image_info,"png:exclude-chunk");
11910
11911     if (source == 3)
11912       value=GetImageArtifact(image,"png:exclude-chunk");
11913
11914     if (source == 4)
11915       value=GetImageOption(image_info,"png:include-chunks");
11916
11917     if (source == 5)
11918       value=GetImageArtifact(image,"png:include-chunks");
11919
11920     if (source == 6)
11921       value=GetImageOption(image_info,"png:include-chunk");
11922
11923     if (source == 7)
11924       value=GetImageArtifact(image,"png:include-chunk");
11925
11926     if (value == NULL)
11927        continue;
11928
11929     if (source < 4)
11930       excluding = MagickTrue;
11931     else
11932       excluding = MagickFalse;
11933
11934     if (logging != MagickFalse)
11935       {
11936         if (source == 0 || source == 2)
11937            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11938               "  png:exclude-chunk=%s found in image options.\n", value);
11939         else if (source == 1 || source == 3)
11940            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11941               "  png:exclude-chunk=%s found in image artifacts.\n", value);
11942         else if (source == 4 || source == 6)
11943            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11944               "  png:include-chunk=%s found in image options.\n", value);
11945         else /* if (source == 5 || source == 7) */
11946            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11947               "  png:include-chunk=%s found in image artifacts.\n", value);
11948       }
11949
11950     if (IsOptionMember("all",value) != MagickFalse)
11951       {
11952         mng_info->ping_exclude_bKGD=excluding;
11953         mng_info->ping_exclude_cHRM=excluding;
11954         mng_info->ping_exclude_date=excluding;
11955         mng_info->ping_exclude_EXIF=excluding;
11956         mng_info->ping_exclude_gAMA=excluding;
11957         mng_info->ping_exclude_iCCP=excluding;
11958         /* mng_info->ping_exclude_iTXt=excluding; */
11959         mng_info->ping_exclude_oFFs=excluding;
11960         mng_info->ping_exclude_pHYs=excluding;
11961         mng_info->ping_exclude_sRGB=excluding;
11962         mng_info->ping_exclude_tEXt=excluding;
11963         mng_info->ping_exclude_tIME=excluding;
11964         mng_info->ping_exclude_tRNS=excluding;
11965         mng_info->ping_exclude_vpAg=excluding;
11966         mng_info->ping_exclude_zCCP=excluding;
11967         mng_info->ping_exclude_zTXt=excluding;
11968       }
11969
11970     if (IsOptionMember("none",value) != MagickFalse)
11971       {
11972         mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
11973           MagickTrue;
11974         mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
11975           MagickTrue;
11976         mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
11977           MagickTrue;
11978         mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
11979           MagickTrue;
11980         mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
11981           MagickTrue;
11982         mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
11983           MagickTrue;
11984         /* mng_info->ping_exclude_iTXt=!excluding; */
11985         mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
11986           MagickTrue;
11987         mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
11988           MagickTrue;
11989         mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
11990           MagickTrue;
11991         mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
11992           MagickTrue;
11993         mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
11994           MagickTrue;
11995         mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
11996           MagickTrue;
11997         mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
11998           MagickTrue;
11999         mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12000           MagickTrue;
12001         mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12002           MagickTrue;
12003       }
12004
12005     if (IsOptionMember("bkgd",value) != MagickFalse)
12006       mng_info->ping_exclude_bKGD=excluding;
12007
12008     if (IsOptionMember("chrm",value) != MagickFalse)
12009       mng_info->ping_exclude_cHRM=excluding;
12010
12011     if (IsOptionMember("date",value) != MagickFalse)
12012       mng_info->ping_exclude_date=excluding;
12013
12014     if (IsOptionMember("exif",value) != MagickFalse)
12015       mng_info->ping_exclude_EXIF=excluding;
12016
12017     if (IsOptionMember("gama",value) != MagickFalse)
12018       mng_info->ping_exclude_gAMA=excluding;
12019
12020     if (IsOptionMember("iccp",value) != MagickFalse)
12021       mng_info->ping_exclude_iCCP=excluding;
12022
12023 #if 0
12024     if (IsOptionMember("itxt",value) != MagickFalse)
12025       mng_info->ping_exclude_iTXt=excluding;
12026 #endif
12027
12028     if (IsOptionMember("offs",value) != MagickFalse)
12029       mng_info->ping_exclude_oFFs=excluding;
12030
12031     if (IsOptionMember("phys",value) != MagickFalse)
12032       mng_info->ping_exclude_pHYs=excluding;
12033
12034     if (IsOptionMember("srgb",value) != MagickFalse)
12035       mng_info->ping_exclude_sRGB=excluding;
12036
12037     if (IsOptionMember("text",value) != MagickFalse)
12038       mng_info->ping_exclude_tEXt=excluding;
12039
12040     if (IsOptionMember("time",value) != MagickFalse)
12041       mng_info->ping_exclude_tIME=excluding;
12042
12043     if (IsOptionMember("trns",value) != MagickFalse)
12044       mng_info->ping_exclude_tRNS=excluding;
12045
12046     if (IsOptionMember("vpag",value) != MagickFalse)
12047       mng_info->ping_exclude_vpAg=excluding;
12048
12049     if (IsOptionMember("zccp",value) != MagickFalse)
12050       mng_info->ping_exclude_zCCP=excluding;
12051
12052     if (IsOptionMember("ztxt",value) != MagickFalse)
12053       mng_info->ping_exclude_zTXt=excluding;
12054   }
12055
12056   if (logging != MagickFalse)
12057   {
12058     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12059       "  Chunks to be excluded from the output png:");
12060     if (mng_info->ping_exclude_bKGD != MagickFalse)
12061       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12062           "    bKGD");
12063     if (mng_info->ping_exclude_cHRM != MagickFalse)
12064       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12065           "    cHRM");
12066     if (mng_info->ping_exclude_date != MagickFalse)
12067       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12068           "    date");
12069     if (mng_info->ping_exclude_EXIF != MagickFalse)
12070       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12071           "    EXIF");
12072     if (mng_info->ping_exclude_gAMA != MagickFalse)
12073       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12074           "    gAMA");
12075     if (mng_info->ping_exclude_iCCP != MagickFalse)
12076       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12077           "    iCCP");
12078 #if 0
12079     if (mng_info->ping_exclude_iTXt != MagickFalse)
12080       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12081           "    iTXt");
12082 #endif
12083
12084     if (mng_info->ping_exclude_oFFs != MagickFalse)
12085       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12086           "    oFFs");
12087     if (mng_info->ping_exclude_pHYs != MagickFalse)
12088       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12089           "    pHYs");
12090     if (mng_info->ping_exclude_sRGB != MagickFalse)
12091       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12092           "    sRGB");
12093     if (mng_info->ping_exclude_tEXt != MagickFalse)
12094       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12095           "    tEXt");
12096     if (mng_info->ping_exclude_tIME != MagickFalse)
12097       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12098           "    tIME");
12099     if (mng_info->ping_exclude_tRNS != MagickFalse)
12100       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12101           "    tRNS");
12102     if (mng_info->ping_exclude_vpAg != MagickFalse)
12103       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12104           "    vpAg");
12105     if (mng_info->ping_exclude_zCCP != MagickFalse)
12106       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12107           "    zCCP");
12108     if (mng_info->ping_exclude_zTXt != MagickFalse)
12109       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12110           "    zTXt");
12111   }
12112
12113   mng_info->need_blob = MagickTrue;
12114
12115   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12116
12117   MngInfoFreeStruct(mng_info,&have_mng_structure);
12118
12119   if (logging != MagickFalse)
12120     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12121
12122   return(status);
12123 }
12124
12125 #if defined(JNG_SUPPORTED)
12126
12127 /* Write one JNG image */
12128 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12129    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12130 {
12131   Image
12132     *jpeg_image;
12133
12134   ImageInfo
12135     *jpeg_image_info;
12136
12137   MagickBooleanType
12138     logging,
12139     status;
12140
12141   size_t
12142     length;
12143
12144   unsigned char
12145     *blob,
12146     chunk[80],
12147     *p;
12148
12149   unsigned int
12150     jng_alpha_compression_method,
12151     jng_alpha_sample_depth,
12152     jng_color_type,
12153     transparent;
12154
12155   size_t
12156     jng_alpha_quality,
12157     jng_quality;
12158
12159   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12160     "  Enter WriteOneJNGImage()");
12161
12162   blob=(unsigned char *) NULL;
12163   jpeg_image=(Image *) NULL;
12164   jpeg_image_info=(ImageInfo *) NULL;
12165   length=0;
12166
12167   status=MagickTrue;
12168   transparent=image_info->type==GrayscaleAlphaType ||
12169      image_info->type==TrueColorAlphaType ||
12170      image->alpha_trait != UndefinedPixelTrait;
12171
12172   jng_alpha_sample_depth = 0;
12173
12174   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12175
12176   jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12177
12178   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12179       image_info->quality;
12180
12181   if (jng_alpha_quality >= 1000)
12182     jng_alpha_quality /= 1000;
12183
12184   length=0;
12185
12186   if (transparent != 0)
12187     {
12188       jng_color_type=14;
12189
12190       /* Create JPEG blob, image, and image_info */
12191       if (logging != MagickFalse)
12192         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12193           "  Creating jpeg_image_info for alpha.");
12194
12195       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12196
12197       if (jpeg_image_info == (ImageInfo *) NULL)
12198         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12199
12200       if (logging != MagickFalse)
12201         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12202           "  Creating jpeg_image.");
12203
12204       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12205       if (jpeg_image == (Image *) NULL)
12206         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12207       (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12208       jpeg_image->alpha_trait=UndefinedPixelTrait;
12209       jpeg_image->quality=jng_alpha_quality;
12210       jpeg_image_info->type=GrayscaleType;
12211       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12212       (void) AcquireUniqueFilename(jpeg_image->filename);
12213       (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
12214         "%s",jpeg_image->filename);
12215     }
12216   else
12217     {
12218       jng_alpha_compression_method=0;
12219       jng_color_type=10;
12220       jng_alpha_sample_depth=0;
12221     }
12222
12223   /* To do: check bit depth of PNG alpha channel */
12224
12225   /* Check if image is grayscale. */
12226   if (image_info->type != TrueColorAlphaType && image_info->type !=
12227     TrueColorType && SetImageGray(image,exception))
12228     jng_color_type-=2;
12229
12230   if (logging != MagickFalse)
12231     {
12232         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12233           "    JNG Quality           = %d",(int) jng_quality);
12234         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12235           "    JNG Color Type        = %d",jng_color_type);
12236         if (transparent != 0)
12237           {
12238             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12239               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12240             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12241               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12242             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12243               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12244           }
12245     }
12246
12247   if (transparent != 0)
12248     {
12249       if (jng_alpha_compression_method==0)
12250         {
12251           const char
12252             *value;
12253
12254           /* Encode alpha as a grayscale PNG blob */
12255           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12256             exception);
12257           if (status == MagickFalse)
12258             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12259  
12260           if (logging != MagickFalse)
12261             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12262               "  Creating PNG blob.");
12263
12264           (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12265           (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12266           jpeg_image_info->interlace=NoInterlace;
12267
12268           /* Exclude all ancillary chunks */
12269           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12270
12271           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12272             exception);
12273
12274           /* Retrieve sample depth used */
12275           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12276           if (value != (char *) NULL)
12277             jng_alpha_sample_depth= (unsigned int) value[0];
12278         }
12279       else
12280         {
12281           /* Encode alpha as a grayscale JPEG blob */
12282
12283           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12284             exception);
12285           if (status == MagickFalse)
12286             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12287  
12288
12289           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12290           (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12291           jpeg_image_info->interlace=NoInterlace;
12292           if (logging != MagickFalse)
12293             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12294               "  Creating blob.");
12295           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12296            exception);
12297           jng_alpha_sample_depth=8;
12298
12299           if (logging != MagickFalse)
12300             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12301               "  Successfully read jpeg_image into a blob, length=%.20g.",
12302               (double) length);
12303
12304         }
12305       /* Destroy JPEG image and image_info */
12306       jpeg_image=DestroyImage(jpeg_image);
12307       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12308       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12309     }
12310
12311   /* Write JHDR chunk */
12312   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12313   PNGType(chunk,mng_JHDR);
12314   LogPNGChunk(logging,mng_JHDR,16L);
12315   PNGLong(chunk+4,(png_uint_32) image->columns);
12316   PNGLong(chunk+8,(png_uint_32) image->rows);
12317   chunk[12]=jng_color_type;
12318   chunk[13]=8;  /* sample depth */
12319   chunk[14]=8; /*jng_image_compression_method */
12320   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12321   chunk[16]=jng_alpha_sample_depth;
12322   chunk[17]=jng_alpha_compression_method;
12323   chunk[18]=0; /*jng_alpha_filter_method */
12324   chunk[19]=0; /*jng_alpha_interlace_method */
12325   (void) WriteBlob(image,20,chunk);
12326   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12327   if (logging != MagickFalse)
12328     {
12329       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12330         "    JNG width:%15lu",(unsigned long) image->columns);
12331
12332       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12333         "    JNG height:%14lu",(unsigned long) image->rows);
12334
12335       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12336         "    JNG color type:%10d",jng_color_type);
12337
12338       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12339         "    JNG sample depth:%8d",8);
12340
12341       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12342         "    JNG compression:%9d",8);
12343
12344       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12345         "    JNG interlace:%11d",0);
12346
12347       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12348         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
12349
12350       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12351         "    JNG alpha compression:%3d",jng_alpha_compression_method);
12352
12353       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12354         "    JNG alpha filter:%8d",0);
12355
12356       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12357         "    JNG alpha interlace:%5d",0);
12358     }
12359
12360   /* Write any JNG-chunk-b profiles */
12361   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12362
12363   /*
12364      Write leading ancillary chunks
12365   */
12366
12367   if (transparent != 0)
12368   {
12369     /*
12370       Write JNG bKGD chunk
12371     */
12372
12373     unsigned char
12374       blue,
12375       green,
12376       red;
12377
12378     ssize_t
12379       num_bytes;
12380
12381     if (jng_color_type == 8 || jng_color_type == 12)
12382       num_bytes=6L;
12383     else
12384       num_bytes=10L;
12385     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12386     PNGType(chunk,mng_bKGD);
12387     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12388     red=ScaleQuantumToChar(image->background_color.red);
12389     green=ScaleQuantumToChar(image->background_color.green);
12390     blue=ScaleQuantumToChar(image->background_color.blue);
12391     *(chunk+4)=0;
12392     *(chunk+5)=red;
12393     *(chunk+6)=0;
12394     *(chunk+7)=green;
12395     *(chunk+8)=0;
12396     *(chunk+9)=blue;
12397     (void) WriteBlob(image,(size_t) num_bytes,chunk);
12398     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12399   }
12400
12401   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12402     {
12403       /*
12404         Write JNG sRGB chunk
12405       */
12406       (void) WriteBlobMSBULong(image,1L);
12407       PNGType(chunk,mng_sRGB);
12408       LogPNGChunk(logging,mng_sRGB,1L);
12409
12410       if (image->rendering_intent != UndefinedIntent)
12411         chunk[4]=(unsigned char)
12412           Magick_RenderingIntent_to_PNG_RenderingIntent(
12413           (image->rendering_intent));
12414
12415       else
12416         chunk[4]=(unsigned char)
12417           Magick_RenderingIntent_to_PNG_RenderingIntent(
12418           (PerceptualIntent));
12419
12420       (void) WriteBlob(image,5,chunk);
12421       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12422     }
12423   else
12424     {
12425       if (image->gamma != 0.0)
12426         {
12427           /*
12428              Write JNG gAMA chunk
12429           */
12430           (void) WriteBlobMSBULong(image,4L);
12431           PNGType(chunk,mng_gAMA);
12432           LogPNGChunk(logging,mng_gAMA,4L);
12433           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12434           (void) WriteBlob(image,8,chunk);
12435           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12436         }
12437
12438       if ((mng_info->equal_chrms == MagickFalse) &&
12439           (image->chromaticity.red_primary.x != 0.0))
12440         {
12441           PrimaryInfo
12442             primary;
12443
12444           /*
12445              Write JNG cHRM chunk
12446           */
12447           (void) WriteBlobMSBULong(image,32L);
12448           PNGType(chunk,mng_cHRM);
12449           LogPNGChunk(logging,mng_cHRM,32L);
12450           primary=image->chromaticity.white_point;
12451           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12452           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12453           primary=image->chromaticity.red_primary;
12454           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12455           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12456           primary=image->chromaticity.green_primary;
12457           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12458           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12459           primary=image->chromaticity.blue_primary;
12460           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12461           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12462           (void) WriteBlob(image,36,chunk);
12463           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12464         }
12465     }
12466
12467   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12468     {
12469       /*
12470          Write JNG pHYs chunk
12471       */
12472       (void) WriteBlobMSBULong(image,9L);
12473       PNGType(chunk,mng_pHYs);
12474       LogPNGChunk(logging,mng_pHYs,9L);
12475       if (image->units == PixelsPerInchResolution)
12476         {
12477           PNGLong(chunk+4,(png_uint_32)
12478             (image->resolution.x*100.0/2.54+0.5));
12479
12480           PNGLong(chunk+8,(png_uint_32)
12481             (image->resolution.y*100.0/2.54+0.5));
12482
12483           chunk[12]=1;
12484         }
12485
12486       else
12487         {
12488           if (image->units == PixelsPerCentimeterResolution)
12489             {
12490               PNGLong(chunk+4,(png_uint_32)
12491                 (image->resolution.x*100.0+0.5));
12492
12493               PNGLong(chunk+8,(png_uint_32)
12494                 (image->resolution.y*100.0+0.5));
12495
12496               chunk[12]=1;
12497             }
12498
12499           else
12500             {
12501               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12502               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12503               chunk[12]=0;
12504             }
12505         }
12506       (void) WriteBlob(image,13,chunk);
12507       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12508     }
12509
12510   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12511     {
12512       /*
12513          Write JNG oFFs chunk
12514       */
12515       (void) WriteBlobMSBULong(image,9L);
12516       PNGType(chunk,mng_oFFs);
12517       LogPNGChunk(logging,mng_oFFs,9L);
12518       PNGsLong(chunk+4,(ssize_t) (image->page.x));
12519       PNGsLong(chunk+8,(ssize_t) (image->page.y));
12520       chunk[12]=0;
12521       (void) WriteBlob(image,13,chunk);
12522       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12523     }
12524   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12525     {
12526        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
12527        PNGType(chunk,mng_vpAg);
12528        LogPNGChunk(logging,mng_vpAg,9L);
12529        PNGLong(chunk+4,(png_uint_32) image->page.width);
12530        PNGLong(chunk+8,(png_uint_32) image->page.height);
12531        chunk[12]=0;   /* unit = pixels */
12532        (void) WriteBlob(image,13,chunk);
12533        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12534     }
12535
12536   if (transparent != 0)
12537     {
12538       if (jng_alpha_compression_method==0)
12539         {
12540           register ssize_t
12541             i;
12542
12543           size_t
12544             len;
12545
12546           /* Write IDAT chunk header */
12547           if (logging != MagickFalse)
12548             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12549               "  Write IDAT chunks from blob, length=%.20g.",(double)
12550               length);
12551
12552           /* Copy IDAT chunks */
12553           len=0;
12554           p=blob+8;
12555           for (i=8; i<(ssize_t) length; i+=len+12)
12556           {
12557             len=(size_t) (*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12558             p+=4;
12559
12560             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12561               {
12562                 /* Found an IDAT chunk. */
12563                 (void) WriteBlobMSBULong(image,len);
12564                 LogPNGChunk(logging,mng_IDAT,len);
12565                 (void) WriteBlob(image,len+4,p);
12566                 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
12567               }
12568
12569             else
12570               {
12571                 if (logging != MagickFalse)
12572                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12573                     "    Skipping %c%c%c%c chunk, length=%.20g.",
12574                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
12575               }
12576             p+=(8+len);
12577           }
12578         }
12579       else if (length != 0)
12580         {
12581           /* Write JDAA chunk header */
12582           if (logging != MagickFalse)
12583             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12584               "  Write JDAA chunk, length=%.20g.",(double) length);
12585           (void) WriteBlobMSBULong(image,(size_t) length);
12586           PNGType(chunk,mng_JDAA);
12587           LogPNGChunk(logging,mng_JDAA,length);
12588           /* Write JDAT chunk(s) data */
12589           (void) WriteBlob(image,4,chunk);
12590           (void) WriteBlob(image,length,blob);
12591           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12592              (uInt) length));
12593         }
12594       blob=(unsigned char *) RelinquishMagickMemory(blob);
12595     }
12596
12597   /* Encode image as a JPEG blob */
12598   if (logging != MagickFalse)
12599     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12600       "  Creating jpeg_image_info.");
12601   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12602   if (jpeg_image_info == (ImageInfo *) NULL)
12603     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12604
12605   if (logging != MagickFalse)
12606     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12607       "  Creating jpeg_image.");
12608
12609   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12610   if (jpeg_image == (Image *) NULL)
12611     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12612   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12613
12614   (void) AcquireUniqueFilename(jpeg_image->filename);
12615   (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12616     jpeg_image->filename);
12617
12618   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12619     exception);
12620
12621   if (logging != MagickFalse)
12622     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12623       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12624       (double) jpeg_image->rows);
12625
12626   if (status == MagickFalse)
12627     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12628
12629   if (jng_color_type == 8 || jng_color_type == 12)
12630     jpeg_image_info->type=GrayscaleType;
12631
12632   jpeg_image_info->quality=jng_quality;
12633   jpeg_image->quality=jng_quality;
12634   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12635   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12636
12637   if (logging != MagickFalse)
12638     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12639       "  Creating blob.");
12640
12641   blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12642
12643   if (logging != MagickFalse)
12644     {
12645       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12646         "  Successfully read jpeg_image into a blob, length=%.20g.",
12647         (double) length);
12648
12649       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12650         "  Write JDAT chunk, length=%.20g.",(double) length);
12651     }
12652
12653   /* Write JDAT chunk(s) */
12654   (void) WriteBlobMSBULong(image,(size_t) length);
12655   PNGType(chunk,mng_JDAT);
12656   LogPNGChunk(logging,mng_JDAT,length);
12657   (void) WriteBlob(image,4,chunk);
12658   (void) WriteBlob(image,length,blob);
12659   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12660
12661   jpeg_image=DestroyImage(jpeg_image);
12662   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12663   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12664   blob=(unsigned char *) RelinquishMagickMemory(blob);
12665
12666   /* Write any JNG-chunk-e profiles */
12667   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12668
12669   /* Write IEND chunk */
12670   (void) WriteBlobMSBULong(image,0L);
12671   PNGType(chunk,mng_IEND);
12672   LogPNGChunk(logging,mng_IEND,0);
12673   (void) WriteBlob(image,4,chunk);
12674   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12675
12676   if (logging != MagickFalse)
12677     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12678       "  exit WriteOneJNGImage()");
12679
12680   return(status);
12681 }
12682
12683
12684 /*
12685 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12686 %                                                                             %
12687 %                                                                             %
12688 %                                                                             %
12689 %   W r i t e J N G I m a g e                                                 %
12690 %                                                                             %
12691 %                                                                             %
12692 %                                                                             %
12693 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12694 %
12695 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12696 %
12697 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
12698 %
12699 %  The format of the WriteJNGImage method is:
12700 %
12701 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12702 %        Image *image,ExceptionInfo *exception)
12703 %
12704 %  A description of each parameter follows:
12705 %
12706 %    o image_info: the image info.
12707 %
12708 %    o image:  The image.
12709 %
12710 %    o exception: return any errors or warnings in this structure.
12711 %
12712 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12713 */
12714 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12715   ExceptionInfo *exception)
12716 {
12717   MagickBooleanType
12718     have_mng_structure,
12719     logging,
12720     status;
12721
12722   MngInfo
12723     *mng_info;
12724
12725   /*
12726     Open image file.
12727   */
12728   assert(image_info != (const ImageInfo *) NULL);
12729   assert(image_info->signature == MagickSignature);
12730   assert(image != (Image *) NULL);
12731   assert(image->signature == MagickSignature);
12732   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12733   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12734   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12735   if (status == MagickFalse)
12736     return(status);
12737
12738   /*
12739     Allocate a MngInfo structure.
12740   */
12741   have_mng_structure=MagickFalse;
12742   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12743   if (mng_info == (MngInfo *) NULL)
12744     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12745   /*
12746     Initialize members of the MngInfo structure.
12747   */
12748   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12749   mng_info->image=image;
12750   have_mng_structure=MagickTrue;
12751
12752   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12753
12754   status=WriteOneJNGImage(mng_info,image_info,image,exception);
12755   (void) CloseBlob(image);
12756
12757   (void) CatchImageException(image);
12758   MngInfoFreeStruct(mng_info,&have_mng_structure);
12759   if (logging != MagickFalse)
12760     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12761   return(status);
12762 }
12763 #endif
12764
12765 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12766   ExceptionInfo *exception)
12767 {
12768   const char
12769     *option;
12770
12771   Image
12772     *next_image;
12773
12774   MagickBooleanType
12775     have_mng_structure,
12776     status;
12777
12778   volatile MagickBooleanType
12779     logging;
12780
12781   MngInfo
12782     *mng_info;
12783
12784   int
12785     image_count,
12786     need_iterations,
12787     need_matte;
12788
12789   volatile int
12790 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12791     defined(PNG_MNG_FEATURES_SUPPORTED)
12792     need_local_plte,
12793 #endif
12794     all_images_are_gray,
12795     need_defi,
12796     use_global_plte;
12797
12798   register ssize_t
12799     i;
12800
12801   unsigned char
12802     chunk[800];
12803
12804   volatile unsigned int
12805     write_jng,
12806     write_mng;
12807
12808   volatile size_t
12809     scene;
12810
12811   size_t
12812     final_delay=0,
12813     initial_delay;
12814
12815 #if (PNG_LIBPNG_VER < 10200)
12816     if (image_info->verbose)
12817       printf("Your PNG library (libpng-%s) is rather old.\n",
12818          PNG_LIBPNG_VER_STRING);
12819 #endif
12820
12821   /*
12822     Open image file.
12823   */
12824   assert(image_info != (const ImageInfo *) NULL);
12825   assert(image_info->signature == MagickSignature);
12826   assert(image != (Image *) NULL);
12827   assert(image->signature == MagickSignature);
12828   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12829   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12830   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12831   if (status == MagickFalse)
12832     return(status);
12833
12834   /*
12835     Allocate a MngInfo structure.
12836   */
12837   have_mng_structure=MagickFalse;
12838   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12839   if (mng_info == (MngInfo *) NULL)
12840     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12841   /*
12842     Initialize members of the MngInfo structure.
12843   */
12844   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12845   mng_info->image=image;
12846   have_mng_structure=MagickTrue;
12847   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12848
12849   /*
12850    * See if user has requested a specific PNG subformat to be used
12851    * for all of the PNGs in the MNG being written, e.g.,
12852    *
12853    *    convert *.png png8:animation.mng
12854    *
12855    * To do: check -define png:bit_depth and png:color_type as well,
12856    * or perhaps use mng:bit_depth and mng:color_type instead for
12857    * global settings.
12858    */
12859
12860   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12861   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12862   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12863
12864   write_jng=MagickFalse;
12865   if (image_info->compression == JPEGCompression)
12866     write_jng=MagickTrue;
12867
12868   mng_info->adjoin=image_info->adjoin &&
12869     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12870
12871   if (logging != MagickFalse)
12872     {
12873       /* Log some info about the input */
12874       Image
12875         *p;
12876
12877       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12878         "  Checking input image(s)\n"
12879         "    Image_info depth: %.20g,    Type: %d",
12880         (double) image_info->depth, image_info->type);
12881
12882       scene=0;
12883       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12884       {
12885
12886         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12887            "    Scene: %.20g\n,   Image depth: %.20g",
12888            (double) scene++, (double) p->depth);
12889
12890         if (p->alpha_trait != UndefinedPixelTrait)
12891           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12892             "      Matte: True");
12893
12894         else
12895           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12896             "      Matte: False");
12897
12898         if (p->storage_class == PseudoClass)
12899           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12900             "      Storage class: PseudoClass");
12901
12902         else
12903           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12904             "      Storage class: DirectClass");
12905
12906         if (p->colors)
12907           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12908             "      Number of colors: %.20g",(double) p->colors);
12909
12910         else
12911           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12912             "      Number of colors: unspecified");
12913
12914         if (mng_info->adjoin == MagickFalse)
12915           break;
12916       }
12917     }
12918
12919   use_global_plte=MagickFalse;
12920   all_images_are_gray=MagickFalse;
12921 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12922   need_local_plte=MagickTrue;
12923 #endif
12924   need_defi=MagickFalse;
12925   need_matte=MagickFalse;
12926   mng_info->framing_mode=1;
12927   mng_info->old_framing_mode=1;
12928
12929   if (write_mng)
12930       if (image_info->page != (char *) NULL)
12931         {
12932           /*
12933             Determine image bounding box.
12934           */
12935           SetGeometry(image,&mng_info->page);
12936           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12937             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12938         }
12939   if (write_mng)
12940     {
12941       unsigned int
12942         need_geom;
12943
12944       unsigned short
12945         red,
12946         green,
12947         blue;
12948
12949       mng_info->page=image->page;
12950       need_geom=MagickTrue;
12951       if (mng_info->page.width || mng_info->page.height)
12952          need_geom=MagickFalse;
12953       /*
12954         Check all the scenes.
12955       */
12956       initial_delay=image->delay;
12957       need_iterations=MagickFalse;
12958       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12959       mng_info->equal_physs=MagickTrue,
12960       mng_info->equal_gammas=MagickTrue;
12961       mng_info->equal_srgbs=MagickTrue;
12962       mng_info->equal_backgrounds=MagickTrue;
12963       image_count=0;
12964 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12965     defined(PNG_MNG_FEATURES_SUPPORTED)
12966       all_images_are_gray=MagickTrue;
12967       mng_info->equal_palettes=MagickFalse;
12968       need_local_plte=MagickFalse;
12969 #endif
12970       for (next_image=image; next_image != (Image *) NULL; )
12971       {
12972         if (need_geom)
12973           {
12974             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12975               mng_info->page.width=next_image->columns+next_image->page.x;
12976
12977             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12978               mng_info->page.height=next_image->rows+next_image->page.y;
12979           }
12980
12981         if (next_image->page.x || next_image->page.y)
12982           need_defi=MagickTrue;
12983
12984         if (next_image->alpha_trait != UndefinedPixelTrait)
12985           need_matte=MagickTrue;
12986
12987         if ((int) next_image->dispose >= BackgroundDispose)
12988           if ((next_image->alpha_trait != UndefinedPixelTrait) ||
12989                next_image->page.x || next_image->page.y ||
12990               ((next_image->columns < mng_info->page.width) &&
12991                (next_image->rows < mng_info->page.height)))
12992             mng_info->need_fram=MagickTrue;
12993
12994         if (next_image->iterations)
12995           need_iterations=MagickTrue;
12996
12997         final_delay=next_image->delay;
12998
12999         if (final_delay != initial_delay || final_delay > 1UL*
13000            next_image->ticks_per_second)
13001           mng_info->need_fram=1;
13002
13003 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13004     defined(PNG_MNG_FEATURES_SUPPORTED)
13005         /*
13006           check for global palette possibility.
13007         */
13008         if (image->alpha_trait != UndefinedPixelTrait)
13009            need_local_plte=MagickTrue;
13010
13011         if (need_local_plte == 0)
13012           {
13013             if (SetImageGray(image,exception) == MagickFalse)
13014               all_images_are_gray=MagickFalse;
13015             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13016             if (use_global_plte == 0)
13017               use_global_plte=mng_info->equal_palettes;
13018             need_local_plte=!mng_info->equal_palettes;
13019           }
13020 #endif
13021         if (GetNextImageInList(next_image) != (Image *) NULL)
13022           {
13023             if (next_image->background_color.red !=
13024                 next_image->next->background_color.red ||
13025                 next_image->background_color.green !=
13026                 next_image->next->background_color.green ||
13027                 next_image->background_color.blue !=
13028                 next_image->next->background_color.blue)
13029               mng_info->equal_backgrounds=MagickFalse;
13030
13031             if (next_image->gamma != next_image->next->gamma)
13032               mng_info->equal_gammas=MagickFalse;
13033
13034             if (next_image->rendering_intent !=
13035                 next_image->next->rendering_intent)
13036               mng_info->equal_srgbs=MagickFalse;
13037
13038             if ((next_image->units != next_image->next->units) ||
13039                 (next_image->resolution.x != next_image->next->resolution.x) ||
13040                 (next_image->resolution.y != next_image->next->resolution.y))
13041               mng_info->equal_physs=MagickFalse;
13042
13043             if (mng_info->equal_chrms)
13044               {
13045                 if (next_image->chromaticity.red_primary.x !=
13046                     next_image->next->chromaticity.red_primary.x ||
13047                     next_image->chromaticity.red_primary.y !=
13048                     next_image->next->chromaticity.red_primary.y ||
13049                     next_image->chromaticity.green_primary.x !=
13050                     next_image->next->chromaticity.green_primary.x ||
13051                     next_image->chromaticity.green_primary.y !=
13052                     next_image->next->chromaticity.green_primary.y ||
13053                     next_image->chromaticity.blue_primary.x !=
13054                     next_image->next->chromaticity.blue_primary.x ||
13055                     next_image->chromaticity.blue_primary.y !=
13056                     next_image->next->chromaticity.blue_primary.y ||
13057                     next_image->chromaticity.white_point.x !=
13058                     next_image->next->chromaticity.white_point.x ||
13059                     next_image->chromaticity.white_point.y !=
13060                     next_image->next->chromaticity.white_point.y)
13061                   mng_info->equal_chrms=MagickFalse;
13062               }
13063           }
13064         image_count++;
13065         next_image=GetNextImageInList(next_image);
13066       }
13067       if (image_count < 2)
13068         {
13069           mng_info->equal_backgrounds=MagickFalse;
13070           mng_info->equal_chrms=MagickFalse;
13071           mng_info->equal_gammas=MagickFalse;
13072           mng_info->equal_srgbs=MagickFalse;
13073           mng_info->equal_physs=MagickFalse;
13074           use_global_plte=MagickFalse;
13075 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13076           need_local_plte=MagickTrue;
13077 #endif
13078           need_iterations=MagickFalse;
13079         }
13080
13081      if (mng_info->need_fram == MagickFalse)
13082        {
13083          /*
13084            Only certain framing rates 100/n are exactly representable without
13085            the FRAM chunk but we'll allow some slop in VLC files
13086          */
13087          if (final_delay == 0)
13088            {
13089              if (need_iterations != MagickFalse)
13090                {
13091                  /*
13092                    It's probably a GIF with loop; don't run it *too* fast.
13093                  */
13094                  if (mng_info->adjoin)
13095                    {
13096                      final_delay=10;
13097                      (void) ThrowMagickException(exception,GetMagickModule(),
13098                        CoderWarning,
13099                        "input has zero delay between all frames; assuming",
13100                        " 10 cs `%s'","");
13101                    }
13102                }
13103              else
13104                mng_info->ticks_per_second=0;
13105            }
13106          if (final_delay != 0)
13107            mng_info->ticks_per_second=(png_uint_32)
13108               (image->ticks_per_second/final_delay);
13109          if (final_delay > 50)
13110            mng_info->ticks_per_second=2;
13111
13112          if (final_delay > 75)
13113            mng_info->ticks_per_second=1;
13114
13115          if (final_delay > 125)
13116            mng_info->need_fram=MagickTrue;
13117
13118          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13119             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13120             (final_delay != 25) && (final_delay != 50) && (1UL*final_delay !=
13121                1UL*image->ticks_per_second))
13122            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13123        }
13124
13125      if (mng_info->need_fram != MagickFalse)
13126         mng_info->ticks_per_second=1UL*image->ticks_per_second;
13127      /*
13128         If pseudocolor, we should also check to see if all the
13129         palettes are identical and write a global PLTE if they are.
13130         ../glennrp Feb 99.
13131      */
13132      /*
13133         Write the MNG version 1.0 signature and MHDR chunk.
13134      */
13135      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13136      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13137      PNGType(chunk,mng_MHDR);
13138      LogPNGChunk(logging,mng_MHDR,28L);
13139      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13140      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13141      PNGLong(chunk+12,mng_info->ticks_per_second);
13142      PNGLong(chunk+16,0L);  /* layer count=unknown */
13143      PNGLong(chunk+20,0L);  /* frame count=unknown */
13144      PNGLong(chunk+24,0L);  /* play time=unknown   */
13145      if (write_jng)
13146        {
13147          if (need_matte)
13148            {
13149              if (need_defi || mng_info->need_fram || use_global_plte)
13150                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13151
13152              else
13153                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13154            }
13155
13156          else
13157            {
13158              if (need_defi || mng_info->need_fram || use_global_plte)
13159                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13160
13161              else
13162                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13163            }
13164        }
13165
13166      else
13167        {
13168          if (need_matte)
13169            {
13170              if (need_defi || mng_info->need_fram || use_global_plte)
13171                PNGLong(chunk+28,11L);    /* simplicity=LC */
13172
13173              else
13174                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13175            }
13176
13177          else
13178            {
13179              if (need_defi || mng_info->need_fram || use_global_plte)
13180                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13181
13182              else
13183                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13184            }
13185        }
13186      (void) WriteBlob(image,32,chunk);
13187      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13188      option=GetImageOption(image_info,"mng:need-cacheoff");
13189      if (option != (const char *) NULL)
13190        {
13191          size_t
13192            length;
13193
13194          /*
13195            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13196          */
13197          PNGType(chunk,mng_nEED);
13198          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13199          (void) WriteBlobMSBULong(image,(size_t) length);
13200          LogPNGChunk(logging,mng_nEED,(size_t) length);
13201          length+=4;
13202          (void) WriteBlob(image,length,chunk);
13203          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13204        }
13205      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13206          (GetNextImageInList(image) != (Image *) NULL) &&
13207          (image->iterations != 1))
13208        {
13209          /*
13210            Write MNG TERM chunk
13211          */
13212          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13213          PNGType(chunk,mng_TERM);
13214          LogPNGChunk(logging,mng_TERM,10L);
13215          chunk[4]=3;  /* repeat animation */
13216          chunk[5]=0;  /* show last frame when done */
13217          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13218             final_delay/MagickMax(image->ticks_per_second,1)));
13219
13220          if (image->iterations == 0)
13221            PNGLong(chunk+10,PNG_UINT_31_MAX);
13222
13223          else
13224            PNGLong(chunk+10,(png_uint_32) image->iterations);
13225
13226          if (logging != MagickFalse)
13227            {
13228              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13229                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13230               final_delay/MagickMax(image->ticks_per_second,1)));
13231
13232              if (image->iterations == 0)
13233                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13234                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13235
13236              else
13237                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13238                  "     Image iterations: %.20g",(double) image->iterations);
13239            }
13240          (void) WriteBlob(image,14,chunk);
13241          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13242        }
13243      /*
13244        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13245      */
13246      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13247           mng_info->equal_srgbs)
13248        {
13249          /*
13250            Write MNG sRGB chunk
13251          */
13252          (void) WriteBlobMSBULong(image,1L);
13253          PNGType(chunk,mng_sRGB);
13254          LogPNGChunk(logging,mng_sRGB,1L);
13255
13256          if (image->rendering_intent != UndefinedIntent)
13257            chunk[4]=(unsigned char)
13258              Magick_RenderingIntent_to_PNG_RenderingIntent(
13259              (image->rendering_intent));
13260
13261          else
13262            chunk[4]=(unsigned char)
13263              Magick_RenderingIntent_to_PNG_RenderingIntent(
13264                (PerceptualIntent));
13265
13266          (void) WriteBlob(image,5,chunk);
13267          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13268          mng_info->have_write_global_srgb=MagickTrue;
13269        }
13270
13271      else
13272        {
13273          if (image->gamma && mng_info->equal_gammas)
13274            {
13275              /*
13276                 Write MNG gAMA chunk
13277              */
13278              (void) WriteBlobMSBULong(image,4L);
13279              PNGType(chunk,mng_gAMA);
13280              LogPNGChunk(logging,mng_gAMA,4L);
13281              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13282              (void) WriteBlob(image,8,chunk);
13283              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13284              mng_info->have_write_global_gama=MagickTrue;
13285            }
13286          if (mng_info->equal_chrms)
13287            {
13288              PrimaryInfo
13289                primary;
13290
13291              /*
13292                 Write MNG cHRM chunk
13293              */
13294              (void) WriteBlobMSBULong(image,32L);
13295              PNGType(chunk,mng_cHRM);
13296              LogPNGChunk(logging,mng_cHRM,32L);
13297              primary=image->chromaticity.white_point;
13298              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13299              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13300              primary=image->chromaticity.red_primary;
13301              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13302              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13303              primary=image->chromaticity.green_primary;
13304              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13305              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13306              primary=image->chromaticity.blue_primary;
13307              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13308              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13309              (void) WriteBlob(image,36,chunk);
13310              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13311              mng_info->have_write_global_chrm=MagickTrue;
13312            }
13313        }
13314      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13315        {
13316          /*
13317             Write MNG pHYs chunk
13318          */
13319          (void) WriteBlobMSBULong(image,9L);
13320          PNGType(chunk,mng_pHYs);
13321          LogPNGChunk(logging,mng_pHYs,9L);
13322
13323          if (image->units == PixelsPerInchResolution)
13324            {
13325              PNGLong(chunk+4,(png_uint_32)
13326                (image->resolution.x*100.0/2.54+0.5));
13327
13328              PNGLong(chunk+8,(png_uint_32)
13329                (image->resolution.y*100.0/2.54+0.5));
13330
13331              chunk[12]=1;
13332            }
13333
13334          else
13335            {
13336              if (image->units == PixelsPerCentimeterResolution)
13337                {
13338                  PNGLong(chunk+4,(png_uint_32)
13339                    (image->resolution.x*100.0+0.5));
13340
13341                  PNGLong(chunk+8,(png_uint_32)
13342                    (image->resolution.y*100.0+0.5));
13343
13344                  chunk[12]=1;
13345                }
13346
13347              else
13348                {
13349                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13350                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13351                  chunk[12]=0;
13352                }
13353            }
13354          (void) WriteBlob(image,13,chunk);
13355          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13356        }
13357      /*
13358        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13359        or does not cover the entire frame.
13360      */
13361      if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13362          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13363          (image->page.width+image->page.x < mng_info->page.width))
13364          || (image->page.height && (image->page.height+image->page.y
13365          < mng_info->page.height))))
13366        {
13367          (void) WriteBlobMSBULong(image,6L);
13368          PNGType(chunk,mng_BACK);
13369          LogPNGChunk(logging,mng_BACK,6L);
13370          red=ScaleQuantumToShort(image->background_color.red);
13371          green=ScaleQuantumToShort(image->background_color.green);
13372          blue=ScaleQuantumToShort(image->background_color.blue);
13373          PNGShort(chunk+4,red);
13374          PNGShort(chunk+6,green);
13375          PNGShort(chunk+8,blue);
13376          (void) WriteBlob(image,10,chunk);
13377          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13378          if (mng_info->equal_backgrounds)
13379            {
13380              (void) WriteBlobMSBULong(image,6L);
13381              PNGType(chunk,mng_bKGD);
13382              LogPNGChunk(logging,mng_bKGD,6L);
13383              (void) WriteBlob(image,10,chunk);
13384              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13385            }
13386        }
13387
13388 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13389      if ((need_local_plte == MagickFalse) &&
13390          (image->storage_class == PseudoClass) &&
13391          (all_images_are_gray == MagickFalse))
13392        {
13393          size_t
13394            data_length;
13395
13396          /*
13397            Write MNG PLTE chunk
13398          */
13399          data_length=3*image->colors;
13400          (void) WriteBlobMSBULong(image,data_length);
13401          PNGType(chunk,mng_PLTE);
13402          LogPNGChunk(logging,mng_PLTE,data_length);
13403
13404          for (i=0; i < (ssize_t) image->colors; i++)
13405          {
13406            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13407              image->colormap[i].red) & 0xff);
13408            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13409              image->colormap[i].green) & 0xff);
13410            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13411              image->colormap[i].blue) & 0xff);
13412          }
13413
13414          (void) WriteBlob(image,data_length+4,chunk);
13415          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13416          mng_info->have_write_global_plte=MagickTrue;
13417        }
13418 #endif
13419     }
13420   scene=0;
13421   mng_info->delay=0;
13422 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13423     defined(PNG_MNG_FEATURES_SUPPORTED)
13424   mng_info->equal_palettes=MagickFalse;
13425 #endif
13426   do
13427   {
13428     if (mng_info->adjoin)
13429     {
13430 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13431     defined(PNG_MNG_FEATURES_SUPPORTED)
13432     /*
13433       If we aren't using a global palette for the entire MNG, check to
13434       see if we can use one for two or more consecutive images.
13435     */
13436     if (need_local_plte && use_global_plte && !all_images_are_gray)
13437       {
13438         if (mng_info->IsPalette)
13439           {
13440             /*
13441               When equal_palettes is true, this image has the same palette
13442               as the previous PseudoClass image
13443             */
13444             mng_info->have_write_global_plte=mng_info->equal_palettes;
13445             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13446             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13447               {
13448                 /*
13449                   Write MNG PLTE chunk
13450                 */
13451                 size_t
13452                   data_length;
13453
13454                 data_length=3*image->colors;
13455                 (void) WriteBlobMSBULong(image,data_length);
13456                 PNGType(chunk,mng_PLTE);
13457                 LogPNGChunk(logging,mng_PLTE,data_length);
13458
13459                 for (i=0; i < (ssize_t) image->colors; i++)
13460                 {
13461                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13462                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13463                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13464                 }
13465
13466                 (void) WriteBlob(image,data_length+4,chunk);
13467                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13468                    (uInt) (data_length+4)));
13469                 mng_info->have_write_global_plte=MagickTrue;
13470               }
13471           }
13472         else
13473           mng_info->have_write_global_plte=MagickFalse;
13474       }
13475 #endif
13476     if (need_defi)
13477       {
13478         ssize_t
13479           previous_x,
13480           previous_y;
13481
13482         if (scene)
13483           {
13484             previous_x=mng_info->page.x;
13485             previous_y=mng_info->page.y;
13486           }
13487         else
13488           {
13489             previous_x=0;
13490             previous_y=0;
13491           }
13492         mng_info->page=image->page;
13493         if ((mng_info->page.x !=  previous_x) ||
13494             (mng_info->page.y != previous_y))
13495           {
13496              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
13497              PNGType(chunk,mng_DEFI);
13498              LogPNGChunk(logging,mng_DEFI,12L);
13499              chunk[4]=0; /* object 0 MSB */
13500              chunk[5]=0; /* object 0 LSB */
13501              chunk[6]=0; /* visible  */
13502              chunk[7]=0; /* abstract */
13503              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13504              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13505              (void) WriteBlob(image,16,chunk);
13506              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13507           }
13508       }
13509     }
13510
13511    mng_info->write_mng=write_mng;
13512
13513    if ((int) image->dispose >= 3)
13514      mng_info->framing_mode=3;
13515
13516    if (mng_info->need_fram && mng_info->adjoin &&
13517        ((image->delay != mng_info->delay) ||
13518         (mng_info->framing_mode != mng_info->old_framing_mode)))
13519      {
13520        if (image->delay == mng_info->delay)
13521          {
13522            /*
13523              Write a MNG FRAM chunk with the new framing mode.
13524            */
13525            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
13526            PNGType(chunk,mng_FRAM);
13527            LogPNGChunk(logging,mng_FRAM,1L);
13528            chunk[4]=(unsigned char) mng_info->framing_mode;
13529            (void) WriteBlob(image,5,chunk);
13530            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13531          }
13532        else
13533          {
13534            /*
13535              Write a MNG FRAM chunk with the delay.
13536            */
13537            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13538            PNGType(chunk,mng_FRAM);
13539            LogPNGChunk(logging,mng_FRAM,10L);
13540            chunk[4]=(unsigned char) mng_info->framing_mode;
13541            chunk[5]=0;  /* frame name separator (no name) */
13542            chunk[6]=2;  /* flag for changing default delay */
13543            chunk[7]=0;  /* flag for changing frame timeout */
13544            chunk[8]=0;  /* flag for changing frame clipping */
13545            chunk[9]=0;  /* flag for changing frame sync_id */
13546            PNGLong(chunk+10,(png_uint_32)
13547              ((mng_info->ticks_per_second*
13548              image->delay)/MagickMax(image->ticks_per_second,1)));
13549            (void) WriteBlob(image,14,chunk);
13550            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13551            mng_info->delay=(png_uint_32) image->delay;
13552          }
13553        mng_info->old_framing_mode=mng_info->framing_mode;
13554      }
13555
13556 #if defined(JNG_SUPPORTED)
13557    if (image_info->compression == JPEGCompression)
13558      {
13559        ImageInfo
13560          *write_info;
13561
13562        if (logging != MagickFalse)
13563          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13564            "  Writing JNG object.");
13565        /* To do: specify the desired alpha compression method. */
13566        write_info=CloneImageInfo(image_info);
13567        write_info->compression=UndefinedCompression;
13568        status=WriteOneJNGImage(mng_info,write_info,image,exception);
13569        write_info=DestroyImageInfo(write_info);
13570      }
13571    else
13572 #endif
13573      {
13574        if (logging != MagickFalse)
13575          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13576            "  Writing PNG object.");
13577
13578        mng_info->need_blob = MagickFalse;
13579        mng_info->ping_preserve_colormap = MagickFalse;
13580
13581        /* We don't want any ancillary chunks written */
13582        mng_info->ping_exclude_bKGD=MagickTrue;
13583        mng_info->ping_exclude_cHRM=MagickTrue;
13584        mng_info->ping_exclude_date=MagickTrue;
13585        mng_info->ping_exclude_EXIF=MagickTrue;
13586        mng_info->ping_exclude_gAMA=MagickTrue;
13587        mng_info->ping_exclude_iCCP=MagickTrue;
13588        /* mng_info->ping_exclude_iTXt=MagickTrue; */
13589        mng_info->ping_exclude_oFFs=MagickTrue;
13590        mng_info->ping_exclude_pHYs=MagickTrue;
13591        mng_info->ping_exclude_sRGB=MagickTrue;
13592        mng_info->ping_exclude_tEXt=MagickTrue;
13593        mng_info->ping_exclude_tRNS=MagickTrue;
13594        mng_info->ping_exclude_vpAg=MagickTrue;
13595        mng_info->ping_exclude_zCCP=MagickTrue;
13596        mng_info->ping_exclude_zTXt=MagickTrue;
13597
13598        status=WriteOnePNGImage(mng_info,image_info,image,exception);
13599      }
13600
13601     if (status == MagickFalse)
13602       {
13603         MngInfoFreeStruct(mng_info,&have_mng_structure);
13604         (void) CloseBlob(image);
13605         return(MagickFalse);
13606       }
13607     (void) CatchImageException(image);
13608     if (GetNextImageInList(image) == (Image *) NULL)
13609       break;
13610     image=SyncNextImageInList(image);
13611     status=SetImageProgress(image,SaveImagesTag,scene++,
13612       GetImageListLength(image));
13613
13614     if (status == MagickFalse)
13615       break;
13616
13617   } while (mng_info->adjoin);
13618
13619   if (write_mng)
13620     {
13621       while (GetPreviousImageInList(image) != (Image *) NULL)
13622         image=GetPreviousImageInList(image);
13623       /*
13624         Write the MEND chunk.
13625       */
13626       (void) WriteBlobMSBULong(image,0x00000000L);
13627       PNGType(chunk,mng_MEND);
13628       LogPNGChunk(logging,mng_MEND,0L);
13629       (void) WriteBlob(image,4,chunk);
13630       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13631     }
13632   /*
13633     Relinquish resources.
13634   */
13635   (void) CloseBlob(image);
13636   MngInfoFreeStruct(mng_info,&have_mng_structure);
13637
13638   if (logging != MagickFalse)
13639     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13640
13641   return(MagickTrue);
13642 }
13643 #else /* PNG_LIBPNG_VER > 10011 */
13644
13645 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13646 {
13647   (void) image;
13648   printf("Your PNG library is too old: You have libpng-%s\n",
13649      PNG_LIBPNG_VER_STRING);
13650
13651   ThrowBinaryException(CoderError,"PNG library is too old",
13652      image_info->filename);
13653 }
13654
13655 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13656 {
13657   return(WritePNGImage(image_info,image));
13658 }
13659 #endif /* PNG_LIBPNG_VER > 10011 */
13660 #endif