]> granicus.if.org Git - imagemagick/blob - coders/png.c
595c4c3ddde06cb5f859147b9d8119354a2b8af3
[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-2017 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 %    https://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 #define IM
41
42 \f
43 /*
44   Include declarations.
45 */
46 #include "MagickCore/studio.h"
47 #include "MagickCore/artifact.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/blob.h"
50 #include "MagickCore/blob-private.h"
51 #include "MagickCore/cache.h"
52 #include "MagickCore/channel.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colormap.h"
56 #include "MagickCore/colorspace.h"
57 #include "MagickCore/colorspace-private.h"
58 #include "MagickCore/constitute.h"
59 #include "MagickCore/enhance.h"
60 #include "MagickCore/exception.h"
61 #include "MagickCore/exception-private.h"
62 #include "MagickCore/geometry.h"
63 #include "MagickCore/histogram.h"
64 #include "MagickCore/image.h"
65 #include "MagickCore/image-private.h"
66 #include "MagickCore/layer.h"
67 #include "MagickCore/list.h"
68 #include "MagickCore/log.h"
69 #include "MagickCore/MagickCore.h"
70 #include "MagickCore/memory_.h"
71 #include "MagickCore/module.h"
72 #include "MagickCore/monitor.h"
73 #include "MagickCore/monitor-private.h"
74 #include "MagickCore/option.h"
75 #include "MagickCore/pixel.h"
76 #include "MagickCore/pixel-accessor.h"
77 #include "MagickCore/profile.h"
78 #include "MagickCore/property.h"
79 #include "MagickCore/quantum-private.h"
80 #include "MagickCore/resource_.h"
81 #include "MagickCore/semaphore.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/static.h"
84 #include "MagickCore/statistic.h"
85 #include "MagickCore/string_.h"
86 #include "MagickCore/string-private.h"
87 #include "MagickCore/transform.h"
88 #include "MagickCore/utility.h"
89 #if defined(MAGICKCORE_PNG_DELEGATE)
90
91 /* Suppress libpng pedantic warnings that were added in
92  * libpng-1.2.41 and libpng-1.4.0.  If you are working on
93  * migration to libpng-1.5, remove these defines and then
94  * fix any code that generates warnings.
95  */
96 /* #define PNG_DEPRECATED   Use of this function is deprecated */
97 /* #define PNG_USE_RESULT   The result of this function must be checked */
98 /* #define PNG_NORETURN     This function does not return */
99 /* #define PNG_ALLOCATED    The result of the function is new memory */
100 /* #define PNG_DEPSTRUCT    Access to this struct member is deprecated */
101
102 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
103 #define PNG_PTR_NORETURN
104
105 #include "png.h"
106 #include "zlib.h"
107 \f
108 /* ImageMagick differences */
109 #define first_scene scene
110
111 #if PNG_LIBPNG_VER > 10011
112 /*
113   Optional declarations. Define or undefine them as you like.
114 */
115 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
116
117 /*
118   Features under construction.  Define these to work on them.
119 */
120 #undef MNG_OBJECT_BUFFERS
121 #undef MNG_BASI_SUPPORTED
122 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
123 #define MNG_INSERT_LAYERS   /* Troublesome, but seem to work as of 5.4.4 */
124 #if defined(MAGICKCORE_JPEG_DELEGATE)
125 #  define JNG_SUPPORTED /* Not finished as of 5.5.2.  See "To do" comments. */
126 #endif
127 #if !defined(RGBColorMatchExact)
128 #define IsPNGColorEqual(color,target) \
129        (((color).red == (target).red) && \
130         ((color).green == (target).green) && \
131         ((color).blue == (target).blue))
132 #endif
133
134 /* Table of recognized sRGB ICC profiles */
135 struct sRGB_info_struct
136 {
137     png_uint_32 len;
138     png_uint_32 crc;
139     png_byte intent;
140 };
141
142 const struct sRGB_info_struct sRGB_info[] =
143 {
144     /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
145     { 3048, 0x3b8772b9UL, 0},
146
147     /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
148     { 3052, 0x427ebb21UL, 1},
149
150     /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
151     {60988, 0x306fd8aeUL, 0},
152
153     /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
154      {60960, 0xbbef7812UL, 0},
155
156     /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
157      { 3024, 0x5d5129ceUL, 1},
158
159      /* HP-Microsoft sRGB v2 perceptual */
160      { 3144, 0x182ea552UL, 0},
161
162      /* HP-Microsoft sRGB v2 media-relative */
163      { 3144, 0xf29e526dUL, 1},
164
165      /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
166      {  524, 0xd4938c39UL, 0},
167
168      /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
169      { 3212, 0x034af5a1UL, 0},
170
171      /* Not recognized */
172      {    0, 0x00000000UL, 0},
173 };
174
175 /* Macros for left-bit-replication to ensure that pixels
176  * and PixelInfos all have the same image->depth, and for use
177  * in PNG8 quantization.
178  */
179
180 /* LBR01: Replicate top bit */
181
182 #define LBR01PacketRed(pixelpacket) \
183      (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
184         0 : QuantumRange);
185
186 #define LBR01PacketGreen(pixelpacket) \
187      (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
188         0 : QuantumRange);
189
190 #define LBR01PacketBlue(pixelpacket) \
191      (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
192         0 : QuantumRange);
193
194 #define LBR01PacketAlpha(pixelpacket) \
195      (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
196         0 : QuantumRange);
197
198 #define LBR01PacketRGB(pixelpacket) \
199         { \
200         LBR01PacketRed((pixelpacket)); \
201         LBR01PacketGreen((pixelpacket)); \
202         LBR01PacketBlue((pixelpacket)); \
203         }
204
205 #define LBR01PacketRGBO(pixelpacket) \
206         { \
207         LBR01PacketRGB((pixelpacket)); \
208         LBR01PacketAlpha((pixelpacket)); \
209         }
210
211 #define LBR01PixelRed(pixel) \
212         (SetPixelRed(image, \
213         ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
214         0 : QuantumRange,(pixel)));
215
216 #define LBR01PixelGreen(pixel) \
217         (SetPixelGreen(image, \
218         ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
219         0 : QuantumRange,(pixel)));
220
221 #define LBR01PixelBlue(pixel) \
222         (SetPixelBlue(image, \
223         ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
224         0 : QuantumRange,(pixel)));
225
226 #define LBR01PixelAlpha(pixel) \
227         (SetPixelAlpha(image, \
228         ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
229         0 : QuantumRange,(pixel)));
230
231 #define LBR01PixelRGB(pixel) \
232         { \
233         LBR01PixelRed((pixel)); \
234         LBR01PixelGreen((pixel)); \
235         LBR01PixelBlue((pixel)); \
236         }
237
238 #define LBR01PixelRGBA(pixel) \
239         { \
240         LBR01PixelRGB((pixel)); \
241         LBR01PixelAlpha((pixel)); \
242         }
243
244 /* LBR02: Replicate top 2 bits */
245
246 #define LBR02PacketRed(pixelpacket) \
247    { \
248      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
249      (pixelpacket).red=ScaleCharToQuantum( \
250        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
251    }
252 #define LBR02PacketGreen(pixelpacket) \
253    { \
254      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
255      (pixelpacket).green=ScaleCharToQuantum( \
256        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
257    }
258 #define LBR02PacketBlue(pixelpacket) \
259    { \
260      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
261      (pixelpacket).blue=ScaleCharToQuantum( \
262        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
263    }
264 #define LBR02PacketAlpha(pixelpacket) \
265    { \
266      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
267      (pixelpacket).alpha=ScaleCharToQuantum( \
268        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
269    }
270
271 #define LBR02PacketRGB(pixelpacket) \
272         { \
273         LBR02PacketRed((pixelpacket)); \
274         LBR02PacketGreen((pixelpacket)); \
275         LBR02PacketBlue((pixelpacket)); \
276         }
277
278 #define LBR02PacketRGBO(pixelpacket) \
279         { \
280         LBR02PacketRGB((pixelpacket)); \
281         LBR02PacketAlpha((pixelpacket)); \
282         }
283
284 #define LBR02PixelRed(pixel) \
285    { \
286      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
287        & 0xc0; \
288      SetPixelRed(image, ScaleCharToQuantum( \
289        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
290        (pixel)); \
291    }
292 #define LBR02PixelGreen(pixel) \
293    { \
294      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
295        & 0xc0; \
296      SetPixelGreen(image, ScaleCharToQuantum( \
297        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
298        (pixel)); \
299    }
300 #define LBR02PixelBlue(pixel) \
301    { \
302      unsigned char lbr_bits= \
303        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
304      SetPixelBlue(image, ScaleCharToQuantum( \
305        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
306        (pixel)); \
307    }
308 #define LBR02PixelAlpha(pixel) \
309    { \
310      unsigned char lbr_bits= \
311        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
312      SetPixelAlpha(image, ScaleCharToQuantum( \
313        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
314        (pixel) ); \
315    }
316
317 #define LBR02PixelRGB(pixel) \
318         { \
319         LBR02PixelRed((pixel)); \
320         LBR02PixelGreen((pixel)); \
321         LBR02PixelBlue((pixel)); \
322         }
323
324 #define LBR02PixelRGBA(pixel) \
325         { \
326         LBR02PixelRGB((pixel)); \
327         LBR02PixelAlpha((pixel)); \
328         }
329
330 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
331    PNG8 quantization) */
332
333 #define LBR03PacketRed(pixelpacket) \
334    { \
335      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
336      (pixelpacket).red=ScaleCharToQuantum( \
337        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
338    }
339 #define LBR03PacketGreen(pixelpacket) \
340    { \
341      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
342      (pixelpacket).green=ScaleCharToQuantum( \
343        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
344    }
345 #define LBR03PacketBlue(pixelpacket) \
346    { \
347      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
348      (pixelpacket).blue=ScaleCharToQuantum( \
349        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
350    }
351
352 #define LBR03PacketRGB(pixelpacket) \
353         { \
354         LBR03PacketRed((pixelpacket)); \
355         LBR03PacketGreen((pixelpacket)); \
356         LBR03PacketBlue((pixelpacket)); \
357         }
358
359 #define LBR03PixelRed(pixel) \
360    { \
361      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
362        & 0xe0; \
363      SetPixelRed(image, ScaleCharToQuantum( \
364        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
365    }
366 #define LBR03Green(pixel) \
367    { \
368      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
369        & 0xe0; \
370      SetPixelGreen(image, ScaleCharToQuantum( \
371        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
372    }
373 #define LBR03Blue(pixel) \
374    { \
375      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
376        & 0xe0; \
377      SetPixelBlue(image, ScaleCharToQuantum( \
378        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
379    }
380
381 #define LBR03RGB(pixel) \
382         { \
383         LBR03PixelRed((pixel)); \
384         LBR03Green((pixel)); \
385         LBR03Blue((pixel)); \
386         }
387
388 /* LBR04: Replicate top 4 bits */
389
390 #define LBR04PacketRed(pixelpacket) \
391    { \
392      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
393      (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
394    }
395 #define LBR04PacketGreen(pixelpacket) \
396    { \
397      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
398      (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
399    }
400 #define LBR04PacketBlue(pixelpacket) \
401    { \
402      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
403      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
404    }
405 #define LBR04PacketAlpha(pixelpacket) \
406    { \
407      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
408      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
409    }
410
411 #define LBR04PacketRGB(pixelpacket) \
412         { \
413         LBR04PacketRed((pixelpacket)); \
414         LBR04PacketGreen((pixelpacket)); \
415         LBR04PacketBlue((pixelpacket)); \
416         }
417
418 #define LBR04PacketRGBO(pixelpacket) \
419         { \
420         LBR04PacketRGB((pixelpacket)); \
421         LBR04PacketAlpha((pixelpacket)); \
422         }
423
424 #define LBR04PixelRed(pixel) \
425    { \
426      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
427        & 0xf0; \
428      SetPixelRed(image,\
429        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
430    }
431 #define LBR04PixelGreen(pixel) \
432    { \
433      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
434        & 0xf0; \
435      SetPixelGreen(image,\
436        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
437    }
438 #define LBR04PixelBlue(pixel) \
439    { \
440      unsigned char lbr_bits= \
441        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
442      SetPixelBlue(image,\
443        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
444    }
445 #define LBR04PixelAlpha(pixel) \
446    { \
447      unsigned char lbr_bits= \
448        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
449      SetPixelAlpha(image,\
450        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
451    }
452
453 #define LBR04PixelRGB(pixel) \
454         { \
455         LBR04PixelRed((pixel)); \
456         LBR04PixelGreen((pixel)); \
457         LBR04PixelBlue((pixel)); \
458         }
459
460 #define LBR04PixelRGBA(pixel) \
461         { \
462         LBR04PixelRGB((pixel)); \
463         LBR04PixelAlpha((pixel)); \
464         }
465
466 /*
467   Establish thread safety.
468   setjmp/longjmp is claimed to be safe on these platforms:
469   setjmp/longjmp is alleged to be unsafe on these platforms:
470 */
471 #ifdef PNG_SETJMP_SUPPORTED
472 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
473 #   define IMPNG_SETJMP_NOT_THREAD_SAFE
474 # endif
475
476 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
477 static SemaphoreInfo
478   *ping_semaphore = (SemaphoreInfo *) NULL;
479 # endif
480 #endif
481
482 /*
483   This temporary until I set up malloc'ed object attributes array.
484   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
485   waste more memory.
486 */
487 #define MNG_MAX_OBJECTS 256
488
489 /*
490   If this not defined, spec is interpreted strictly.  If it is
491   defined, an attempt will be made to recover from some errors,
492   including
493       o global PLTE too short
494 */
495 #undef MNG_LOOSE
496
497 /*
498   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
499   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
500   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
501   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
502   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
503   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
504   will be enabled by default in libpng-1.2.0.
505 */
506 #ifdef PNG_MNG_FEATURES_SUPPORTED
507 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
508 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
509 #  endif
510 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
511 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
512 #  endif
513 #endif
514
515 /*
516   Maximum valid size_t in PNG/MNG chunks is (2^31)-1
517   This macro is only defined in libpng-1.0.3 and later.
518   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
519 */
520 #ifndef PNG_UINT_31_MAX
521 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
522 #endif
523
524 /*
525   Constant strings for known chunk types.  If you need to add a chunk,
526   add a string holding the name here.   To make the code more
527   portable, we use ASCII numbers like this, not characters.
528 */
529
530 static const png_byte mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
531 static const png_byte mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
532 static const png_byte mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
533 static const png_byte mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
534 static const png_byte mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
535 static const png_byte mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
536 static const png_byte mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
537 static const png_byte mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
538 static const png_byte mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
539 static const png_byte mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
540 static const png_byte mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
541 static const png_byte mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
542 static const png_byte mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
543 static const png_byte mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
544 static const png_byte mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
545 static const png_byte mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
546 static const png_byte mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
547 static const png_byte mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
548 static const png_byte mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
549 static const png_byte mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
550 static const png_byte mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
551 static const png_byte mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
552 static const png_byte mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
553 static const png_byte mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
554 static const png_byte mng_caNv[5]={ 99,  97,  78, 118, (png_byte) '\0'};
555 static const png_byte mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
556 static const png_byte mng_eXIf[5]={101,  88,  73, 102, (png_byte) '\0'};
557 static const png_byte mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
558 static const png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
559 static const png_byte mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
560 static const png_byte mng_orNT[5]={111, 114,  78,  84, (png_byte) '\0'};
561 static const png_byte mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
562 static const png_byte mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
563 static const png_byte mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
564 static const png_byte mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
565 static const png_byte mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
566 static const png_byte mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
567
568 #if defined(JNG_SUPPORTED)
569 static const png_byte mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
570 static const png_byte mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
571 static const png_byte mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
572 static const png_byte mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
573 static const png_byte mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
574 static const png_byte mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
575 #endif
576
577 #if 0
578 /* Other known chunks that are not yet supported by ImageMagick: */
579 static const png_byte mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
580 static const png_byte mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
581 static const png_byte mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
582 static const png_byte mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
583 static const png_byte mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
584 static const png_byte mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
585 static const png_byte mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
586 #endif
587
588 typedef struct _MngBox
589 {
590   long
591     left,
592     right,
593     top,
594     bottom;
595 } MngBox;
596
597 typedef struct _MngPair
598 {
599   volatile long
600     a,
601     b;
602 } MngPair;
603
604 #ifdef MNG_OBJECT_BUFFERS
605 typedef struct _MngBuffer
606 {
607
608   size_t
609     height,
610     width;
611
612   Image
613     *image;
614
615   png_color
616     plte[256];
617
618   int
619     reference_count;
620
621   unsigned char
622     alpha_sample_depth,
623     compression_method,
624     color_type,
625     concrete,
626     filter_method,
627     frozen,
628     image_type,
629     interlace_method,
630     pixel_sample_depth,
631     plte_length,
632     sample_depth,
633     viewable;
634 } MngBuffer;
635 #endif
636
637 typedef struct _MngInfo
638 {
639
640 #ifdef MNG_OBJECT_BUFFERS
641   MngBuffer
642     *ob[MNG_MAX_OBJECTS];
643 #endif
644
645   Image *
646     image;
647
648   RectangleInfo
649     page;
650
651   int
652     adjoin,
653 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
654     bytes_in_read_buffer,
655     found_empty_plte,
656 #endif
657     equal_backgrounds,
658     equal_chrms,
659     equal_gammas,
660 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
661     defined(PNG_MNG_FEATURES_SUPPORTED)
662     equal_palettes,
663 #endif
664     equal_physs,
665     equal_srgbs,
666     framing_mode,
667     have_global_bkgd,
668     have_global_chrm,
669     have_global_gama,
670     have_global_phys,
671     have_global_sbit,
672     have_global_srgb,
673     have_saved_bkgd_index,
674     have_write_global_chrm,
675     have_write_global_gama,
676     have_write_global_plte,
677     have_write_global_srgb,
678     need_fram,
679     object_id,
680     old_framing_mode,
681     saved_bkgd_index;
682
683   int
684     new_number_colors;
685
686   ssize_t
687     image_found,
688     loop_count[256],
689     loop_iteration[256],
690     scenes_found,
691     x_off[MNG_MAX_OBJECTS],
692     y_off[MNG_MAX_OBJECTS];
693
694   MngBox
695     clip,
696     frame,
697     image_box,
698     object_clip[MNG_MAX_OBJECTS];
699
700   unsigned char
701     /* These flags could be combined into one byte */
702     exists[MNG_MAX_OBJECTS],
703     frozen[MNG_MAX_OBJECTS],
704     loop_active[256],
705     invisible[MNG_MAX_OBJECTS],
706     viewable[MNG_MAX_OBJECTS];
707
708   MagickOffsetType
709     loop_jump[256];
710
711   png_colorp
712     global_plte;
713
714   png_color_8
715     global_sbit;
716
717   png_byte
718 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
719     read_buffer[8],
720 #endif
721     global_trns[256];
722
723   float
724     global_gamma;
725
726   ChromaticityInfo
727     global_chrm;
728
729   RenderingIntent
730     global_srgb_intent;
731
732   unsigned long
733     delay,
734     global_plte_length,
735     global_trns_length,
736     global_x_pixels_per_unit,
737     global_y_pixels_per_unit,
738     mng_width,
739     mng_height,
740     ticks_per_second;
741
742   MagickBooleanType
743     need_blob;
744
745   unsigned int
746     IsPalette,
747     global_phys_unit_type,
748     basi_warning,
749     clon_warning,
750     dhdr_warning,
751     jhdr_warning,
752     magn_warning,
753     past_warning,
754     phyg_warning,
755     phys_warning,
756     sbit_warning,
757     show_warning,
758     mng_type,
759     write_mng,
760     write_png_colortype,
761     write_png_depth,
762     write_png_compression_level,
763     write_png_compression_strategy,
764     write_png_compression_filter,
765     write_png8,
766     write_png24,
767     write_png32,
768     write_png48,
769     write_png64;
770
771 #ifdef MNG_BASI_SUPPORTED
772   unsigned long
773     basi_width,
774     basi_height;
775
776   unsigned int
777     basi_depth,
778     basi_color_type,
779     basi_compression_method,
780     basi_filter_type,
781     basi_interlace_method,
782     basi_red,
783     basi_green,
784     basi_blue,
785     basi_alpha,
786     basi_viewable;
787 #endif
788
789   png_uint_16
790     magn_first,
791     magn_last,
792     magn_mb,
793     magn_ml,
794     magn_mr,
795     magn_mt,
796     magn_mx,
797     magn_my,
798     magn_methx,
799     magn_methy;
800
801   PixelInfo
802     mng_global_bkgd;
803
804   /* Added at version 6.6.6-7 */
805   MagickBooleanType
806     ping_exclude_bKGD,
807     ping_exclude_cHRM,
808     ping_exclude_date,
809     ping_exclude_eXIf,
810     ping_exclude_EXIF,
811     ping_exclude_gAMA,
812     ping_exclude_iCCP,
813     /* ping_exclude_iTXt, */
814     ping_exclude_oFFs,
815     ping_exclude_pHYs,
816     ping_exclude_sRGB,
817     ping_exclude_tEXt,
818     ping_exclude_tRNS,
819     ping_exclude_caNv,
820     ping_exclude_zCCP, /* hex-encoded iCCP */
821     ping_exclude_zTXt,
822     ping_preserve_colormap,
823   /* Added at version 6.8.5-7 */
824     ping_preserve_iCCP,
825   /* Added at version 6.8.9-9 */
826     ping_exclude_tIME;
827
828 } MngInfo;
829 #endif /* VER */
830 \f
831 /*
832   Forward declarations.
833 */
834 static MagickBooleanType
835   WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
836
837 static MagickBooleanType
838   WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
839
840 #if defined(JNG_SUPPORTED)
841 static MagickBooleanType
842   WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
843 #endif
844
845 #if PNG_LIBPNG_VER > 10011
846
847
848 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
849 static MagickBooleanType
850 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
851 {
852     /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
853      *
854      * This is true if the high byte and the next highest byte of
855      * each sample of the image, the colormap, and the background color
856      * are equal to each other.  We check this by seeing if the samples
857      * are unchanged when we scale them down to 8 and back up to Quantum.
858      *
859      * We don't use the method GetImageDepth() because it doesn't check
860      * background and doesn't handle PseudoClass specially.
861      */
862
863 #define QuantumToCharToQuantumEqQuantum(quantum) \
864  ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
865
866     MagickBooleanType
867       ok_to_reduce=MagickFalse;
868
869     if (image->depth >= 16)
870       {
871
872         const Quantum
873           *p;
874
875         ok_to_reduce=
876            QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
877            QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
878            QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
879            MagickTrue : MagickFalse;
880
881         if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
882           {
883             int indx;
884
885             for (indx=0; indx < (ssize_t) image->colors; indx++)
886               {
887                 ok_to_reduce=(
888                    QuantumToCharToQuantumEqQuantum(
889                    image->colormap[indx].red) &&
890                    QuantumToCharToQuantumEqQuantum(
891                    image->colormap[indx].green) &&
892                    QuantumToCharToQuantumEqQuantum(
893                    image->colormap[indx].blue)) ?
894                    MagickTrue : MagickFalse;
895
896                 if (ok_to_reduce == MagickFalse)
897                    break;
898               }
899           }
900
901         if ((ok_to_reduce != MagickFalse) &&
902             (image->storage_class != PseudoClass))
903           {
904             ssize_t
905               y;
906
907             register ssize_t
908               x;
909
910             for (y=0; y < (ssize_t) image->rows; y++)
911             {
912               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
913
914               if (p == (const Quantum *) NULL)
915                 {
916                   ok_to_reduce = MagickFalse;
917                   break;
918                 }
919
920               for (x=(ssize_t) image->columns-1; x >= 0; x--)
921               {
922                 ok_to_reduce=
923                    QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
924                    QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
925                    QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
926                    MagickTrue : MagickFalse;
927
928                 if (ok_to_reduce == MagickFalse)
929                   break;
930
931                 p+=GetPixelChannels(image);
932               }
933               if (x >= 0)
934                 break;
935             }
936           }
937
938         if (ok_to_reduce != MagickFalse)
939           {
940             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
941                 "    OK to reduce PNG bit depth to 8 without loss of info");
942           }
943         else
944           {
945             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
946                 "    Not OK to reduce PNG bit depth to 8 without losing info");
947           }
948       }
949
950     return ok_to_reduce;
951 }
952 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
953
954 static const char* PngColorTypeToString(const unsigned int color_type)
955 {
956   const char
957     *result = "Unknown";
958
959   switch (color_type)
960     {
961     case PNG_COLOR_TYPE_GRAY:
962       result = "Gray";
963       break;
964     case PNG_COLOR_TYPE_GRAY_ALPHA:
965       result = "Gray+Alpha";
966       break;
967     case PNG_COLOR_TYPE_PALETTE:
968       result = "Palette";
969       break;
970     case PNG_COLOR_TYPE_RGB:
971       result = "RGB";
972       break;
973     case PNG_COLOR_TYPE_RGB_ALPHA:
974       result = "RGB+Alpha";
975       break;
976     }
977
978   return result;
979 }
980
981 static int
982 Magick_Orientation_to_Exif_Orientation(const OrientationType orientation)
983 {
984   switch (orientation)
985   {
986     /* Convert to Exif orientations as defined in "Exchangeable image file
987      * format for digital still cameras: Exif Version 2.31",
988      * http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
989      */
990
991     case TopLeftOrientation:
992        return 1;
993     case TopRightOrientation:
994        return 2;
995     case BottomRightOrientation:
996        return 3;
997     case BottomLeftOrientation:
998        return 4;
999     case LeftTopOrientation:
1000        return 5;
1001     case RightTopOrientation:
1002        return 6;
1003     case RightBottomOrientation:
1004        return 7;
1005     case LeftBottomOrientation:
1006        return 8;
1007     case UndefinedOrientation:
1008     default:
1009        return 0;
1010   }
1011 }
1012 static OrientationType
1013 Magick_Orientation_from_Exif_Orientation(const int orientation)
1014 {
1015   switch (orientation)
1016   {
1017     case 1:
1018       return TopLeftOrientation;
1019     case 2:
1020       return TopRightOrientation;
1021     case 3:
1022       return BottomRightOrientation;
1023     case 4:
1024       return BottomLeftOrientation;
1025     case 5:
1026       return LeftTopOrientation;
1027     case 6:
1028       return RightTopOrientation;
1029     case 7:
1030       return RightBottomOrientation;
1031     case 8:
1032       return LeftBottomOrientation;
1033     case 0:
1034     default:
1035       return UndefinedOrientation;
1036   }
1037 }
1038
1039 static int
1040 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1041 {
1042   switch (intent)
1043   {
1044     case PerceptualIntent:
1045        return 0;
1046
1047     case RelativeIntent:
1048        return 1;
1049
1050     case SaturationIntent:
1051        return 2;
1052
1053     case AbsoluteIntent:
1054        return 3;
1055
1056     default:
1057        return -1;
1058   }
1059 }
1060
1061 static RenderingIntent
1062 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1063 {
1064   switch (ping_intent)
1065   {
1066     case 0:
1067       return PerceptualIntent;
1068
1069     case 1:
1070       return RelativeIntent;
1071
1072     case 2:
1073       return SaturationIntent;
1074
1075     case 3:
1076       return AbsoluteIntent;
1077
1078     default:
1079       return UndefinedIntent;
1080     }
1081 }
1082
1083 static const char *
1084 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1085 {
1086   switch (ping_intent)
1087   {
1088     case 0:
1089       return "Perceptual Intent";
1090
1091     case 1:
1092       return "Relative Intent";
1093
1094     case 2:
1095       return "Saturation Intent";
1096
1097     case 3:
1098       return "Absolute Intent";
1099
1100     default:
1101       return "Undefined Intent";
1102     }
1103 }
1104
1105 static const char *
1106 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1107 {
1108   switch (ping_colortype)
1109   {
1110     case 0:
1111       return "Grayscale";
1112
1113     case 2:
1114       return "Truecolor";
1115
1116     case 3:
1117       return "Indexed";
1118
1119     case 4:
1120       return "GrayAlpha";
1121
1122     case 6:
1123       return "RGBA";
1124
1125     default:
1126       return "UndefinedColorType";
1127     }
1128 }
1129
1130 #endif /* PNG_LIBPNG_VER > 10011 */
1131 #endif /* MAGICKCORE_PNG_DELEGATE */
1132 \f
1133 /*
1134 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1135 %                                                                             %
1136 %                                                                             %
1137 %                                                                             %
1138 %   I s M N G                                                                 %
1139 %                                                                             %
1140 %                                                                             %
1141 %                                                                             %
1142 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1143 %
1144 %  IsMNG() returns MagickTrue if the image format type, identified by the
1145 %  magick string, is MNG.
1146 %
1147 %  The format of the IsMNG method is:
1148 %
1149 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1150 %
1151 %  A description of each parameter follows:
1152 %
1153 %    o magick: compare image format pattern against these bytes.
1154 %
1155 %    o length: Specifies the length of the magick string.
1156 %
1157 %
1158 */
1159 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1160 {
1161   if (length < 8)
1162     return(MagickFalse);
1163
1164   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1165     return(MagickTrue);
1166
1167   return(MagickFalse);
1168 }
1169 \f
1170 /*
1171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1172 %                                                                             %
1173 %                                                                             %
1174 %                                                                             %
1175 %   I s J N G                                                                 %
1176 %                                                                             %
1177 %                                                                             %
1178 %                                                                             %
1179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1180 %
1181 %  IsJNG() returns MagickTrue if the image format type, identified by the
1182 %  magick string, is JNG.
1183 %
1184 %  The format of the IsJNG method is:
1185 %
1186 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1187 %
1188 %  A description of each parameter follows:
1189 %
1190 %    o magick: compare image format pattern against these bytes.
1191 %
1192 %    o length: Specifies the length of the magick string.
1193 %
1194 %
1195 */
1196 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1197 {
1198   if (length < 8)
1199     return(MagickFalse);
1200
1201   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1202     return(MagickTrue);
1203
1204   return(MagickFalse);
1205 }
1206 \f
1207 /*
1208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1209 %                                                                             %
1210 %                                                                             %
1211 %                                                                             %
1212 %   I s P N G                                                                 %
1213 %                                                                             %
1214 %                                                                             %
1215 %                                                                             %
1216 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1217 %
1218 %  IsPNG() returns MagickTrue if the image format type, identified by the
1219 %  magick string, is PNG.
1220 %
1221 %  The format of the IsPNG method is:
1222 %
1223 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1224 %
1225 %  A description of each parameter follows:
1226 %
1227 %    o magick: compare image format pattern against these bytes.
1228 %
1229 %    o length: Specifies the length of the magick string.
1230 %
1231 */
1232 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1233 {
1234   if (length < 8)
1235     return(MagickFalse);
1236
1237   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1238     return(MagickTrue);
1239
1240   return(MagickFalse);
1241 }
1242 \f
1243 #if defined(MAGICKCORE_PNG_DELEGATE)
1244 #if defined(__cplusplus) || defined(c_plusplus)
1245 extern "C" {
1246 #endif
1247
1248 #if (PNG_LIBPNG_VER > 10011)
1249 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1250 {
1251   unsigned char
1252     buffer[4];
1253
1254   assert(image != (Image *) NULL);
1255   assert(image->signature == MagickCoreSignature);
1256   buffer[0]=(unsigned char) (value >> 24);
1257   buffer[1]=(unsigned char) (value >> 16);
1258   buffer[2]=(unsigned char) (value >> 8);
1259   buffer[3]=(unsigned char) value;
1260   return((size_t) WriteBlob(image,4,buffer));
1261 }
1262
1263 static void PNGLong(png_bytep p,png_uint_32 value)
1264 {
1265   *p++=(png_byte) ((value >> 24) & 0xff);
1266   *p++=(png_byte) ((value >> 16) & 0xff);
1267   *p++=(png_byte) ((value >> 8) & 0xff);
1268   *p++=(png_byte) (value & 0xff);
1269 }
1270
1271 static void PNGsLong(png_bytep p,png_int_32 value)
1272 {
1273   *p++=(png_byte) ((value >> 24) & 0xff);
1274   *p++=(png_byte) ((value >> 16) & 0xff);
1275   *p++=(png_byte) ((value >> 8) & 0xff);
1276   *p++=(png_byte) (value & 0xff);
1277 }
1278
1279 static void PNGShort(png_bytep p,png_uint_16 value)
1280 {
1281   *p++=(png_byte) ((value >> 8) & 0xff);
1282   *p++=(png_byte) (value & 0xff);
1283 }
1284
1285 static void PNGType(png_bytep p,const png_byte *type)
1286 {
1287   (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1288 }
1289
1290 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1291    size_t length)
1292 {
1293   if (logging != MagickFalse)
1294     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1295       "  Writing %c%c%c%c chunk, length: %.20g",
1296       type[0],type[1],type[2],type[3],(double) length);
1297 }
1298 #endif /* PNG_LIBPNG_VER > 10011 */
1299
1300 #if defined(__cplusplus) || defined(c_plusplus)
1301 }
1302 #endif
1303
1304 #if PNG_LIBPNG_VER > 10011
1305 /*
1306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1307 %                                                                             %
1308 %                                                                             %
1309 %                                                                             %
1310 %   R e a d P N G I m a g e                                                   %
1311 %                                                                             %
1312 %                                                                             %
1313 %                                                                             %
1314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1315 %
1316 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1317 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1318 %  allocates the memory necessary for the new Image structure and returns a
1319 %  pointer to the new image or set of images.
1320 %
1321 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1322 %
1323 %  The format of the ReadPNGImage method is:
1324 %
1325 %     Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1326 %
1327 %  A description of each parameter follows:
1328 %
1329 %    o image_info: the image info.
1330 %
1331 %    o exception: return any errors or warnings in this structure.
1332 %
1333 %  To do, more or less in chronological order (as of version 5.5.2,
1334 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1335 %
1336 %    Get 16-bit cheap transparency working.
1337 %
1338 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1339 %
1340 %    Preserve all unknown and not-yet-handled known chunks found in input
1341 %    PNG file and copy them into output PNG files according to the PNG
1342 %    copying rules.
1343 %
1344 %    (At this point, PNG encoding should be in full MNG compliance)
1345 %
1346 %    Provide options for choice of background to use when the MNG BACK
1347 %    chunk is not present or is not mandatory (i.e., leave transparent,
1348 %    user specified, MNG BACK, PNG bKGD)
1349 %
1350 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1351 %    efficiently by linking in the duplicate frames.].
1352 %
1353 %    Decode and act on the MHDR simplicity profile (offer option to reject
1354 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1355 %
1356 %    Upgrade to full MNG without Delta-PNG.
1357 %
1358 %        o  BACK [done a while ago except for background image ID]
1359 %        o  MOVE [done 15 May 1999]
1360 %        o  CLIP [done 15 May 1999]
1361 %        o  DISC [done 19 May 1999]
1362 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1363 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1364 %        o  SHOW
1365 %        o  PAST
1366 %        o  BASI
1367 %        o  MNG-level tEXt/iTXt/zTXt
1368 %        o  pHYg
1369 %        o  pHYs
1370 %        o  sBIT
1371 %        o  bKGD
1372 %        o  iTXt (wait for libpng implementation).
1373 %
1374 %    Use the scene signature to discover when an identical scene is
1375 %    being reused, and just point to the original image->exception instead
1376 %    of storing another set of pixels.  This not specific to MNG
1377 %    but could be applied generally.
1378 %
1379 %    Upgrade to full MNG with Delta-PNG.
1380 %
1381 %    JNG tEXt/iTXt/zTXt
1382 %
1383 %    We will not attempt to read files containing the CgBI chunk.
1384 %    They are really Xcode files meant for display on the iPhone.
1385 %    These are not valid PNG files and it is impossible to recover
1386 %    the original PNG from files that have been converted to Xcode-PNG,
1387 %    since irretrievable loss of color data has occurred due to the
1388 %    use of premultiplied alpha.
1389 */
1390
1391 #if defined(__cplusplus) || defined(c_plusplus)
1392 extern "C" {
1393 #endif
1394
1395 /*
1396   This the function that does the actual reading of data.  It is
1397   the same as the one supplied in libpng, except that it receives the
1398   datastream from the ReadBlob() function instead of standard input.
1399 */
1400 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1401 {
1402   Image
1403     *image;
1404
1405   image=(Image *) png_get_io_ptr(png_ptr);
1406   if (length != 0)
1407     {
1408       png_size_t
1409         check;
1410
1411       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1412       if (check != length)
1413         {
1414           char
1415             msg[MagickPathExtent];
1416
1417           (void) FormatLocaleString(msg,MagickPathExtent,
1418             "Expected %.20g bytes; found %.20g bytes",(double) length,
1419             (double) check);
1420           png_warning(png_ptr,msg);
1421           png_error(png_ptr,"Read Exception");
1422         }
1423     }
1424 }
1425
1426 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1427     !defined(PNG_MNG_FEATURES_SUPPORTED)
1428 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1429  * older than libpng-1.0.3a, which was the first to allow the empty
1430  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1431  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1432  * encountered after an empty PLTE, so we have to look ahead for bKGD
1433  * chunks and remove them from the datastream that is passed to libpng,
1434  * and store their contents for later use.
1435  */
1436 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1437 {
1438   MngInfo
1439     *mng_info;
1440
1441   Image
1442     *image;
1443
1444   png_size_t
1445     check;
1446
1447   register ssize_t
1448     i;
1449
1450   i=0;
1451   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1452   image=(Image *) mng_info->image;
1453   while (mng_info->bytes_in_read_buffer && length)
1454   {
1455     data[i]=mng_info->read_buffer[i];
1456     mng_info->bytes_in_read_buffer--;
1457     length--;
1458     i++;
1459   }
1460   if (length != 0)
1461     {
1462       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1463
1464       if (check != length)
1465         png_error(png_ptr,"Read Exception");
1466
1467       if (length == 4)
1468         {
1469           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1470               (data[3] == 0))
1471             {
1472               check=(png_size_t) ReadBlob(image,(size_t) length,
1473                 (char *) mng_info->read_buffer);
1474               mng_info->read_buffer[4]=0;
1475               mng_info->bytes_in_read_buffer=4;
1476               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1477                 mng_info->found_empty_plte=MagickTrue;
1478               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1479                 {
1480                   mng_info->found_empty_plte=MagickFalse;
1481                   mng_info->have_saved_bkgd_index=MagickFalse;
1482                 }
1483             }
1484
1485           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1486               (data[3] == 1))
1487             {
1488               check=(png_size_t) ReadBlob(image,(size_t) length,
1489                 (char *) mng_info->read_buffer);
1490               mng_info->read_buffer[4]=0;
1491               mng_info->bytes_in_read_buffer=4;
1492               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1493                 if (mng_info->found_empty_plte)
1494                   {
1495                     /*
1496                       Skip the bKGD data byte and CRC.
1497                     */
1498                     check=(png_size_t)
1499                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1500                     check=(png_size_t) ReadBlob(image,(size_t) length,
1501                       (char *) mng_info->read_buffer);
1502                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1503                     mng_info->have_saved_bkgd_index=MagickTrue;
1504                     mng_info->bytes_in_read_buffer=0;
1505                   }
1506             }
1507         }
1508     }
1509 }
1510 #endif
1511
1512 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1513 {
1514   Image
1515     *image;
1516
1517   image=(Image *) png_get_io_ptr(png_ptr);
1518   if (length != 0)
1519     {
1520       png_size_t
1521         check;
1522
1523       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1524
1525       if (check != length)
1526         png_error(png_ptr,"WriteBlob Failed");
1527     }
1528 }
1529
1530 static void png_flush_data(png_structp png_ptr)
1531 {
1532   (void) png_ptr;
1533 }
1534
1535 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1536 static int PalettesAreEqual(Image *a,Image *b)
1537 {
1538   ssize_t
1539     i;
1540
1541   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1542     return((int) MagickFalse);
1543
1544   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1545     return((int) MagickFalse);
1546
1547   if (a->colors != b->colors)
1548     return((int) MagickFalse);
1549
1550   for (i=0; i < (ssize_t) a->colors; i++)
1551   {
1552     if ((a->colormap[i].red != b->colormap[i].red) ||
1553         (a->colormap[i].green != b->colormap[i].green) ||
1554         (a->colormap[i].blue != b->colormap[i].blue))
1555       return((int) MagickFalse);
1556   }
1557
1558   return((int) MagickTrue);
1559 }
1560 #endif
1561
1562 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1563 {
1564   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1565       mng_info->exists[i] && !mng_info->frozen[i])
1566     {
1567 #ifdef MNG_OBJECT_BUFFERS
1568       if (mng_info->ob[i] != (MngBuffer *) NULL)
1569         {
1570           if (mng_info->ob[i]->reference_count > 0)
1571             mng_info->ob[i]->reference_count--;
1572
1573           if (mng_info->ob[i]->reference_count == 0)
1574             {
1575               if (mng_info->ob[i]->image != (Image *) NULL)
1576                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1577
1578               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1579             }
1580         }
1581       mng_info->ob[i]=(MngBuffer *) NULL;
1582 #endif
1583       mng_info->exists[i]=MagickFalse;
1584       mng_info->invisible[i]=MagickFalse;
1585       mng_info->viewable[i]=MagickFalse;
1586       mng_info->frozen[i]=MagickFalse;
1587       mng_info->x_off[i]=0;
1588       mng_info->y_off[i]=0;
1589       mng_info->object_clip[i].left=0;
1590       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1591       mng_info->object_clip[i].top=0;
1592       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1593     }
1594 }
1595
1596 static MngInfo *MngInfoFreeStruct(MngInfo *mng_info)
1597 {
1598   register ssize_t
1599     i;
1600
1601   if (mng_info == (MngInfo *) NULL)
1602     return((MngInfo *) NULL);
1603
1604   for (i=1; i < MNG_MAX_OBJECTS; i++)
1605     MngInfoDiscardObject(mng_info,i);
1606
1607   mng_info->global_plte=(png_colorp)
1608     RelinquishMagickMemory(mng_info->global_plte);
1609
1610   return((MngInfo *) RelinquishMagickMemory(mng_info));
1611 }
1612
1613 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1614 {
1615   MngBox
1616     box;
1617
1618   box=box1;
1619   if (box.left < box2.left)
1620     box.left=box2.left;
1621
1622   if (box.top < box2.top)
1623     box.top=box2.top;
1624
1625   if (box.right > box2.right)
1626     box.right=box2.right;
1627
1628   if (box.bottom > box2.bottom)
1629     box.bottom=box2.bottom;
1630
1631   return box;
1632 }
1633
1634 static long mng_get_long(unsigned char *p)
1635 {
1636   return ((long) (((png_uint_32) p[0] << 24) | ((png_uint_32) p[1] << 16) |
1637     ((png_uint_32) p[2] << 8) | (png_uint_32) p[3]));
1638 }
1639
1640 static MngBox mng_read_box(MngBox previous_box,char delta_type,
1641   unsigned char *p)
1642 {
1643    MngBox
1644       box;
1645
1646   /*
1647     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1648   */
1649   box.left=mng_get_long(p);
1650   box.right=mng_get_long(&p[4]);
1651   box.top=mng_get_long(&p[8]);
1652   box.bottom=mng_get_long(&p[12]);
1653   if (delta_type != 0)
1654     {
1655       box.left+=previous_box.left;
1656       box.right+=previous_box.right;
1657       box.top+=previous_box.top;
1658       box.bottom+=previous_box.bottom;
1659     }
1660
1661   return(box);
1662 }
1663
1664 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1665   unsigned char *p)
1666 {
1667   MngPair
1668     pair;
1669
1670   /*
1671     Read two ssize_t's from CLON, MOVE or PAST chunk
1672   */
1673   pair.a=mng_get_long(p);
1674   pair.b=mng_get_long(&p[4]);
1675   if (delta_type != 0)
1676     {
1677       pair.a+=previous_pair.a;
1678       pair.b+=previous_pair.b;
1679     }
1680
1681   return(pair);
1682 }
1683
1684 typedef struct _PNGErrorInfo
1685 {
1686   Image
1687     *image;
1688
1689   ExceptionInfo
1690     *exception;
1691 } PNGErrorInfo;
1692
1693 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1694 {
1695   ExceptionInfo
1696     *exception;
1697
1698   Image
1699     *image;
1700
1701   PNGErrorInfo
1702     *error_info;
1703
1704   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1705   image=error_info->image;
1706   exception=error_info->exception;
1707
1708   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1709     "  libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1710
1711   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1712     "`%s'",image->filename);
1713
1714 #if (PNG_LIBPNG_VER < 10500)
1715   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1716    * are building with libpng-1.4.x and can be ignored.
1717    */
1718   longjmp(ping->jmpbuf,1);
1719 #else
1720   png_longjmp(ping,1);
1721 #endif
1722 }
1723
1724 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1725 {
1726   ExceptionInfo
1727     *exception;
1728
1729   Image
1730     *image;
1731
1732   PNGErrorInfo
1733     *error_info;
1734
1735   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1736     png_error(ping, message);
1737
1738   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1739   image=error_info->image;
1740   exception=error_info->exception;
1741   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1742     "  libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1743
1744   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1745     message,"`%s'",image->filename);
1746 }
1747
1748 #ifdef PNG_USER_MEM_SUPPORTED
1749 #if PNG_LIBPNG_VER >= 10400
1750 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1751 #else
1752 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1753 #endif
1754 {
1755   (void) png_ptr;
1756   return((png_voidp) AcquireMagickMemory((size_t) size));
1757 }
1758
1759 /*
1760   Free a pointer.  It is removed from the list at the same time.
1761 */
1762 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1763 {
1764   (void) png_ptr;
1765   ptr=RelinquishMagickMemory(ptr);
1766   return((png_free_ptr) NULL);
1767 }
1768 #endif
1769
1770 #if defined(__cplusplus) || defined(c_plusplus)
1771 }
1772 #endif
1773
1774 static int
1775 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1776    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1777 {
1778   register ssize_t
1779     i;
1780
1781   register unsigned char
1782     *dp;
1783
1784   register png_charp
1785     sp;
1786
1787   png_uint_32
1788     length,
1789     nibbles;
1790
1791   StringInfo
1792     *profile;
1793
1794   const unsigned char
1795     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1796                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1797                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1798                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1799                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1800                  13,14,15};
1801
1802   sp=text[ii].text+1;
1803   /* look for newline */
1804   while (*sp != '\n')
1805      sp++;
1806
1807   /* look for length */
1808   while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1809      sp++;
1810
1811   length=(png_uint_32) StringToLong(sp);
1812
1813   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1814        "      length: %lu",(unsigned long) length);
1815
1816   while (*sp != ' ' && *sp != '\n')
1817      sp++;
1818
1819   /* allocate space */
1820   if (length == 0)
1821   {
1822     png_warning(ping,"invalid profile length");
1823     return(MagickFalse);
1824   }
1825
1826   profile=BlobToStringInfo((const void *) NULL,length);
1827
1828   if (profile == (StringInfo *) NULL)
1829   {
1830     png_warning(ping, "unable to copy profile");
1831     return(MagickFalse);
1832   }
1833
1834   /* copy profile, skipping white space and column 1 "=" signs */
1835   dp=GetStringInfoDatum(profile);
1836   nibbles=length*2;
1837
1838   for (i=0; i < (ssize_t) nibbles; i++)
1839   {
1840     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1841     {
1842       if (*sp == '\0')
1843         {
1844           png_warning(ping, "ran out of profile data");
1845           profile=DestroyStringInfo(profile);
1846           return(MagickFalse);
1847         }
1848       sp++;
1849     }
1850
1851     if (i%2 == 0)
1852       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1853
1854     else
1855       (*dp++)+=unhex[(int) *sp++];
1856   }
1857   /*
1858     We have already read "Raw profile type.
1859   */
1860   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1861   profile=DestroyStringInfo(profile);
1862
1863   if (image_info->verbose)
1864     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1865
1866   return MagickTrue;
1867 }
1868
1869 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1870
1871 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1872 {
1873   Image
1874     *image;
1875
1876
1877   /* The unknown chunk structure contains the chunk data:
1878      png_byte name[5];
1879      png_byte *data;
1880      png_size_t size;
1881
1882      Note that libpng has already taken care of the CRC handling.
1883
1884      Returns one of the following:
1885          return(-n);  chunk had an error
1886          return(0);  did not recognize
1887          return(n);  success
1888   */
1889
1890   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1891      "    read_user_chunk: found %c%c%c%c chunk",
1892        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1893
1894   if (chunk->name[0]  == 101 &&
1895       (chunk->name[1] ==  88 || chunk->name[1] == 120 ) &&
1896       chunk->name[2] ==   73 &&
1897       chunk-> name[3] == 102)
1898     {
1899       /* process eXIf or exIf chunk */
1900
1901       PNGErrorInfo
1902         *error_info;
1903
1904       StringInfo
1905         *profile;
1906
1907       unsigned char
1908         *p;
1909
1910       png_byte
1911         *s;
1912
1913       size_t
1914         i;
1915
1916       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1917         " recognized eXIf chunk");
1918
1919       image=(Image *) png_get_user_chunk_ptr(ping);
1920
1921       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1922
1923       profile=BlobToStringInfo((const void *) NULL,chunk->size+6);
1924
1925       if (profile == (StringInfo *) NULL)
1926         {
1927           (void) ThrowMagickException(error_info->exception,GetMagickModule(),
1928             ResourceLimitError,"MemoryAllocationFailed","`%s'",
1929             image->filename);
1930           return(-1);
1931         }
1932       p=GetStringInfoDatum(profile);
1933
1934       if (*p != 'E')
1935         {
1936           /* Initialize profile with "Exif\0\0" if it is not
1937              already present by accident
1938           */
1939           *p++ ='E';
1940           *p++ ='x';
1941           *p++ ='i';
1942           *p++ ='f';
1943           *p++ ='\0';
1944           *p++ ='\0';
1945         }
1946       else
1947         {
1948           if (p[1] != 'x' || p[2] != 'i' || p[3] != 'f' ||
1949               p[4] != '\0' || p[5] != '\0')
1950             {
1951               /* Chunk is malformed */
1952               profile=DestroyStringInfo(profile);
1953               return(-1);
1954             }
1955          }
1956
1957       /* copy chunk->data to profile */
1958       s=chunk->data;
1959       for (i=0; i<chunk->size; i++)
1960         *p++ = *s++;
1961
1962       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1963       (void) SetImageProfile(image,"exif",profile,
1964         error_info->exception);
1965
1966       profile=DestroyStringInfo(profile);
1967
1968       return(1);
1969     }
1970
1971   /* orNT */
1972   if (chunk->name[0] == 111 &&
1973       chunk->name[1] == 114 &&
1974       chunk->name[2] ==  78 &&
1975       chunk->name[3] ==  84)
1976     {
1977      /* recognized orNT */
1978      if (chunk->size != 1)
1979        return(-1); /* Error return */
1980
1981      image=(Image *) png_get_user_chunk_ptr(ping);
1982
1983      image->orientation=
1984        Magick_Orientation_from_Exif_Orientation((int) chunk->data[0]);
1985
1986      return(1);
1987     }
1988
1989   /* vpAg (deprecated, replaced by caNv) */
1990   if (chunk->name[0] == 118 &&
1991       chunk->name[1] == 112 &&
1992       chunk->name[2] ==  65 &&
1993       chunk->name[3] == 103)
1994     {
1995       /* recognized vpAg */
1996
1997       if (chunk->size != 9)
1998         return(-1); /* Error return */
1999
2000       if (chunk->data[8] != 0)
2001         return(0);  /* ImageMagick requires pixel units */
2002
2003       image=(Image *) png_get_user_chunk_ptr(ping);
2004
2005       image->page.width=(size_t)mng_get_long(chunk->data);
2006       image->page.height=(size_t)mng_get_long(&chunk->data[4]);
2007
2008       return(1);
2009     }
2010
2011   /* caNv */
2012   if (chunk->name[0] ==  99 &&
2013       chunk->name[1] ==  97 &&
2014       chunk->name[2] ==  78 &&
2015       chunk->name[3] == 118)
2016     {
2017       /* recognized caNv */
2018
2019       if (chunk->size != 16)
2020         return(-1); /* Error return */
2021
2022       image=(Image *) png_get_user_chunk_ptr(ping);
2023
2024       image->page.width=(size_t)mng_get_long(chunk->data);
2025       image->page.height=(size_t)mng_get_long(&chunk->data[4]);
2026       image->page.x=(size_t)mng_get_long(&chunk->data[8]);
2027       image->page.y=(size_t)mng_get_long(&chunk->data[12]);
2028
2029       return(1);
2030     }
2031
2032   return(0); /* Did not recognize */
2033 }
2034 #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
2035
2036 #if defined(PNG_tIME_SUPPORTED)
2037 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
2038   ExceptionInfo *exception)
2039 {
2040   png_timep
2041     time;
2042
2043   if (png_get_tIME(ping,info,&time))
2044     {
2045       char
2046         timestamp[21];
2047
2048       FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
2049         time->year,time->month,time->day,time->hour,time->minute,time->second);
2050       SetImageProperty(image,"png:tIME",timestamp,exception);
2051     }
2052 }
2053 #endif
2054
2055 /*
2056 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2057 %                                                                             %
2058 %                                                                             %
2059 %                                                                             %
2060 %   R e a d O n e P N G I m a g e                                             %
2061 %                                                                             %
2062 %                                                                             %
2063 %                                                                             %
2064 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2065 %
2066 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2067 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
2068 %  necessary for the new Image structure and returns a pointer to the new
2069 %  image.
2070 %
2071 %  The format of the ReadOnePNGImage method is:
2072 %
2073 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2074 %         ExceptionInfo *exception)
2075 %
2076 %  A description of each parameter follows:
2077 %
2078 %    o mng_info: Specifies a pointer to a MngInfo structure.
2079 %
2080 %    o image_info: the image info.
2081 %
2082 %    o exception: return any errors or warnings in this structure.
2083 %
2084 */
2085 static Image *ReadOnePNGImage(MngInfo *mng_info,
2086     const ImageInfo *image_info, ExceptionInfo *exception)
2087 {
2088   /* Read one PNG image */
2089
2090   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2091
2092   Image
2093     *image;
2094
2095   char
2096     im_vers[32],
2097     libpng_runv[32],
2098     libpng_vers[32],
2099     zlib_runv[32],
2100     zlib_vers[32];
2101
2102   int
2103     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2104     num_raw_profiles,
2105     num_text,
2106     num_text_total,
2107     num_passes,
2108     number_colors,
2109     pass,
2110     ping_bit_depth,
2111     ping_color_type,
2112     ping_file_depth,
2113     ping_interlace_method,
2114     ping_compression_method,
2115     ping_filter_method,
2116     ping_num_trans,
2117     unit_type;
2118
2119   double
2120     file_gamma;
2121
2122   MagickBooleanType
2123     logging,
2124     ping_found_cHRM,
2125     ping_found_gAMA,
2126     ping_found_iCCP,
2127     ping_found_sRGB,
2128     ping_found_sRGB_cHRM,
2129     ping_preserve_iCCP,
2130     status;
2131
2132   MemoryInfo
2133     *volatile pixel_info;
2134
2135   PixelInfo
2136     transparent_color;
2137
2138   PNGErrorInfo
2139     error_info;
2140
2141   png_bytep
2142      ping_trans_alpha;
2143
2144   png_color_16p
2145      ping_background,
2146      ping_trans_color;
2147
2148   png_info
2149     *end_info,
2150     *ping_info;
2151
2152   png_struct
2153     *ping;
2154
2155   png_textp
2156     text;
2157
2158   png_uint_32
2159     ping_height,
2160     ping_width,
2161     x_resolution,
2162     y_resolution;
2163
2164   QuantumInfo
2165     *volatile quantum_info;
2166
2167   Quantum
2168     *volatile quantum_scanline;
2169
2170   ssize_t
2171     ping_rowbytes,
2172     y;
2173
2174   register unsigned char
2175     *p;
2176
2177   register ssize_t
2178     i,
2179     x;
2180
2181   register Quantum
2182     *q;
2183
2184   size_t
2185     length,
2186     row_offset;
2187
2188   ssize_t
2189     j;
2190
2191   unsigned char
2192     *ping_pixels;
2193
2194 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2195   png_byte unused_chunks[]=
2196   {
2197     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2198     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2199     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2200     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2201     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2202 #if !defined(PNG_tIME_SUPPORTED)
2203     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2204 #endif
2205 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2206                           /* ignore the APNG chunks */
2207      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2208     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2209     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2210 #endif
2211   };
2212 #endif
2213
2214   /* Define these outside of the following "if logging()" block so they will
2215    * show in debuggers.
2216    */
2217   *im_vers='\0';
2218   (void) ConcatenateMagickString(im_vers,
2219          MagickLibVersionText,32);
2220   (void) ConcatenateMagickString(im_vers,
2221          MagickLibAddendum,32);
2222
2223   *libpng_vers='\0';
2224   (void) ConcatenateMagickString(libpng_vers,
2225          PNG_LIBPNG_VER_STRING,32);
2226   *libpng_runv='\0';
2227   (void) ConcatenateMagickString(libpng_runv,
2228          png_get_libpng_ver(NULL),32);
2229
2230   *zlib_vers='\0';
2231   (void) ConcatenateMagickString(zlib_vers,
2232          ZLIB_VERSION,32);
2233   *zlib_runv='\0';
2234   (void) ConcatenateMagickString(zlib_runv,
2235          zlib_version,32);
2236
2237   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2238        "  Enter ReadOnePNGImage()\n"
2239        "    IM version     = %s\n"
2240        "    Libpng version = %s",
2241        im_vers, libpng_vers);
2242
2243   if (logging != MagickFalse)
2244   {
2245     if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2246     {
2247    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2248       "      running with   %s", libpng_runv);
2249     }
2250     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2251         "    Zlib version   = %s", zlib_vers);
2252     if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2253     {
2254     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2255         "      running with   %s", zlib_runv);
2256     }
2257   }
2258
2259 #if (PNG_LIBPNG_VER < 10200)
2260   if (image_info->verbose)
2261     printf("Your PNG library (libpng-%s) is rather old.\n",
2262        PNG_LIBPNG_VER_STRING);
2263 #endif
2264
2265 #if (PNG_LIBPNG_VER >= 10400)
2266 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
2267   if (image_info->verbose)
2268     {
2269       printf("Your PNG library (libpng-%s) is an old beta version.\n",
2270            PNG_LIBPNG_VER_STRING);
2271       printf("Please update it.\n");
2272     }
2273 #  endif
2274 #endif
2275
2276
2277   quantum_info = (QuantumInfo *) NULL;
2278   image=mng_info->image;
2279
2280   if (logging != MagickFalse)
2281   {
2282     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2283        "    Before reading:\n"
2284        "      image->alpha_trait=%d\n"
2285        "      image->rendering_intent=%d\n"
2286        "      image->colorspace=%d\n"
2287        "      image->gamma=%f",
2288        (int) image->alpha_trait, (int) image->rendering_intent,
2289        (int) image->colorspace, image->gamma);
2290   }
2291   intent=
2292     Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2293
2294   /* Set to an out-of-range color unless tRNS chunk is present */
2295   transparent_color.red=65537;
2296   transparent_color.green=65537;
2297   transparent_color.blue=65537;
2298   transparent_color.alpha=65537;
2299
2300   number_colors=0;
2301   num_text = 0;
2302   num_text_total = 0;
2303   num_raw_profiles = 0;
2304
2305   ping_found_cHRM = MagickFalse;
2306   ping_found_gAMA = MagickFalse;
2307   ping_found_iCCP = MagickFalse;
2308   ping_found_sRGB = MagickFalse;
2309   ping_found_sRGB_cHRM = MagickFalse;
2310   ping_preserve_iCCP = MagickFalse;
2311
2312
2313   /*
2314     Allocate the PNG structures
2315   */
2316 #ifdef PNG_USER_MEM_SUPPORTED
2317  error_info.image=image;
2318  error_info.exception=exception;
2319  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2320    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2321    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2322 #else
2323   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2324     MagickPNGErrorHandler,MagickPNGWarningHandler);
2325 #endif
2326   if (ping == (png_struct *) NULL)
2327     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2328
2329   ping_info=png_create_info_struct(ping);
2330
2331   if (ping_info == (png_info *) NULL)
2332     {
2333       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2334       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2335     }
2336
2337   end_info=png_create_info_struct(ping);
2338
2339   if (end_info == (png_info *) NULL)
2340     {
2341       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2342       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2343     }
2344
2345   pixel_info=(MemoryInfo *) NULL;
2346   quantum_scanline = (Quantum *) NULL;
2347   quantum_info = (QuantumInfo *) NULL;
2348
2349   if (setjmp(png_jmpbuf(ping)))
2350     {
2351       /*
2352         PNG image is corrupt.
2353       */
2354       png_destroy_read_struct(&ping,&ping_info,&end_info);
2355
2356       if (pixel_info != (MemoryInfo *) NULL)
2357         pixel_info=RelinquishVirtualMemory(pixel_info);
2358
2359       if (quantum_info != (QuantumInfo *) NULL)
2360         quantum_info=DestroyQuantumInfo(quantum_info);
2361
2362       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2363
2364 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2365       UnlockSemaphoreInfo(ping_semaphore);
2366 #endif
2367
2368       if (logging != MagickFalse)
2369         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2370           "  exit ReadOnePNGImage() with error.");
2371
2372       return(GetFirstImageInList(image));
2373     }
2374
2375   /* {  For navigation to end of SETJMP-protected block.  Within this
2376    *    block, use png_error() instead of Throwing an Exception, to ensure
2377    *    that libpng is able to clean up, and that the semaphore is unlocked.
2378    */
2379
2380 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2381   LockSemaphoreInfo(ping_semaphore);
2382 #endif
2383
2384 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2385   /* Allow benign errors */
2386   png_set_benign_errors(ping, 1);
2387 #endif
2388
2389 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2390   /* Reject images with too many rows or columns */
2391   png_set_user_limits(ping,
2392     (png_uint_32) MagickMin(0x7fffffffL,
2393         GetMagickResourceLimit(WidthResource)),
2394     (png_uint_32) MagickMin(0x7fffffffL,
2395         GetMagickResourceLimit(HeightResource)));
2396 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2397
2398   /*
2399     Prepare PNG for reading.
2400   */
2401
2402   mng_info->image_found++;
2403   png_set_sig_bytes(ping,8);
2404
2405   if (LocaleCompare(image_info->magick,"MNG") == 0)
2406     {
2407 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2408       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2409       png_set_read_fn(ping,image,png_get_data);
2410 #else
2411 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2412       png_permit_empty_plte(ping,MagickTrue);
2413       png_set_read_fn(ping,image,png_get_data);
2414 #else
2415       mng_info->image=image;
2416       mng_info->bytes_in_read_buffer=0;
2417       mng_info->found_empty_plte=MagickFalse;
2418       mng_info->have_saved_bkgd_index=MagickFalse;
2419       png_set_read_fn(ping,mng_info,mng_get_data);
2420 #endif
2421 #endif
2422     }
2423
2424   else
2425     png_set_read_fn(ping,image,png_get_data);
2426
2427   {
2428     const char
2429       *value;
2430
2431     value=GetImageOption(image_info,"profile:skip");
2432
2433     if (IsOptionMember("ICC",value) == MagickFalse)
2434     {
2435
2436        value=GetImageOption(image_info,"png:preserve-iCCP");
2437
2438        if (value == NULL)
2439           value=GetImageArtifact(image,"png:preserve-iCCP");
2440
2441        if (value != NULL)
2442           ping_preserve_iCCP=MagickTrue;
2443
2444 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2445        /* Don't let libpng check for ICC/sRGB profile because we're going
2446         * to do that anyway.  This feature was added at libpng-1.6.12.
2447         * If logging, go ahead and check and issue a warning as appropriate.
2448         */
2449        if (logging == MagickFalse)
2450           png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2451 #endif
2452     }
2453 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2454     else
2455     {
2456        png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
2457     }
2458 #endif
2459   }
2460 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2461   /* Ignore unused chunks and all unknown chunks except for caNv and vpAg */
2462 # if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2463   png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2464 # else
2465   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2466 # endif
2467   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
2468   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
2469   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2470      (int)sizeof(unused_chunks)/5);
2471   /* Callback for other unknown chunks */
2472   png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
2473 #endif
2474
2475 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2476 #  if (PNG_LIBPNG_VER >= 10400)
2477     /* Limit the size of the chunk storage cache used for sPLT, text,
2478      * and unknown chunks.
2479      */
2480     png_set_chunk_cache_max(ping, 32767);
2481 #  endif
2482 #endif
2483
2484 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2485     /* Disable new libpng-1.5.10 feature */
2486     png_set_check_for_invalid_index (ping, 0);
2487 #endif
2488
2489 #if (PNG_LIBPNG_VER < 10400)
2490 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2491    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2492   /* Disable thread-unsafe features of pnggccrd */
2493   if (png_access_version_number() >= 10200)
2494   {
2495     png_uint_32 mmx_disable_mask=0;
2496     png_uint_32 asm_flags;
2497
2498     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2499                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2500                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2501                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2502     asm_flags=png_get_asm_flags(ping);
2503     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2504   }
2505 #  endif
2506 #endif
2507
2508   png_read_info(ping,ping_info);
2509
2510   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2511                &ping_bit_depth,&ping_color_type,
2512                &ping_interlace_method,&ping_compression_method,
2513                &ping_filter_method);
2514
2515   ping_file_depth = ping_bit_depth;
2516
2517   /* Swap bytes if requested */
2518   if (ping_file_depth == 16)
2519   {
2520      const char
2521        *value;
2522
2523      value=GetImageOption(image_info,"png:swap-bytes");
2524
2525      if (value == NULL)
2526         value=GetImageArtifact(image,"png:swap-bytes");
2527
2528      if (value != NULL)
2529         png_set_swap(ping);
2530   }
2531
2532   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2533   {
2534       char
2535         msg[MagickPathExtent];
2536
2537       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2538          (int) ping_color_type);
2539       (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2540
2541       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2542          (int) ping_bit_depth);
2543       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2544   }
2545
2546   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2547                       &ping_trans_color);
2548
2549   (void) png_get_bKGD(ping, ping_info, &ping_background);
2550
2551   if (ping_bit_depth < 8)
2552     {
2553        png_set_packing(ping);
2554        ping_bit_depth = 8;
2555     }
2556
2557   image->depth=ping_bit_depth;
2558   image->depth=GetImageQuantumDepth(image,MagickFalse);
2559   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2560
2561   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2562       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2563     {
2564       image->rendering_intent=UndefinedIntent;
2565       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2566       (void) ResetMagickMemory(&image->chromaticity,0,
2567         sizeof(image->chromaticity));
2568     }
2569
2570   if (logging != MagickFalse)
2571     {
2572       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2573         "    PNG width: %.20g, height: %.20g\n"
2574         "    PNG color_type: %d, bit_depth: %d\n"
2575         "    PNG compression_method: %d\n"
2576         "    PNG interlace_method: %d, filter_method: %d",
2577         (double) ping_width, (double) ping_height,
2578         ping_color_type, ping_bit_depth,
2579         ping_compression_method,
2580         ping_interlace_method,ping_filter_method);
2581
2582     }
2583
2584   if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2585     {
2586       ping_found_iCCP=MagickTrue;
2587       if (logging != MagickFalse)
2588         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2589           "    Found PNG iCCP chunk.");
2590     }
2591
2592   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2593     {
2594       ping_found_gAMA=MagickTrue;
2595       if (logging != MagickFalse)
2596         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2597           "    Found PNG gAMA chunk.");
2598     }
2599
2600   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2601     {
2602       ping_found_cHRM=MagickTrue;
2603       if (logging != MagickFalse)
2604         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2605           "    Found PNG cHRM chunk.");
2606     }
2607
2608   if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2609       PNG_INFO_sRGB))
2610     {
2611       ping_found_sRGB=MagickTrue;
2612       if (logging != MagickFalse)
2613         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2614           "    Found PNG sRGB chunk.");
2615     }
2616
2617 #ifdef PNG_READ_iCCP_SUPPORTED
2618     if (ping_found_iCCP !=MagickTrue &&
2619       ping_found_sRGB != MagickTrue &&
2620       png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2621     {
2622       ping_found_iCCP=MagickTrue;
2623       if (logging != MagickFalse)
2624         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2625           "    Found PNG iCCP chunk.");
2626     }
2627
2628   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2629     {
2630       int
2631         compression;
2632
2633 #if (PNG_LIBPNG_VER < 10500)
2634       png_charp
2635         info;
2636 #else
2637       png_bytep
2638         info;
2639 #endif
2640
2641       png_charp
2642         name;
2643
2644       png_uint_32
2645         profile_length;
2646
2647       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2648         &profile_length);
2649
2650       if (profile_length != 0)
2651         {
2652           StringInfo
2653             *profile;
2654
2655           if (logging != MagickFalse)
2656             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2657               "    Reading PNG iCCP chunk.");
2658
2659           profile=BlobToStringInfo(info,profile_length);
2660
2661           if (profile == (StringInfo *) NULL)
2662           {
2663             png_warning(ping, "ICC profile is NULL");
2664             profile=DestroyStringInfo(profile);
2665           }
2666           else
2667           {
2668             if (ping_preserve_iCCP == MagickFalse)
2669             {
2670                  int
2671                    icheck,
2672                    got_crc=0;
2673
2674
2675                  png_uint_32
2676                    length,
2677                    profile_crc=0;
2678
2679                  unsigned char
2680                    *data;
2681
2682                  length=(png_uint_32) GetStringInfoLength(profile);
2683
2684                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2685                  {
2686                    if (length == sRGB_info[icheck].len)
2687                    {
2688                      if (got_crc == 0)
2689                      {
2690                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2691                          "    Got a %lu-byte ICC profile (potentially sRGB)",
2692                          (unsigned long) length);
2693
2694                        data=GetStringInfoDatum(profile);
2695                        profile_crc=crc32(0,data,length);
2696
2697                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2698                            "      with crc=%8x",(unsigned int) profile_crc);
2699                        got_crc++;
2700                      }
2701
2702                      if (profile_crc == sRGB_info[icheck].crc)
2703                      {
2704                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2705                             "      It is sRGB with rendering intent = %s",
2706                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
2707                              sRGB_info[icheck].intent));
2708                         if (image->rendering_intent==UndefinedIntent)
2709                         {
2710                           image->rendering_intent=
2711                           Magick_RenderingIntent_from_PNG_RenderingIntent(
2712                              sRGB_info[icheck].intent);
2713                         }
2714                         break;
2715                      }
2716                    }
2717                  }
2718                  if (sRGB_info[icheck].len == 0)
2719                  {
2720                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2721                         "    Got %lu-byte ICC profile not recognized as sRGB",
2722                         (unsigned long) length);
2723                     (void) SetImageProfile(image,"icc",profile,exception);
2724                  }
2725             }
2726             else /* Preserve-iCCP */
2727             {
2728                     (void) SetImageProfile(image,"icc",profile,exception);
2729             }
2730
2731             profile=DestroyStringInfo(profile);
2732           }
2733       }
2734     }
2735 #endif
2736
2737 #if defined(PNG_READ_sRGB_SUPPORTED)
2738   {
2739     if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2740         PNG_INFO_sRGB))
2741     {
2742       if (png_get_sRGB(ping,ping_info,&intent))
2743       {
2744         if (image->rendering_intent == UndefinedIntent)
2745           image->rendering_intent=
2746              Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2747
2748         if (logging != MagickFalse)
2749           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2750             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2751       }
2752     }
2753
2754     else if (mng_info->have_global_srgb)
2755       {
2756         if (image->rendering_intent == UndefinedIntent)
2757           image->rendering_intent=
2758             Magick_RenderingIntent_from_PNG_RenderingIntent
2759             (mng_info->global_srgb_intent);
2760       }
2761   }
2762 #endif
2763
2764
2765   {
2766      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2767        if (mng_info->have_global_gama)
2768          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2769
2770      if (png_get_gAMA(ping,ping_info,&file_gamma))
2771        {
2772          image->gamma=(float) file_gamma;
2773          if (logging != MagickFalse)
2774            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2775              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2776        }
2777   }
2778
2779   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2780     {
2781       if (mng_info->have_global_chrm != MagickFalse)
2782         {
2783           (void) png_set_cHRM(ping,ping_info,
2784             mng_info->global_chrm.white_point.x,
2785             mng_info->global_chrm.white_point.y,
2786             mng_info->global_chrm.red_primary.x,
2787             mng_info->global_chrm.red_primary.y,
2788             mng_info->global_chrm.green_primary.x,
2789             mng_info->global_chrm.green_primary.y,
2790             mng_info->global_chrm.blue_primary.x,
2791             mng_info->global_chrm.blue_primary.y);
2792         }
2793     }
2794
2795   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2796     {
2797       (void) png_get_cHRM(ping,ping_info,
2798         &image->chromaticity.white_point.x,
2799         &image->chromaticity.white_point.y,
2800         &image->chromaticity.red_primary.x,
2801         &image->chromaticity.red_primary.y,
2802         &image->chromaticity.green_primary.x,
2803         &image->chromaticity.green_primary.y,
2804         &image->chromaticity.blue_primary.x,
2805         &image->chromaticity.blue_primary.y);
2806
2807        ping_found_cHRM=MagickTrue;
2808
2809        if (image->chromaticity.red_primary.x>0.6399f &&
2810            image->chromaticity.red_primary.x<0.6401f &&
2811            image->chromaticity.red_primary.y>0.3299f &&
2812            image->chromaticity.red_primary.y<0.3301f &&
2813            image->chromaticity.green_primary.x>0.2999f &&
2814            image->chromaticity.green_primary.x<0.3001f &&
2815            image->chromaticity.green_primary.y>0.5999f &&
2816            image->chromaticity.green_primary.y<0.6001f &&
2817            image->chromaticity.blue_primary.x>0.1499f &&
2818            image->chromaticity.blue_primary.x<0.1501f &&
2819            image->chromaticity.blue_primary.y>0.0599f &&
2820            image->chromaticity.blue_primary.y<0.0601f &&
2821            image->chromaticity.white_point.x>0.3126f &&
2822            image->chromaticity.white_point.x<0.3128f &&
2823            image->chromaticity.white_point.y>0.3289f &&
2824            image->chromaticity.white_point.y<0.3291f)
2825           ping_found_sRGB_cHRM=MagickTrue;
2826     }
2827
2828   if (image->rendering_intent != UndefinedIntent)
2829     {
2830       if (ping_found_sRGB != MagickTrue &&
2831           (ping_found_gAMA != MagickTrue ||
2832           (image->gamma > .45 && image->gamma < .46)) &&
2833           (ping_found_cHRM != MagickTrue ||
2834           ping_found_sRGB_cHRM != MagickFalse) &&
2835           ping_found_iCCP != MagickTrue)
2836       {
2837          png_set_sRGB(ping,ping_info,
2838             Magick_RenderingIntent_to_PNG_RenderingIntent
2839             (image->rendering_intent));
2840          file_gamma=1.000f/2.200f;
2841          ping_found_sRGB=MagickTrue;
2842          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2843            "    Setting sRGB as if in input");
2844       }
2845     }
2846
2847 #if defined(PNG_oFFs_SUPPORTED)
2848   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2849     {
2850       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2851       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2852
2853       if (logging != MagickFalse)
2854         if (image->page.x || image->page.y)
2855           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2856             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2857             image->page.x,(double) image->page.y);
2858     }
2859 #endif
2860 #if defined(PNG_pHYs_SUPPORTED)
2861   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2862     {
2863       if (mng_info->have_global_phys)
2864         {
2865           png_set_pHYs(ping,ping_info,
2866                        mng_info->global_x_pixels_per_unit,
2867                        mng_info->global_y_pixels_per_unit,
2868                        mng_info->global_phys_unit_type);
2869         }
2870     }
2871
2872   x_resolution=0;
2873   y_resolution=0;
2874   unit_type=0;
2875   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2876     {
2877       /*
2878         Set image resolution.
2879       */
2880       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2881         &unit_type);
2882       image->resolution.x=(double) x_resolution;
2883       image->resolution.y=(double) y_resolution;
2884
2885       if (unit_type == PNG_RESOLUTION_METER)
2886         {
2887           image->units=PixelsPerCentimeterResolution;
2888           image->resolution.x=(double) x_resolution/100.0;
2889           image->resolution.y=(double) y_resolution/100.0;
2890         }
2891
2892       if (logging != MagickFalse)
2893         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2894           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2895           (double) x_resolution,(double) y_resolution,unit_type);
2896     }
2897 #endif
2898
2899   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2900     {
2901       png_colorp
2902         palette;
2903
2904       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2905
2906       if ((number_colors == 0) &&
2907           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2908         {
2909           if (mng_info->global_plte_length)
2910             {
2911               png_set_PLTE(ping,ping_info,mng_info->global_plte,
2912                 (int) mng_info->global_plte_length);
2913
2914               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2915               {
2916                 if (mng_info->global_trns_length)
2917                   {
2918                     png_warning(ping,
2919                       "global tRNS has more entries than global PLTE");
2920                   }
2921                 else
2922                   {
2923                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
2924                        (int) mng_info->global_trns_length,NULL);
2925                   }
2926                }
2927 #ifdef PNG_READ_bKGD_SUPPORTED
2928               if (
2929 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2930                    mng_info->have_saved_bkgd_index ||
2931 #endif
2932                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2933                     {
2934                       png_color_16
2935                          background;
2936
2937 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2938                       if (mng_info->have_saved_bkgd_index)
2939                         background.index=mng_info->saved_bkgd_index;
2940 #endif
2941                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2942                         background.index=ping_background->index;
2943
2944                       background.red=(png_uint_16)
2945                         mng_info->global_plte[background.index].red;
2946
2947                       background.green=(png_uint_16)
2948                         mng_info->global_plte[background.index].green;
2949
2950                       background.blue=(png_uint_16)
2951                         mng_info->global_plte[background.index].blue;
2952
2953                       background.gray=(png_uint_16)
2954                         mng_info->global_plte[background.index].green;
2955
2956                       png_set_bKGD(ping,ping_info,&background);
2957                     }
2958 #endif
2959                 }
2960               else
2961                 png_error(ping,"No global PLTE in file");
2962             }
2963         }
2964
2965 #ifdef PNG_READ_bKGD_SUPPORTED
2966   if (mng_info->have_global_bkgd &&
2967           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2968       image->background_color=mng_info->mng_global_bkgd;
2969
2970   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2971     {
2972       unsigned int
2973         bkgd_scale;
2974
2975       /* Set image background color.
2976        * Scale background components to 16-bit, then scale
2977        * to quantum depth
2978        */
2979
2980         bkgd_scale = 1;
2981
2982         if (ping_file_depth == 1)
2983            bkgd_scale = 255;
2984
2985         else if (ping_file_depth == 2)
2986            bkgd_scale = 85;
2987
2988         else if (ping_file_depth == 4)
2989            bkgd_scale = 17;
2990
2991         if (ping_file_depth <= 8)
2992            bkgd_scale *= 257;
2993
2994         ping_background->red *= bkgd_scale;
2995         ping_background->green *= bkgd_scale;
2996         ping_background->blue *= bkgd_scale;
2997
2998         if (logging != MagickFalse)
2999           {
3000             if (logging != MagickFalse)
3001               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3002                  "    Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
3003                  "    bkgd_scale=%d.  ping_background=(%d,%d,%d)",
3004                  ping_background->red,ping_background->green,
3005                  ping_background->blue,
3006                  bkgd_scale,ping_background->red,
3007                  ping_background->green,ping_background->blue);
3008           }
3009
3010         image->background_color.red=
3011             ScaleShortToQuantum(ping_background->red);
3012
3013         image->background_color.green=
3014             ScaleShortToQuantum(ping_background->green);
3015
3016         image->background_color.blue=
3017           ScaleShortToQuantum(ping_background->blue);
3018
3019         image->background_color.alpha=OpaqueAlpha;
3020
3021         if (logging != MagickFalse)
3022           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3023             "    image->background_color=(%.20g,%.20g,%.20g).",
3024             (double) image->background_color.red,
3025             (double) image->background_color.green,
3026             (double) image->background_color.blue);
3027     }
3028 #endif /* PNG_READ_bKGD_SUPPORTED */
3029
3030   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3031     {
3032       /*
3033         Image has a tRNS chunk.
3034       */
3035       int
3036         max_sample;
3037
3038       size_t
3039         one = 1;
3040
3041       if (logging != MagickFalse)
3042         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3043           "    Reading PNG tRNS chunk.");
3044
3045       max_sample = (int) ((one << ping_file_depth) - 1);
3046
3047       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3048           (int)ping_trans_color->gray > max_sample) ||
3049           (ping_color_type == PNG_COLOR_TYPE_RGB &&
3050           ((int)ping_trans_color->red > max_sample ||
3051           (int)ping_trans_color->green > max_sample ||
3052           (int)ping_trans_color->blue > max_sample)))
3053         {
3054           if (logging != MagickFalse)
3055             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3056               "    Ignoring PNG tRNS chunk with out-of-range sample.");
3057           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3058           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3059           image->alpha_trait=UndefinedPixelTrait;
3060         }
3061       else
3062         {
3063           int
3064             scale_to_short;
3065
3066           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3067
3068           /* Scale transparent_color to short */
3069           transparent_color.red= scale_to_short*ping_trans_color->red;
3070           transparent_color.green= scale_to_short*ping_trans_color->green;
3071           transparent_color.blue= scale_to_short*ping_trans_color->blue;
3072           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3073
3074           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3075             {
3076               if (logging != MagickFalse)
3077               {
3078                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3079                   "    Raw tRNS graylevel = %d, scaled graylevel = %d.",
3080                   (int) ping_trans_color->gray,(int) transparent_color.alpha);
3081
3082               }
3083               transparent_color.red=transparent_color.alpha;
3084               transparent_color.green=transparent_color.alpha;
3085               transparent_color.blue=transparent_color.alpha;
3086             }
3087         }
3088     }
3089 #if defined(PNG_READ_sBIT_SUPPORTED)
3090   if (mng_info->have_global_sbit)
3091     {
3092       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3093         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3094     }
3095 #endif
3096   num_passes=png_set_interlace_handling(ping);
3097
3098   png_read_update_info(ping,ping_info);
3099
3100   ping_rowbytes=png_get_rowbytes(ping,ping_info);
3101
3102   /*
3103     Initialize image structure.
3104   */
3105   mng_info->image_box.left=0;
3106   mng_info->image_box.right=(ssize_t) ping_width;
3107   mng_info->image_box.top=0;
3108   mng_info->image_box.bottom=(ssize_t) ping_height;
3109   if (mng_info->mng_type == 0)
3110     {
3111       mng_info->mng_width=ping_width;
3112       mng_info->mng_height=ping_height;
3113       mng_info->frame=mng_info->image_box;
3114       mng_info->clip=mng_info->image_box;
3115     }
3116
3117   else
3118     {
3119       image->page.y=mng_info->y_off[mng_info->object_id];
3120     }
3121
3122   image->compression=ZipCompression;
3123   image->columns=ping_width;
3124   image->rows=ping_height;
3125
3126   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3127       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3128     {
3129       double
3130         image_gamma = image->gamma;
3131
3132       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3133          "    image->gamma=%f",(float) image_gamma);
3134
3135       if (image_gamma > 0.75)
3136         {
3137           /* Set image->rendering_intent to Undefined,
3138            * image->colorspace to GRAY, and reset image->chromaticity.
3139            */
3140           image->intensity = Rec709LuminancePixelIntensityMethod;
3141           SetImageColorspace(image,GRAYColorspace,exception);
3142         }
3143       else
3144         {
3145           RenderingIntent
3146             save_rendering_intent = image->rendering_intent;
3147           ChromaticityInfo
3148             save_chromaticity = image->chromaticity;
3149
3150           SetImageColorspace(image,GRAYColorspace,exception);
3151           image->rendering_intent = save_rendering_intent;
3152           image->chromaticity = save_chromaticity;
3153         }
3154
3155       image->gamma = image_gamma;
3156     }
3157   else
3158     {
3159       double
3160         image_gamma = image->gamma;
3161
3162       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3163          "    image->gamma=%f",(float) image_gamma);
3164
3165       if (image_gamma > 0.75)
3166         {
3167           /* Set image->rendering_intent to Undefined,
3168            * image->colorspace to GRAY, and reset image->chromaticity.
3169            */
3170           image->intensity = Rec709LuminancePixelIntensityMethod;
3171           SetImageColorspace(image,RGBColorspace,exception);
3172         }
3173       else
3174         {
3175           RenderingIntent
3176             save_rendering_intent = image->rendering_intent;
3177           ChromaticityInfo
3178             save_chromaticity = image->chromaticity;
3179
3180           SetImageColorspace(image,sRGBColorspace,exception);
3181           image->rendering_intent = save_rendering_intent;
3182           image->chromaticity = save_chromaticity;
3183         }
3184
3185       image->gamma = image_gamma;
3186     }
3187
3188   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3189       "    image->colorspace=%d",(int) image->colorspace);
3190
3191   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3192       ((int) ping_bit_depth < 16 &&
3193       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3194     {
3195       size_t
3196         one;
3197
3198       image->storage_class=PseudoClass;
3199       one=1;
3200       image->colors=one << ping_file_depth;
3201 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3202       if (image->colors > 256)
3203         image->colors=256;
3204 #else
3205       if (image->colors > 65536L)
3206         image->colors=65536L;
3207 #endif
3208       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3209         {
3210           png_colorp
3211             palette;
3212
3213           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3214           image->colors=(size_t) number_colors;
3215
3216           if (logging != MagickFalse)
3217             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3218               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3219         }
3220     }
3221
3222   if (image->storage_class == PseudoClass)
3223     {
3224       /*
3225         Initialize image colormap.
3226       */
3227       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3228         png_error(ping,"Memory allocation failed");
3229
3230       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3231         {
3232           png_colorp
3233             palette;
3234
3235           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3236
3237           for (i=0; i < (ssize_t) number_colors; i++)
3238           {
3239             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3240             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3241             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3242           }
3243
3244           for ( ; i < (ssize_t) image->colors; i++)
3245           {
3246             image->colormap[i].red=0;
3247             image->colormap[i].green=0;
3248             image->colormap[i].blue=0;
3249           }
3250         }
3251
3252       else
3253         {
3254           Quantum
3255             scale;
3256
3257           scale = (Quantum) (65535.0/((1UL << ping_file_depth)-1.0));
3258
3259 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
3260           scale = ScaleShortToQuantum(scale);
3261 #endif
3262
3263           for (i=0; i < (ssize_t) image->colors; i++)
3264           {
3265             image->colormap[i].red=(Quantum) (i*scale);
3266             image->colormap[i].green=(Quantum) (i*scale);
3267             image->colormap[i].blue=(Quantum) (i*scale);
3268           }
3269        }
3270     }
3271
3272    /* Set some properties for reporting by "identify" */
3273     {
3274       char
3275         msg[MagickPathExtent];
3276
3277      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3278         ping_interlace_method in value */
3279
3280      (void) FormatLocaleString(msg,MagickPathExtent,
3281          "%d, %d",(int) ping_width, (int) ping_height);
3282      (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3283
3284      (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3285         (int) ping_file_depth);
3286      (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3287
3288      (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3289          (int) ping_color_type,
3290          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3291      (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3292
3293      if (ping_interlace_method == 0)
3294        {
3295          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3296             (int) ping_interlace_method);
3297        }
3298      else if (ping_interlace_method == 1)
3299        {
3300          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3301             (int) ping_interlace_method);
3302        }
3303      else
3304        {
3305          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3306             (int) ping_interlace_method);
3307        }
3308        (void) SetImageProperty(image,"png:IHDR.interlace_method",
3309          msg,exception);
3310
3311      if (number_colors != 0)
3312        {
3313          (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3314             (int) number_colors);
3315          (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3316             exception);
3317        }
3318    }
3319 #if defined(PNG_tIME_SUPPORTED)
3320    read_tIME_chunk(image,ping,ping_info,exception);
3321 #endif
3322
3323
3324   /*
3325     Read image scanlines.
3326   */
3327   if (image->delay != 0)
3328     mng_info->scenes_found++;
3329
3330   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3331       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3332       (image_info->first_scene+image_info->number_scenes))))
3333     {
3334       /* This happens later in non-ping decodes */
3335       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3336         image->storage_class=DirectClass;
3337       image->alpha_trait=
3338         (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3339          ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3340          (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3341         BlendPixelTrait : UndefinedPixelTrait;
3342
3343       if (logging != MagickFalse)
3344         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3345           "    Skipping PNG image data for scene %.20g",(double)
3346           mng_info->scenes_found-1);
3347       png_destroy_read_struct(&ping,&ping_info,&end_info);
3348
3349 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3350       UnlockSemaphoreInfo(ping_semaphore);
3351 #endif
3352
3353       if (logging != MagickFalse)
3354         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3355           "  exit ReadOnePNGImage().");
3356
3357       return(image);
3358     }
3359
3360   if (logging != MagickFalse)
3361     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3362       "    Reading PNG IDAT chunk(s)");
3363
3364   status=SetImageExtent(image,image->columns,image->rows,exception);
3365   if (status == MagickFalse)
3366     {
3367       png_destroy_read_struct(&ping,&ping_info,&end_info);
3368 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3369       UnlockSemaphoreInfo(ping_semaphore);
3370 #endif
3371       return(DestroyImageList(image));
3372     }
3373
3374   if (num_passes > 1)
3375     pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3376       sizeof(*ping_pixels));
3377   else
3378     pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3379
3380   if (pixel_info == (MemoryInfo *) NULL)
3381     png_error(ping,"Memory allocation failed");
3382   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3383
3384   if (logging != MagickFalse)
3385     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3386       "    Converting PNG pixels to pixel packets");
3387   /*
3388     Convert PNG pixels to pixel packets.
3389   */
3390   quantum_info=AcquireQuantumInfo(image_info,image);
3391
3392   if (quantum_info == (QuantumInfo *) NULL)
3393      png_error(ping,"Failed to allocate quantum_info");
3394
3395   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3396
3397   {
3398
3399    MagickBooleanType
3400      found_transparent_pixel;
3401
3402   found_transparent_pixel=MagickFalse;
3403
3404   if (image->storage_class == DirectClass)
3405     {
3406       for (pass=0; pass < num_passes; pass++)
3407       {
3408         /*
3409           Convert image to DirectClass pixel packets.
3410         */
3411         image->alpha_trait=
3412             (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3413             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3414             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3415             BlendPixelTrait : UndefinedPixelTrait;
3416
3417         for (y=0; y < (ssize_t) image->rows; y++)
3418         {
3419           if (num_passes > 1)
3420             row_offset=ping_rowbytes*y;
3421
3422           else
3423             row_offset=0;
3424
3425           png_read_row(ping,ping_pixels+row_offset,NULL);
3426
3427           if (pass < num_passes-1)
3428             continue;
3429
3430           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3431
3432           if (q == (Quantum *) NULL)
3433             break;
3434
3435           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3436             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3437               GrayQuantum,ping_pixels+row_offset,exception);
3438
3439           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3440             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3441               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3442
3443           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3444             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3445               RGBAQuantum,ping_pixels+row_offset,exception);
3446
3447           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3448             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3449               IndexQuantum,ping_pixels+row_offset,exception);
3450
3451           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3452             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3453               RGBQuantum,ping_pixels+row_offset,exception);
3454
3455           if (found_transparent_pixel == MagickFalse)
3456             {
3457               /* Is there a transparent pixel in the row? */
3458               if (y== 0 && logging != MagickFalse)
3459                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3460                    "    Looking for cheap transparent pixel");
3461
3462               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3463               {
3464                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3465                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3466                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3467                   {
3468                     if (logging != MagickFalse)
3469                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3470                         "    ...got one.");
3471
3472                     found_transparent_pixel = MagickTrue;
3473                     break;
3474                   }
3475                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3476                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3477                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3478                     transparent_color.red &&
3479                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3480                     transparent_color.green &&
3481                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3482                     transparent_color.blue))
3483                   {
3484                     if (logging != MagickFalse)
3485                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3486                         "    ...got one.");
3487                     found_transparent_pixel = MagickTrue;
3488                     break;
3489                   }
3490                 q+=GetPixelChannels(image);
3491               }
3492             }
3493
3494           if (num_passes == 1)
3495             {
3496               status=SetImageProgress(image,LoadImageTag,
3497                   (MagickOffsetType) y, image->rows);
3498
3499               if (status == MagickFalse)
3500                 break;
3501             }
3502           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3503             break;
3504         }
3505
3506         if (num_passes != 1)
3507           {
3508             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3509             if (status == MagickFalse)
3510               break;
3511           }
3512       }
3513     }
3514
3515   else /* image->storage_class != DirectClass */
3516
3517     for (pass=0; pass < num_passes; pass++)
3518     {
3519       register Quantum
3520         *r;
3521
3522       /*
3523         Convert grayscale image to PseudoClass pixel packets.
3524       */
3525       if (logging != MagickFalse)
3526         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3527           "    Converting grayscale pixels to pixel packets");
3528
3529       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3530         BlendPixelTrait : UndefinedPixelTrait;
3531
3532       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3533         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3534         sizeof(*quantum_scanline));
3535
3536       if (quantum_scanline == (Quantum *) NULL)
3537         png_error(ping,"Memory allocation failed");
3538
3539       for (y=0; y < (ssize_t) image->rows; y++)
3540       {
3541         Quantum
3542            alpha;
3543
3544         if (num_passes > 1)
3545           row_offset=ping_rowbytes*y;
3546
3547         else
3548           row_offset=0;
3549
3550         png_read_row(ping,ping_pixels+row_offset,NULL);
3551
3552         if (pass < num_passes-1)
3553           continue;
3554
3555         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3556
3557         if (q == (Quantum *) NULL)
3558           break;
3559
3560         p=ping_pixels+row_offset;
3561         r=quantum_scanline;
3562
3563         switch (ping_bit_depth)
3564         {
3565           case 8:
3566           {
3567
3568             if (ping_color_type == 4)
3569               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3570               {
3571                 *r++=*p++;
3572
3573                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3574
3575                 SetPixelAlpha(image,alpha,q);
3576
3577                 if (alpha != OpaqueAlpha)
3578                   found_transparent_pixel = MagickTrue;
3579
3580                 q+=GetPixelChannels(image);
3581               }
3582
3583             else
3584               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3585                 *r++=*p++;
3586
3587             break;
3588           }
3589
3590           case 16:
3591           {
3592             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3593             {
3594 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3595               unsigned short
3596                 quantum;
3597
3598               if (image->colors > 256)
3599                 quantum=(((unsigned int) *p++) << 8);
3600
3601               else
3602                 quantum=0;
3603
3604               quantum|=(*p++);
3605               *r=ScaleShortToQuantum(quantum);
3606               r++;
3607
3608               if (ping_color_type == 4)
3609                 {
3610                   if (image->colors > 256)
3611                     quantum=(((unsigned int) *p++) << 8);
3612                   else
3613                     quantum=0;
3614
3615                   quantum|=(*p++);
3616
3617                   alpha=ScaleShortToQuantum(quantum);
3618                   SetPixelAlpha(image,alpha,q);
3619
3620                   if (alpha != OpaqueAlpha)
3621                     found_transparent_pixel = MagickTrue;
3622
3623                   q+=GetPixelChannels(image);
3624                 }
3625
3626 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3627               *r++=(*p++);
3628               p++; /* strip low byte */
3629
3630               if (ping_color_type == 4)
3631                 {
3632                   SetPixelAlpha(image,*p++,q);
3633
3634                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3635                     found_transparent_pixel = MagickTrue;
3636
3637                   p++;
3638                   q+=GetPixelChannels(image);
3639                 }
3640 #endif
3641             }
3642
3643             break;
3644           }
3645
3646           default:
3647             break;
3648         }
3649
3650         /*
3651           Transfer image scanline.
3652         */
3653         r=quantum_scanline;
3654
3655         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3656
3657         if (q == (Quantum *) NULL)
3658           break;
3659         for (x=0; x < (ssize_t) image->columns; x++)
3660         {
3661           SetPixelIndex(image,*r++,q);
3662           q+=GetPixelChannels(image);
3663         }
3664
3665         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3666           break;
3667
3668         if (num_passes == 1)
3669           {
3670             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3671               image->rows);
3672
3673             if (status == MagickFalse)
3674               break;
3675           }
3676       }
3677
3678       if (num_passes != 1)
3679         {
3680           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3681
3682           if (status == MagickFalse)
3683             break;
3684         }
3685
3686       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3687     }
3688
3689     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3690       UndefinedPixelTrait;
3691
3692     if (logging != MagickFalse)
3693       {
3694         if (found_transparent_pixel != MagickFalse)
3695           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3696             "    Found transparent pixel");
3697         else
3698           {
3699             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3700               "    No transparent pixel was found");
3701
3702             ping_color_type&=0x03;
3703           }
3704       }
3705     }
3706
3707   quantum_info=DestroyQuantumInfo(quantum_info);
3708
3709   if (image->storage_class == PseudoClass)
3710     {
3711       PixelTrait
3712         alpha_trait;
3713
3714       alpha_trait=image->alpha_trait;
3715       image->alpha_trait=UndefinedPixelTrait;
3716       (void) SyncImage(image,exception);
3717       image->alpha_trait=alpha_trait;
3718     }
3719
3720   png_read_end(ping,end_info);
3721
3722   if (logging != MagickFalse)
3723   {
3724     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3725        "  image->storage_class=%d\n",(int) image->storage_class);
3726   }
3727
3728   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3729       (ssize_t) image_info->first_scene && image->delay != 0)
3730     {
3731       png_destroy_read_struct(&ping,&ping_info,&end_info);
3732       pixel_info=RelinquishVirtualMemory(pixel_info);
3733       image->colors=2;
3734       (void) SetImageBackgroundColor(image,exception);
3735 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3736       UnlockSemaphoreInfo(ping_semaphore);
3737 #endif
3738       if (logging != MagickFalse)
3739         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3740           "  exit ReadOnePNGImage() early.");
3741       return(image);
3742     }
3743
3744   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3745     {
3746       ClassType
3747         storage_class;
3748
3749       /*
3750         Image has a transparent background.
3751       */
3752       storage_class=image->storage_class;
3753       image->alpha_trait=BlendPixelTrait;
3754
3755 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3756
3757       if (storage_class == PseudoClass)
3758         {
3759           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3760             {
3761               for (x=0; x < ping_num_trans; x++)
3762               {
3763                  image->colormap[x].alpha_trait=BlendPixelTrait;
3764                  image->colormap[x].alpha =
3765                    ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3766               }
3767             }
3768
3769           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3770             {
3771               for (x=0; x < (int) image->colors; x++)
3772               {
3773                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3774                      transparent_color.alpha)
3775                  {
3776                     image->colormap[x].alpha_trait=BlendPixelTrait;
3777                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3778                  }
3779               }
3780             }
3781           (void) SyncImage(image,exception);
3782         }
3783
3784 #if 1 /* Should have already been done above, but glennrp problem P10
3785        * needs this.
3786        */
3787       else
3788         {
3789           for (y=0; y < (ssize_t) image->rows; y++)
3790           {
3791             image->storage_class=storage_class;
3792             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3793
3794             if (q == (Quantum *) NULL)
3795               break;
3796
3797
3798             /* Caution: on a Q8 build, this does not distinguish between
3799              * 16-bit colors that differ only in the low byte
3800              */
3801             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3802             {
3803               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3804                   transparent_color.red &&
3805                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3806                   transparent_color.green &&
3807                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3808                   transparent_color.blue)
3809                 {
3810                   SetPixelAlpha(image,TransparentAlpha,q);
3811                 }
3812
3813 #if 0 /* I have not found a case where this is needed. */
3814               else
3815                 {
3816                   SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3817                 }
3818 #endif
3819
3820               q+=GetPixelChannels(image);
3821             }
3822
3823             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3824                break;
3825           }
3826         }
3827 #endif
3828
3829       image->storage_class=DirectClass;
3830     }
3831
3832   for (j = 0; j < 2; j++)
3833   {
3834     if (j == 0)
3835       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3836           MagickTrue : MagickFalse;
3837     else
3838       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3839           MagickTrue : MagickFalse;
3840
3841     if (status != MagickFalse)
3842       for (i=0; i < (ssize_t) num_text; i++)
3843       {
3844         /* Check for a profile */
3845
3846         if (logging != MagickFalse)
3847           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3848             "    Reading PNG text chunk");
3849
3850         if (strlen(text[i].key) > 16 &&
3851             memcmp(text[i].key, "Raw profile type ",17) == 0)
3852           {
3853             const char
3854               *value;
3855
3856             value=GetImageOption(image_info,"profile:skip");
3857
3858             if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3859             {
3860                (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3861                   (int) i,exception);
3862                num_raw_profiles++;
3863                if (logging != MagickFalse)
3864                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3865                    "    Read raw profile %s",text[i].key+17);
3866             }
3867             else
3868             {
3869                if (logging != MagickFalse)
3870                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3871                    "    Skipping raw profile %s",text[i].key+17);
3872             }
3873           }
3874
3875         else
3876           {
3877             char
3878               *value;
3879
3880             length=text[i].text_length;
3881             value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
3882               sizeof(*value));
3883             if (value == (char *) NULL)
3884               {
3885                 png_error(ping,"Memory allocation failed");
3886                 break;
3887               }
3888             *value='\0';
3889             (void) ConcatenateMagickString(value,text[i].text,length+2);
3890
3891             /* Don't save "density" or "units" property if we have a pHYs
3892              * chunk
3893              */
3894             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3895                 (LocaleCompare(text[i].key,"density") != 0 &&
3896                 LocaleCompare(text[i].key,"units") != 0))
3897                (void) SetImageProperty(image,text[i].key,value,exception);
3898
3899             if (logging != MagickFalse)
3900             {
3901               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3902                 "      length: %lu\n"
3903                 "      Keyword: %s",
3904                 (unsigned long) length,
3905                 text[i].key);
3906             }
3907
3908             value=DestroyString(value);
3909           }
3910       }
3911     num_text_total += num_text;
3912   }
3913
3914 #ifdef MNG_OBJECT_BUFFERS
3915   /*
3916     Store the object if necessary.
3917   */
3918   if (object_id && !mng_info->frozen[object_id])
3919     {
3920       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3921         {
3922           /*
3923             create a new object buffer.
3924           */
3925           mng_info->ob[object_id]=(MngBuffer *)
3926             AcquireMagickMemory(sizeof(MngBuffer));
3927
3928           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3929             {
3930               mng_info->ob[object_id]->image=(Image *) NULL;
3931               mng_info->ob[object_id]->reference_count=1;
3932             }
3933         }
3934
3935       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3936           mng_info->ob[object_id]->frozen)
3937         {
3938           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3939              png_error(ping,"Memory allocation failed");
3940
3941           if (mng_info->ob[object_id]->frozen)
3942             png_error(ping,"Cannot overwrite frozen MNG object buffer");
3943         }
3944
3945       else
3946         {
3947
3948           if (mng_info->ob[object_id]->image != (Image *) NULL)
3949             mng_info->ob[object_id]->image=DestroyImage
3950                 (mng_info->ob[object_id]->image);
3951
3952           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3953             exception);
3954
3955           if (mng_info->ob[object_id]->image != (Image *) NULL)
3956             mng_info->ob[object_id]->image->file=(FILE *) NULL;
3957
3958           else
3959             png_error(ping, "Cloning image for object buffer failed");
3960
3961           if (ping_width > 250000L || ping_height > 250000L)
3962              png_error(ping,"PNG Image dimensions are too large.");
3963
3964           mng_info->ob[object_id]->width=ping_width;
3965           mng_info->ob[object_id]->height=ping_height;
3966           mng_info->ob[object_id]->color_type=ping_color_type;
3967           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3968           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3969           mng_info->ob[object_id]->compression_method=
3970              ping_compression_method;
3971           mng_info->ob[object_id]->filter_method=ping_filter_method;
3972
3973           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3974             {
3975               png_colorp
3976                 plte;
3977
3978               /*
3979                 Copy the PLTE to the object buffer.
3980               */
3981               png_get_PLTE(ping,ping_info,&plte,&number_colors);
3982               mng_info->ob[object_id]->plte_length=number_colors;
3983
3984               for (i=0; i < number_colors; i++)
3985               {
3986                 mng_info->ob[object_id]->plte[i]=plte[i];
3987               }
3988             }
3989
3990           else
3991               mng_info->ob[object_id]->plte_length=0;
3992         }
3993     }
3994 #endif
3995
3996    /* Set image->alpha_trait to MagickTrue if the input colortype supports
3997     * alpha or if a valid tRNS chunk is present, no matter whether there
3998     * is actual transparency present.
3999     */
4000     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
4001         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
4002         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
4003         BlendPixelTrait : UndefinedPixelTrait;
4004
4005 #if 0  /* I'm not sure what's wrong here but it does not work. */
4006     if (image->alpha_trait != UndefinedPixelTrait)
4007     {
4008       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
4009         (void) SetImageType(image,GrayscaleAlphaType,exception);
4010
4011       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4012         (void) SetImageType(image,PaletteAlphaType,exception);
4013
4014       else
4015         (void) SetImageType(image,TrueColorAlphaType,exception);
4016     }
4017
4018     else
4019     {
4020       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
4021         (void) SetImageType(image,GrayscaleType,exception);
4022
4023       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4024         (void) SetImageType(image,PaletteType,exception);
4025
4026       else
4027         (void) SetImageType(image,TrueColorType,exception);
4028     }
4029 #endif
4030
4031    /* Set more properties for identify to retrieve */
4032    {
4033      char
4034        msg[MagickPathExtent];
4035
4036      if (num_text_total != 0)
4037        {
4038          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
4039          (void) FormatLocaleString(msg,MagickPathExtent,
4040             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
4041          (void) SetImageProperty(image,"png:text",msg,
4042                 exception);
4043        }
4044
4045      if (num_raw_profiles != 0)
4046        {
4047          (void) FormatLocaleString(msg,MagickPathExtent,
4048             "%d were found", num_raw_profiles);
4049          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
4050                 exception);
4051        }
4052
4053      /* cHRM chunk: */
4054      if (ping_found_cHRM != MagickFalse)
4055        {
4056          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4057             "chunk was found (see Chromaticity, above)");
4058          (void) SetImageProperty(image,"png:cHRM",msg,
4059                 exception);
4060        }
4061
4062      /* bKGD chunk: */
4063      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
4064        {
4065          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4066             "chunk was found (see Background color, above)");
4067          (void) SetImageProperty(image,"png:bKGD",msg,
4068                 exception);
4069        }
4070
4071      (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4072         "chunk was found");
4073
4074 #if defined(PNG_iCCP_SUPPORTED)
4075      /* iCCP chunk: */
4076      if (ping_found_iCCP != MagickFalse)
4077         (void) SetImageProperty(image,"png:iCCP",msg,
4078                 exception);
4079 #endif
4080
4081      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
4082         (void) SetImageProperty(image,"png:tRNS",msg,
4083                 exception);
4084
4085 #if defined(PNG_sRGB_SUPPORTED)
4086      /* sRGB chunk: */
4087      if (ping_found_sRGB != MagickFalse)
4088        {
4089          (void) FormatLocaleString(msg,MagickPathExtent,
4090             "intent=%d (%s)",
4091             (int) intent,
4092             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
4093          (void) SetImageProperty(image,"png:sRGB",msg,
4094                  exception);
4095        }
4096 #endif
4097
4098      /* gAMA chunk: */
4099      if (ping_found_gAMA != MagickFalse)
4100        {
4101          (void) FormatLocaleString(msg,MagickPathExtent,
4102             "gamma=%.8g (See Gamma, above)",
4103             file_gamma);
4104          (void) SetImageProperty(image,"png:gAMA",msg,
4105                 exception);
4106        }
4107
4108 #if defined(PNG_pHYs_SUPPORTED)
4109      /* pHYs chunk: */
4110      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
4111        {
4112          (void) FormatLocaleString(msg,MagickPathExtent,
4113             "x_res=%.10g, y_res=%.10g, units=%d",
4114             (double) x_resolution,(double) y_resolution, unit_type);
4115          (void) SetImageProperty(image,"png:pHYs",msg,
4116                 exception);
4117        }
4118 #endif
4119
4120 #if defined(PNG_oFFs_SUPPORTED)
4121      /* oFFs chunk: */
4122      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4123        {
4124          (void) FormatLocaleString(msg,MagickPathExtent,
4125             "x_off=%.20g, y_off=%.20g",
4126             (double) image->page.x,(double) image->page.y);
4127          (void) SetImageProperty(image,"png:oFFs",msg,
4128                 exception);
4129        }
4130 #endif
4131
4132 #if defined(PNG_tIME_SUPPORTED)
4133      read_tIME_chunk(image,ping,end_info,exception);
4134 #endif
4135
4136      /* caNv chunk: */
4137      if ((image->page.width != 0 && image->page.width != image->columns) ||
4138          (image->page.height != 0 && image->page.height != image->rows) ||
4139          (image->page.x != 0 || image->page.y != 0))
4140        {
4141          (void) FormatLocaleString(msg,MagickPathExtent,
4142             "width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
4143             (double) image->page.width,(double) image->page.height,
4144             (double) image->page.x,(double) image->page.y);
4145          (void) SetImageProperty(image,"png:caNv",msg,
4146                 exception);
4147        }
4148    }
4149
4150   /*
4151     Relinquish resources.
4152   */
4153   png_destroy_read_struct(&ping,&ping_info,&end_info);
4154
4155   pixel_info=RelinquishVirtualMemory(pixel_info);
4156
4157   if (logging != MagickFalse)
4158     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4159       "  exit ReadOnePNGImage()");
4160
4161 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4162   UnlockSemaphoreInfo(ping_semaphore);
4163 #endif
4164
4165   /* }  for navigation to beginning of SETJMP-protected block, revert to
4166    *    Throwing an Exception when an error occurs.
4167    */
4168
4169   return(image);
4170
4171 /* end of reading one PNG image */
4172 }
4173
4174 static Image *ReadPNGImage(const ImageInfo *image_info,
4175   ExceptionInfo *exception)
4176 {
4177   Image
4178     *image;
4179
4180   MagickBooleanType
4181     logging,
4182     status;
4183
4184   MngInfo
4185     *mng_info;
4186
4187   char
4188     magic_number[MagickPathExtent];
4189
4190   ssize_t
4191     count;
4192
4193   /*
4194     Open image file.
4195   */
4196   assert(image_info != (const ImageInfo *) NULL);
4197   assert(image_info->signature == MagickCoreSignature);
4198
4199   if (image_info->debug != MagickFalse)
4200     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4201       image_info->filename);
4202
4203   assert(exception != (ExceptionInfo *) NULL);
4204   assert(exception->signature == MagickCoreSignature);
4205   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4206   image=AcquireImage(image_info,exception);
4207   mng_info=(MngInfo *) NULL;
4208   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4209
4210   if (status == MagickFalse)
4211     ThrowReaderException(FileOpenError,"UnableToOpenFile");
4212
4213   /*
4214     Verify PNG signature.
4215   */
4216   count=ReadBlob(image,8,(unsigned char *) magic_number);
4217
4218   if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4219     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4220
4221   /*
4222      Verify that file size large enough to contain a PNG datastream.
4223   */
4224   if (GetBlobSize(image) < 61)
4225     ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
4226
4227   /*
4228     Allocate a MngInfo structure.
4229   */
4230   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4231
4232   if (mng_info == (MngInfo *) NULL)
4233     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4234
4235   /*
4236     Initialize members of the MngInfo structure.
4237   */
4238   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4239   mng_info->image=image;
4240
4241   image=ReadOnePNGImage(mng_info,image_info,exception);
4242   mng_info=MngInfoFreeStruct(mng_info);
4243
4244   if (image == (Image *) NULL)
4245     {
4246       if (logging != MagickFalse)
4247         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4248           "exit ReadPNGImage() with error");
4249
4250       return((Image *) NULL);
4251     }
4252
4253   (void) CloseBlob(image);
4254
4255   if ((image->columns == 0) || (image->rows == 0))
4256     {
4257       if (logging != MagickFalse)
4258         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4259           "exit ReadPNGImage() with error.");
4260
4261       ThrowReaderException(CorruptImageError,"CorruptImage");
4262     }
4263
4264   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4265       ((image->gamma < .45) || (image->gamma > .46)) &&
4266            !(image->chromaticity.red_primary.x>0.6399f &&
4267            image->chromaticity.red_primary.x<0.6401f &&
4268            image->chromaticity.red_primary.y>0.3299f &&
4269            image->chromaticity.red_primary.y<0.3301f &&
4270            image->chromaticity.green_primary.x>0.2999f &&
4271            image->chromaticity.green_primary.x<0.3001f &&
4272            image->chromaticity.green_primary.y>0.5999f &&
4273            image->chromaticity.green_primary.y<0.6001f &&
4274            image->chromaticity.blue_primary.x>0.1499f &&
4275            image->chromaticity.blue_primary.x<0.1501f &&
4276            image->chromaticity.blue_primary.y>0.0599f &&
4277            image->chromaticity.blue_primary.y<0.0601f &&
4278            image->chromaticity.white_point.x>0.3126f &&
4279            image->chromaticity.white_point.x<0.3128f &&
4280            image->chromaticity.white_point.y>0.3289f &&
4281            image->chromaticity.white_point.y<0.3291f))
4282     {
4283        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4284           "SetImageColorspace to RGBColorspace");
4285        SetImageColorspace(image,RGBColorspace,exception);
4286     }
4287     else
4288     {
4289        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4290           "NOT SetImageColorspace to RGBColorspace, image->gamma=%g",
4291         image->gamma);
4292     }
4293
4294   if (logging != MagickFalse)
4295     {
4296        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4297            "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4298                (double) image->page.width,(double) image->page.height,
4299                (double) image->page.x,(double) image->page.y);
4300        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4301            "  image->colorspace: %d", (int) image->colorspace);
4302     }
4303
4304   if (logging != MagickFalse)
4305     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4306
4307   return(image);
4308 }
4309
4310
4311
4312 #if defined(JNG_SUPPORTED)
4313 /*
4314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4315 %                                                                             %
4316 %                                                                             %
4317 %                                                                             %
4318 %   R e a d O n e J N G I m a g e                                             %
4319 %                                                                             %
4320 %                                                                             %
4321 %                                                                             %
4322 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4323 %
4324 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4325 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
4326 %  necessary for the new Image structure and returns a pointer to the new
4327 %  image.
4328 %
4329 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4330 %
4331 %  The format of the ReadOneJNGImage method is:
4332 %
4333 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4334 %         ExceptionInfo *exception)
4335 %
4336 %  A description of each parameter follows:
4337 %
4338 %    o mng_info: Specifies a pointer to a MngInfo structure.
4339 %
4340 %    o image_info: the image info.
4341 %
4342 %    o exception: return any errors or warnings in this structure.
4343 %
4344 */
4345 void
4346 DestroyJNG(unsigned char *chunk,Image **color_image,
4347    ImageInfo **color_image_info,
4348    Image **alpha_image,ImageInfo **alpha_image_info)
4349 {
4350   (void) RelinquishMagickMemory(chunk);
4351   if (*color_image_info)
4352   {
4353     DestroyImageInfo(*color_image_info);
4354     *color_image_info = (ImageInfo *)NULL;
4355   }
4356   if (*alpha_image_info)
4357   {
4358     DestroyImageInfo(*alpha_image_info);
4359     *alpha_image_info = (ImageInfo *)NULL;
4360   }
4361   if (*color_image)
4362   {
4363     DestroyImage(*color_image);
4364     *color_image = (Image *)NULL;
4365   }
4366   if (*alpha_image)
4367   {
4368     DestroyImage(*alpha_image);
4369     *alpha_image = (Image *)NULL;
4370   }
4371 }
4372 static Image *ReadOneJNGImage(MngInfo *mng_info,
4373     const ImageInfo *image_info, ExceptionInfo *exception)
4374 {
4375   Image
4376     *alpha_image,
4377     *color_image,
4378     *image,
4379     *jng_image;
4380
4381   ImageInfo
4382     *alpha_image_info,
4383     *color_image_info;
4384
4385   MagickBooleanType
4386     logging;
4387
4388   ssize_t
4389     y;
4390
4391   MagickBooleanType
4392     status;
4393
4394   png_uint_32
4395     jng_height,
4396     jng_width;
4397
4398   png_byte
4399     jng_color_type,
4400     jng_image_sample_depth,
4401     jng_image_compression_method,
4402     jng_image_interlace_method,
4403     jng_alpha_sample_depth,
4404     jng_alpha_compression_method,
4405     jng_alpha_filter_method,
4406     jng_alpha_interlace_method;
4407
4408   register const Quantum
4409     *s;
4410
4411   register ssize_t
4412     i,
4413     x;
4414
4415   register Quantum
4416     *q;
4417
4418   register unsigned char
4419     *p;
4420
4421   unsigned int
4422     read_JSEP,
4423     reading_idat;
4424
4425   size_t
4426     length;
4427
4428   jng_alpha_compression_method=0;
4429   jng_alpha_sample_depth=8;
4430   jng_color_type=0;
4431   jng_height=0;
4432   jng_width=0;
4433   alpha_image=(Image *) NULL;
4434   color_image=(Image *) NULL;
4435   alpha_image_info=(ImageInfo *) NULL;
4436   color_image_info=(ImageInfo *) NULL;
4437
4438   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4439     "  Enter ReadOneJNGImage()");
4440
4441   image=mng_info->image;
4442
4443   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4444     {
4445       /*
4446         Allocate next image structure.
4447       */
4448       if (logging != MagickFalse)
4449         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4450            "  AcquireNextImage()");
4451
4452       AcquireNextImage(image_info,image,exception);
4453
4454       if (GetNextImageInList(image) == (Image *) NULL)
4455         return(DestroyImageList(image));
4456
4457       image=SyncNextImageInList(image);
4458     }
4459   mng_info->image=image;
4460
4461   /*
4462     Signature bytes have already been read.
4463   */
4464
4465   read_JSEP=MagickFalse;
4466   reading_idat=MagickFalse;
4467   for (;;)
4468   {
4469     char
4470       type[MagickPathExtent];
4471
4472     unsigned char
4473       *chunk;
4474
4475     unsigned int
4476       count;
4477
4478     /*
4479       Read a new JNG chunk.
4480     */
4481     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4482       2*GetBlobSize(image));
4483
4484     if (status == MagickFalse)
4485       break;
4486
4487     type[0]='\0';
4488     (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4489     length=ReadBlobMSBLong(image);
4490     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4491
4492     if (logging != MagickFalse)
4493       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4494         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4495         type[0],type[1],type[2],type[3],(double) length);
4496
4497     if (length > PNG_UINT_31_MAX || count == 0)
4498       {
4499         DestroyJNG(NULL,&color_image,&color_image_info,
4500           &alpha_image,&alpha_image_info);
4501         ThrowReaderException(CorruptImageError,"CorruptImage");
4502       }
4503
4504     p=NULL;
4505     chunk=(unsigned char *) NULL;
4506
4507     if (length != 0)
4508       {
4509         if (length > GetBlobSize(image))
4510           {
4511             DestroyJNG(NULL,&color_image,&color_image_info,NULL,NULL);
4512             ThrowReaderException(CorruptImageError,
4513               "InsufficientImageDataInFile");
4514           }
4515         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4516
4517         if (chunk == (unsigned char *) NULL)
4518           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4519
4520         for (i=0; i < (ssize_t) length; i++)
4521         {
4522           int
4523             c;
4524
4525           c=ReadBlobByte(image);
4526           if (c == EOF)
4527             break;
4528           chunk[i]=(unsigned char) c;
4529         }
4530
4531         p=chunk;
4532       }
4533
4534     (void) ReadBlobMSBLong(image);  /* read crc word */
4535
4536     if (memcmp(type,mng_JHDR,4) == 0)
4537       {
4538         if (length == 16)
4539           {
4540             jng_width=(png_uint_32)mng_get_long(p);
4541             jng_height=(png_uint_32)mng_get_long(&p[4]);
4542             if ((jng_width == 0) || (jng_height == 0))
4543               ThrowReaderException(CorruptImageError,
4544                 "NegativeOrZeroImageSize");
4545             jng_color_type=p[8];
4546             jng_image_sample_depth=p[9];
4547             jng_image_compression_method=p[10];
4548             jng_image_interlace_method=p[11];
4549
4550             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4551               NoInterlace;
4552
4553             jng_alpha_sample_depth=p[12];
4554             jng_alpha_compression_method=p[13];
4555             jng_alpha_filter_method=p[14];
4556             jng_alpha_interlace_method=p[15];
4557
4558             if (logging != MagickFalse)
4559               {
4560                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4561                   "    jng_width:      %16lu,    jng_height:     %16lu\n"
4562                   "    jng_color_type: %16d,     jng_image_sample_depth: %3d\n"
4563                   "    jng_image_compression_method:%3d",
4564                   (unsigned long) jng_width, (unsigned long) jng_height,
4565                   jng_color_type, jng_image_sample_depth,
4566                   jng_image_compression_method);
4567
4568                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4569                   "    jng_image_interlace_method:  %3d"
4570                   "    jng_alpha_sample_depth:      %3d",
4571                   jng_image_interlace_method,
4572                   jng_alpha_sample_depth);
4573
4574                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4575                   "    jng_alpha_compression_method:%3d\n"
4576                   "    jng_alpha_filter_method:     %3d\n"
4577                   "    jng_alpha_interlace_method:  %3d",
4578                   jng_alpha_compression_method,
4579                   jng_alpha_filter_method,
4580                   jng_alpha_interlace_method);
4581               }
4582           }
4583
4584         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4585
4586         if (jng_width > 65535 || jng_height > 65535 ||
4587              (long) jng_width > GetMagickResourceLimit(WidthResource) ||
4588              (long) jng_height > GetMagickResourceLimit(HeightResource))
4589           {
4590             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4591                "    JNG width or height too large: (%lu x %lu)",
4592                 (long) jng_width, (long) jng_height);
4593             DestroyJNG(chunk,&color_image,&color_image_info,
4594               &alpha_image,&alpha_image_info);
4595             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4596           }
4597
4598         continue;
4599       }
4600
4601
4602     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4603         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4604          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4605       {
4606         /*
4607            o create color_image
4608            o open color_blob, attached to color_image
4609            o if (color type has alpha)
4610                open alpha_blob, attached to alpha_image
4611         */
4612
4613         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4614
4615         if (color_image_info == (ImageInfo *) NULL)
4616           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4617
4618         GetImageInfo(color_image_info);
4619         color_image=AcquireImage(color_image_info,exception);
4620
4621         if (color_image == (Image *) NULL)
4622           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4623
4624         if (logging != MagickFalse)
4625           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4626             "    Creating color_blob.");
4627
4628         (void) AcquireUniqueFilename(color_image->filename);
4629         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4630           exception);
4631
4632         if (status == MagickFalse)
4633           {
4634             color_image=DestroyImage(color_image);
4635             return(DestroyImageList(image));
4636           }
4637
4638         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4639           {
4640             alpha_image_info=(ImageInfo *)
4641               AcquireMagickMemory(sizeof(ImageInfo));
4642
4643             if (alpha_image_info == (ImageInfo *) NULL)
4644               {
4645                 color_image=DestroyImage(color_image);
4646                 ThrowReaderException(ResourceLimitError,
4647                   "MemoryAllocationFailed");
4648               }
4649
4650             GetImageInfo(alpha_image_info);
4651             alpha_image=AcquireImage(alpha_image_info,exception);
4652
4653             if (alpha_image == (Image *) NULL)
4654               {
4655                 alpha_image_info=DestroyImageInfo(alpha_image_info);
4656                 color_image=DestroyImage(color_image);
4657                 ThrowReaderException(ResourceLimitError,
4658                   "MemoryAllocationFailed");
4659               }
4660
4661             if (logging != MagickFalse)
4662               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4663                 "    Creating alpha_blob.");
4664
4665             (void) AcquireUniqueFilename(alpha_image->filename);
4666             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4667               exception);
4668
4669             if (status == MagickFalse)
4670               {
4671                 alpha_image=DestroyImage(alpha_image);
4672                 alpha_image_info=DestroyImageInfo(alpha_image_info);
4673                 color_image=DestroyImage(color_image);
4674                 return(DestroyImageList(image));
4675               }
4676
4677             if (jng_alpha_compression_method == 0)
4678               {
4679                 unsigned char
4680                   data[18];
4681
4682                 if (logging != MagickFalse)
4683                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4684                     "    Writing IHDR chunk to alpha_blob.");
4685
4686                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4687                   "\211PNG\r\n\032\n");
4688
4689                 (void) WriteBlobMSBULong(alpha_image,13L);
4690                 PNGType(data,mng_IHDR);
4691                 LogPNGChunk(logging,mng_IHDR,13L);
4692                 PNGLong(data+4,jng_width);
4693                 PNGLong(data+8,jng_height);
4694                 data[12]=jng_alpha_sample_depth;
4695                 data[13]=0; /* color_type gray */
4696                 data[14]=0; /* compression method 0 */
4697                 data[15]=0; /* filter_method 0 */
4698                 data[16]=0; /* interlace_method 0 */
4699                 (void) WriteBlob(alpha_image,17,data);
4700                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4701               }
4702           }
4703         reading_idat=MagickTrue;
4704       }
4705
4706     if (memcmp(type,mng_JDAT,4) == 0)
4707       {
4708         /* Copy chunk to color_image->blob */
4709
4710         if (logging != MagickFalse)
4711           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4712             "    Copying JDAT chunk data to color_blob.");
4713
4714         if (length != 0)
4715           {
4716             (void) WriteBlob(color_image,length,chunk);
4717             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4718           }
4719
4720         continue;
4721       }
4722
4723     if (memcmp(type,mng_IDAT,4) == 0)
4724       {
4725         png_byte
4726            data[5];
4727
4728         /* Copy IDAT header and chunk data to alpha_image->blob */
4729
4730         if (alpha_image != NULL && image_info->ping == MagickFalse)
4731           {
4732             if (logging != MagickFalse)
4733               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4734                 "    Copying IDAT chunk data to alpha_blob.");
4735
4736             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4737             PNGType(data,mng_IDAT);
4738             LogPNGChunk(logging,mng_IDAT,length);
4739             (void) WriteBlob(alpha_image,4,data);
4740             (void) WriteBlob(alpha_image,length,chunk);
4741             (void) WriteBlobMSBULong(alpha_image,
4742               crc32(crc32(0,data,4),chunk,(uInt) length));
4743           }
4744
4745         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4746
4747         continue;
4748       }
4749
4750     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4751       {
4752         /* Copy chunk data to alpha_image->blob */
4753
4754         if (alpha_image != NULL && image_info->ping == MagickFalse)
4755           {
4756             if (logging != MagickFalse)
4757               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4758                 "    Copying JDAA chunk data to alpha_blob.");
4759
4760             (void) WriteBlob(alpha_image,length,chunk);
4761           }
4762
4763         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4764
4765         continue;
4766       }
4767
4768     if (memcmp(type,mng_JSEP,4) == 0)
4769       {
4770         read_JSEP=MagickTrue;
4771
4772         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4773
4774         continue;
4775       }
4776
4777     if (memcmp(type,mng_bKGD,4) == 0)
4778       {
4779         if (length == 2)
4780           {
4781             image->background_color.red=ScaleCharToQuantum(p[1]);
4782             image->background_color.green=image->background_color.red;
4783             image->background_color.blue=image->background_color.red;
4784           }
4785
4786         if (length == 6)
4787           {
4788             image->background_color.red=ScaleCharToQuantum(p[1]);
4789             image->background_color.green=ScaleCharToQuantum(p[3]);
4790             image->background_color.blue=ScaleCharToQuantum(p[5]);
4791           }
4792
4793         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4794         continue;
4795       }
4796
4797     if (memcmp(type,mng_gAMA,4) == 0)
4798       {
4799         if (length == 4)
4800           image->gamma=((float) mng_get_long(p))*0.00001;
4801
4802         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4803         continue;
4804       }
4805
4806     if (memcmp(type,mng_cHRM,4) == 0)
4807       {
4808         if (length == 32)
4809           {
4810             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4811             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4812             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4813             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4814             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4815             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4816             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4817             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4818           }
4819
4820         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4821         continue;
4822       }
4823
4824     if (memcmp(type,mng_sRGB,4) == 0)
4825       {
4826         if (length == 1)
4827           {
4828             image->rendering_intent=
4829               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4830             image->gamma=1.000f/2.200f;
4831             image->chromaticity.red_primary.x=0.6400f;
4832             image->chromaticity.red_primary.y=0.3300f;
4833             image->chromaticity.green_primary.x=0.3000f;
4834             image->chromaticity.green_primary.y=0.6000f;
4835             image->chromaticity.blue_primary.x=0.1500f;
4836             image->chromaticity.blue_primary.y=0.0600f;
4837             image->chromaticity.white_point.x=0.3127f;
4838             image->chromaticity.white_point.y=0.3290f;
4839           }
4840
4841         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4842         continue;
4843       }
4844
4845     if (memcmp(type,mng_oFFs,4) == 0)
4846       {
4847         if (length > 8)
4848           {
4849             image->page.x=(ssize_t) mng_get_long(p);
4850             image->page.y=(ssize_t) mng_get_long(&p[4]);
4851
4852             if ((int) p[8] != 0)
4853               {
4854                 image->page.x/=10000;
4855                 image->page.y/=10000;
4856               }
4857           }
4858
4859         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4860
4861         continue;
4862       }
4863
4864     if (memcmp(type,mng_pHYs,4) == 0)
4865       {
4866         if (length > 8)
4867           {
4868             image->resolution.x=(double) mng_get_long(p);
4869             image->resolution.y=(double) mng_get_long(&p[4]);
4870             if ((int) p[8] == PNG_RESOLUTION_METER)
4871               {
4872                 image->units=PixelsPerCentimeterResolution;
4873                 image->resolution.x=image->resolution.x/100.0f;
4874                 image->resolution.y=image->resolution.y/100.0f;
4875               }
4876           }
4877
4878         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4879         continue;
4880       }
4881
4882 #if 0
4883     if (memcmp(type,mng_iCCP,4) == 0)
4884       {
4885         /* To do: */
4886         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4887
4888         continue;
4889       }
4890 #endif
4891
4892     chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4893
4894     if (memcmp(type,mng_IEND,4))
4895       continue;
4896
4897     break;
4898   }
4899
4900
4901   /* IEND found */
4902
4903   /*
4904     Finish up reading image data:
4905
4906        o read main image from color_blob.
4907
4908        o close color_blob.
4909
4910        o if (color_type has alpha)
4911             if alpha_encoding is PNG
4912                read secondary image from alpha_blob via ReadPNG
4913             if alpha_encoding is JPEG
4914                read secondary image from alpha_blob via ReadJPEG
4915
4916        o close alpha_blob.
4917
4918        o copy intensity of secondary image into
4919          alpha samples of main image.
4920
4921        o destroy the secondary image.
4922   */
4923
4924   if (color_image_info == (ImageInfo *) NULL)
4925     {
4926       assert(color_image == (Image *) NULL);
4927       assert(alpha_image == (Image *) NULL);
4928       return(DestroyImageList(image));
4929     }
4930
4931   if (color_image == (Image *) NULL)
4932     {
4933       assert(alpha_image == (Image *) NULL);
4934       return(DestroyImageList(image));
4935     }
4936
4937   (void) SeekBlob(color_image,0,SEEK_SET);
4938
4939   if (logging != MagickFalse)
4940     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4941       "    Reading jng_image from color_blob.");
4942
4943   assert(color_image_info != (ImageInfo *) NULL);
4944   (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,"%s",
4945     color_image->filename);
4946
4947   color_image_info->ping=MagickFalse;   /* To do: avoid this */
4948   jng_image=ReadImage(color_image_info,exception);
4949
4950   (void) RelinquishUniqueFileResource(color_image->filename);
4951   color_image=DestroyImage(color_image);
4952   color_image_info=DestroyImageInfo(color_image_info);
4953
4954   if (jng_image == (Image *) NULL)
4955     return(DestroyImageList(image));
4956
4957   if (logging != MagickFalse)
4958     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4959       "    Copying jng_image pixels to main image.");
4960
4961   image->rows=jng_height;
4962   image->columns=jng_width;
4963
4964   status=SetImageExtent(image,image->columns,image->rows,exception);
4965   if (status == MagickFalse)
4966     return(DestroyImageList(image));
4967
4968   for (y=0; y < (ssize_t) image->rows; y++)
4969   {
4970     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4971     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4972     for (x=(ssize_t) image->columns; x != 0; x--)
4973     {
4974       SetPixelRed(image,GetPixelRed(jng_image,s),q);
4975       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4976       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4977       q+=GetPixelChannels(image);
4978       s+=GetPixelChannels(jng_image);
4979     }
4980
4981     if (SyncAuthenticPixels(image,exception) == MagickFalse)
4982       break;
4983   }
4984
4985   jng_image=DestroyImage(jng_image);
4986
4987   if (image_info->ping == MagickFalse)
4988     {
4989      if (jng_color_type >= 12)
4990        {
4991          if (jng_alpha_compression_method == 0)
4992            {
4993              png_byte
4994                data[5];
4995              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4996              PNGType(data,mng_IEND);
4997              LogPNGChunk(logging,mng_IEND,0L);
4998              (void) WriteBlob(alpha_image,4,data);
4999              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
5000            }
5001
5002          (void) CloseBlob(alpha_image);
5003
5004          if (logging != MagickFalse)
5005            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5006              "    Reading alpha from alpha_blob.");
5007
5008          (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
5009            "%s",alpha_image->filename);
5010
5011          jng_image=ReadImage(alpha_image_info,exception);
5012
5013          if (jng_image != (Image *) NULL)
5014            for (y=0; y < (ssize_t) image->rows; y++)
5015            {
5016              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
5017                exception);
5018              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5019
5020              if (image->alpha_trait != UndefinedPixelTrait)
5021                for (x=(ssize_t) image->columns; x != 0; x--)
5022                {
5023                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5024                   q+=GetPixelChannels(image);
5025                   s+=GetPixelChannels(jng_image);
5026                }
5027
5028              else
5029                for (x=(ssize_t) image->columns; x != 0; x--)
5030                {
5031                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5032                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
5033                     image->alpha_trait=BlendPixelTrait;
5034                   q+=GetPixelChannels(image);
5035                   s+=GetPixelChannels(jng_image);
5036                }
5037
5038              if (SyncAuthenticPixels(image,exception) == MagickFalse)
5039                break;
5040            }
5041          (void) RelinquishUniqueFileResource(alpha_image->filename);
5042          alpha_image=DestroyImage(alpha_image);
5043          alpha_image_info=DestroyImageInfo(alpha_image_info);
5044          if (jng_image != (Image *) NULL)
5045            jng_image=DestroyImage(jng_image);
5046        }
5047     }
5048
5049   /* Read the JNG image.  */
5050
5051   if (mng_info->mng_type == 0)
5052     {
5053       mng_info->mng_width=jng_width;
5054       mng_info->mng_height=jng_height;
5055     }
5056
5057   if (image->page.width == 0 && image->page.height == 0)
5058     {
5059       image->page.width=jng_width;
5060       image->page.height=jng_height;
5061     }
5062
5063   if (image->page.x == 0 && image->page.y == 0)
5064     {
5065       image->page.x=mng_info->x_off[mng_info->object_id];
5066       image->page.y=mng_info->y_off[mng_info->object_id];
5067     }
5068
5069   else
5070     {
5071       image->page.y=mng_info->y_off[mng_info->object_id];
5072     }
5073
5074   mng_info->image_found++;
5075   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
5076     2*GetBlobSize(image));
5077
5078   if (status == MagickFalse)
5079     return(DestroyImageList(image));
5080
5081   if (logging != MagickFalse)
5082     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5083       "  exit ReadOneJNGImage()");
5084
5085   return(image);
5086 }
5087
5088 /*
5089 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5090 %                                                                             %
5091 %                                                                             %
5092 %                                                                             %
5093 %   R e a d J N G I m a g e                                                   %
5094 %                                                                             %
5095 %                                                                             %
5096 %                                                                             %
5097 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5098 %
5099 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
5100 %  (including the 8-byte signature)  and returns it.  It allocates the memory
5101 %  necessary for the new Image structure and returns a pointer to the new
5102 %  image.
5103 %
5104 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
5105 %
5106 %  The format of the ReadJNGImage method is:
5107 %
5108 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
5109 %         *exception)
5110 %
5111 %  A description of each parameter follows:
5112 %
5113 %    o image_info: the image info.
5114 %
5115 %    o exception: return any errors or warnings in this structure.
5116 %
5117 */
5118
5119 static Image *ReadJNGImage(const ImageInfo *image_info,
5120                 ExceptionInfo *exception)
5121 {
5122   Image
5123     *image;
5124
5125   MagickBooleanType
5126     logging,
5127     status;
5128
5129   MngInfo
5130     *mng_info;
5131
5132   char
5133     magic_number[MagickPathExtent];
5134
5135   size_t
5136     count;
5137
5138   /*
5139     Open image file.
5140   */
5141   assert(image_info != (const ImageInfo *) NULL);
5142   assert(image_info->signature == MagickCoreSignature);
5143   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5144      image_info->filename);
5145   assert(exception != (ExceptionInfo *) NULL);
5146   assert(exception->signature == MagickCoreSignature);
5147   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
5148   image=AcquireImage(image_info,exception);
5149   mng_info=(MngInfo *) NULL;
5150   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5151
5152   if (status == MagickFalse)
5153     return((Image *) NULL);
5154
5155   if (LocaleCompare(image_info->magick,"JNG") != 0)
5156     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5157
5158   /* Verify JNG signature.  */
5159
5160   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5161
5162   if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
5163     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5164
5165   /*
5166      Verify that file size large enough to contain a JNG datastream.
5167   */
5168   if (GetBlobSize(image) < 147)
5169     ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5170
5171   /* Allocate a MngInfo structure.  */
5172
5173   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
5174
5175   if (mng_info == (MngInfo *) NULL)
5176     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5177
5178   /* Initialize members of the MngInfo structure.  */
5179
5180   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5181
5182   mng_info->image=image;
5183   image=ReadOneJNGImage(mng_info,image_info,exception);
5184   mng_info=MngInfoFreeStruct(mng_info);
5185
5186   if (image == (Image *) NULL)
5187     {
5188       if (logging != MagickFalse)
5189         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5190           "exit ReadJNGImage() with error");
5191
5192       return((Image *) NULL);
5193     }
5194   (void) CloseBlob(image);
5195
5196   if (image->columns == 0 || image->rows == 0)
5197     {
5198       if (logging != MagickFalse)
5199         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5200           "exit ReadJNGImage() with error");
5201
5202       ThrowReaderException(CorruptImageError,"CorruptImage");
5203     }
5204
5205   if (logging != MagickFalse)
5206     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5207
5208   return(image);
5209 }
5210 #endif
5211
5212 static Image *ReadOneMNGImage(MngInfo* mng_info, const ImageInfo *image_info,
5213      ExceptionInfo *exception)
5214 {
5215   char
5216     page_geometry[MagickPathExtent];
5217
5218   Image
5219     *image;
5220
5221   MagickBooleanType
5222     logging;
5223
5224   volatile int
5225     first_mng_object,
5226     object_id,
5227     term_chunk_found,
5228     skip_to_iend;
5229
5230   volatile ssize_t
5231     image_count=0;
5232
5233   MagickBooleanType
5234     status;
5235
5236   MagickOffsetType
5237     offset;
5238
5239   MngBox
5240     default_fb,
5241     fb,
5242     previous_fb;
5243
5244 #if defined(MNG_INSERT_LAYERS)
5245   PixelInfo
5246     mng_background_color;
5247 #endif
5248
5249   register unsigned char
5250     *p;
5251
5252   register ssize_t
5253     i;
5254
5255   size_t
5256     count;
5257
5258   ssize_t
5259     loop_level;
5260
5261   volatile short
5262     skipping_loop;
5263
5264 #if defined(MNG_INSERT_LAYERS)
5265   unsigned int
5266     mandatory_back=0;
5267 #endif
5268
5269   volatile unsigned int
5270 #ifdef MNG_OBJECT_BUFFERS
5271     mng_background_object=0,
5272 #endif
5273     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5274
5275   size_t
5276     default_frame_timeout,
5277     frame_timeout,
5278 #if defined(MNG_INSERT_LAYERS)
5279     image_height,
5280     image_width,
5281 #endif
5282     length;
5283
5284   /* These delays are all measured in image ticks_per_second,
5285    * not in MNG ticks_per_second
5286    */
5287   volatile size_t
5288     default_frame_delay,
5289     final_delay,
5290     final_image_delay,
5291     frame_delay,
5292 #if defined(MNG_INSERT_LAYERS)
5293     insert_layers,
5294 #endif
5295     mng_iterations=1,
5296     simplicity=0,
5297     subframe_height=0,
5298     subframe_width=0;
5299
5300   previous_fb.top=0;
5301   previous_fb.bottom=0;
5302   previous_fb.left=0;
5303   previous_fb.right=0;
5304   default_fb.top=0;
5305   default_fb.bottom=0;
5306   default_fb.left=0;
5307   default_fb.right=0;
5308
5309   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
5310     "  Enter ReadOneMNGImage()");
5311
5312   image=mng_info->image;
5313
5314   if (LocaleCompare(image_info->magick,"MNG") == 0)
5315     {
5316       char
5317         magic_number[MagickPathExtent];
5318
5319       /* Verify MNG signature.  */
5320       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5321       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5322         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5323
5324       /* Initialize some nonzero members of the MngInfo structure.  */
5325       for (i=0; i < MNG_MAX_OBJECTS; i++)
5326       {
5327         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5328         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5329       }
5330       mng_info->exists[0]=MagickTrue;
5331     }
5332
5333   skipping_loop=(-1);
5334   first_mng_object=MagickTrue;
5335   mng_type=0;
5336 #if defined(MNG_INSERT_LAYERS)
5337   insert_layers=MagickFalse; /* should be False during convert or mogrify */
5338 #endif
5339   default_frame_delay=0;
5340   default_frame_timeout=0;
5341   frame_delay=0;
5342   final_delay=1;
5343   mng_info->ticks_per_second=1UL*image->ticks_per_second;
5344   object_id=0;
5345   skip_to_iend=MagickFalse;
5346   term_chunk_found=MagickFalse;
5347   mng_info->framing_mode=1;
5348 #if defined(MNG_INSERT_LAYERS)
5349   mandatory_back=MagickFalse;
5350 #endif
5351 #if defined(MNG_INSERT_LAYERS)
5352   mng_background_color=image->background_color;
5353 #endif
5354   default_fb=mng_info->frame;
5355   previous_fb=mng_info->frame;
5356   do
5357   {
5358     char
5359       type[MagickPathExtent];
5360
5361     if (LocaleCompare(image_info->magick,"MNG") == 0)
5362       {
5363         unsigned char
5364           *chunk;
5365
5366         /*
5367           Read a new chunk.
5368         */
5369         type[0]='\0';
5370         (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5371         length=ReadBlobMSBLong(image);
5372         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5373
5374         if (logging != MagickFalse)
5375           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5376            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
5377            type[0],type[1],type[2],type[3],(double) length);
5378
5379         if (length > PNG_UINT_31_MAX)
5380           {
5381             status=MagickFalse;
5382             break;
5383           }
5384
5385         if (count == 0)
5386           ThrowReaderException(CorruptImageError,"CorruptImage");
5387
5388         p=NULL;
5389         chunk=(unsigned char *) NULL;
5390
5391         if (length != 0)
5392           {
5393             if (length > GetBlobSize(image))
5394               ThrowReaderException(CorruptImageError,
5395                 "InsufficientImageDataInFile");
5396             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5397
5398             if (chunk == (unsigned char *) NULL)
5399               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5400
5401             for (i=0; i < (ssize_t) length; i++)
5402             {
5403               int
5404                 c;
5405
5406               c=ReadBlobByte(image);
5407               if (c == EOF)
5408                 break;
5409               chunk[i]=(unsigned char) c;
5410             }
5411
5412             p=chunk;
5413           }
5414
5415         (void) ReadBlobMSBLong(image);  /* read crc word */
5416
5417 #if !defined(JNG_SUPPORTED)
5418         if (memcmp(type,mng_JHDR,4) == 0)
5419           {
5420             skip_to_iend=MagickTrue;
5421
5422             if (mng_info->jhdr_warning == 0)
5423               (void) ThrowMagickException(exception,GetMagickModule(),
5424                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5425
5426             mng_info->jhdr_warning++;
5427           }
5428 #endif
5429         if (memcmp(type,mng_DHDR,4) == 0)
5430           {
5431             skip_to_iend=MagickTrue;
5432
5433             if (mng_info->dhdr_warning == 0)
5434               (void) ThrowMagickException(exception,GetMagickModule(),
5435                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5436
5437             mng_info->dhdr_warning++;
5438           }
5439         if (memcmp(type,mng_MEND,4) == 0)
5440           {
5441             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5442             break;
5443           }
5444
5445         if (skip_to_iend)
5446           {
5447             if (memcmp(type,mng_IEND,4) == 0)
5448               skip_to_iend=MagickFalse;
5449
5450             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5451
5452             if (logging != MagickFalse)
5453               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5454                 "  Skip to IEND.");
5455
5456             continue;
5457           }
5458
5459         if (memcmp(type,mng_MHDR,4) == 0)
5460           {
5461             if (length != 28)
5462               {
5463                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5464                 ThrowReaderException(CorruptImageError,"CorruptImage");
5465               }
5466
5467             mng_info->mng_width=(unsigned long)mng_get_long(p);
5468             mng_info->mng_height=(unsigned long)mng_get_long(&p[4]);
5469
5470             if (logging != MagickFalse)
5471               {
5472                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5473                   "  MNG width: %.20g",(double) mng_info->mng_width);
5474                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5475                   "  MNG height: %.20g",(double) mng_info->mng_height);
5476               }
5477
5478             p+=8;
5479             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5480
5481             if (mng_info->ticks_per_second == 0)
5482               default_frame_delay=0;
5483
5484             else
5485               default_frame_delay=1UL*image->ticks_per_second/
5486                 mng_info->ticks_per_second;
5487
5488             frame_delay=default_frame_delay;
5489             simplicity=0;
5490
5491             p+=16;
5492             simplicity=(size_t) mng_get_long(p);
5493
5494             mng_type=1;    /* Full MNG */
5495
5496             if ((simplicity != 0) && ((simplicity | 11) == 11))
5497               mng_type=2; /* LC */
5498
5499             if ((simplicity != 0) && ((simplicity | 9) == 9))
5500               mng_type=3; /* VLC */
5501
5502 #if defined(MNG_INSERT_LAYERS)
5503             if (mng_type != 3)
5504               insert_layers=MagickTrue;
5505 #endif
5506             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5507               {
5508                 /* Allocate next image structure.  */
5509                 AcquireNextImage(image_info,image,exception);
5510
5511                 if (GetNextImageInList(image) == (Image *) NULL)
5512                   return((Image *) NULL);
5513
5514                 image=SyncNextImageInList(image);
5515                 mng_info->image=image;
5516               }
5517
5518             if ((mng_info->mng_width > 65535L) ||
5519                 (mng_info->mng_height > 65535L))
5520               {
5521                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5522                 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5523               }
5524
5525             (void) FormatLocaleString(page_geometry,MagickPathExtent,
5526               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5527               mng_info->mng_height);
5528
5529             mng_info->frame.left=0;
5530             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5531             mng_info->frame.top=0;
5532             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5533             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5534
5535             for (i=0; i < MNG_MAX_OBJECTS; i++)
5536               mng_info->object_clip[i]=mng_info->frame;
5537
5538             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5539             continue;
5540           }
5541
5542         if (memcmp(type,mng_TERM,4) == 0)
5543           {
5544             int
5545               repeat=0;
5546
5547             if (length != 0)
5548               repeat=p[0];
5549
5550             if (repeat == 3 && length > 8)
5551               {
5552                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5553                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5554
5555                 if (mng_iterations == PNG_UINT_31_MAX)
5556                   mng_iterations=0;
5557
5558                 image->iterations=mng_iterations;
5559                 term_chunk_found=MagickTrue;
5560               }
5561
5562             if (logging != MagickFalse)
5563               {
5564                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5565                   "    repeat=%d,  final_delay=%.20g,  iterations=%.20g",
5566                   repeat,(double) final_delay, (double) image->iterations);
5567               }
5568
5569             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5570             continue;
5571           }
5572         if (memcmp(type,mng_DEFI,4) == 0)
5573           {
5574             if (mng_type == 3)
5575               (void) ThrowMagickException(exception,GetMagickModule(),
5576                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5577                 image->filename);
5578
5579             if (length < 2)
5580               {
5581                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5582                 ThrowReaderException(CorruptImageError,"CorruptImage");
5583               }
5584
5585             object_id=(p[0] << 8) | p[1];
5586
5587             if (mng_type == 2 && object_id != 0)
5588               (void) ThrowMagickException(exception,GetMagickModule(),
5589                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5590                 image->filename);
5591
5592             if (object_id > MNG_MAX_OBJECTS)
5593               {
5594                 /*
5595                   Instead of using a warning we should allocate a larger
5596                   MngInfo structure and continue.
5597                 */
5598                 (void) ThrowMagickException(exception,GetMagickModule(),
5599                   CoderError,"object id too large","`%s'",image->filename);
5600                 object_id=MNG_MAX_OBJECTS;
5601               }
5602
5603             if (mng_info->exists[object_id])
5604               if (mng_info->frozen[object_id])
5605                 {
5606                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5607                   (void) ThrowMagickException(exception,
5608                     GetMagickModule(),CoderError,
5609                     "DEFI cannot redefine a frozen MNG object","`%s'",
5610                     image->filename);
5611                   continue;
5612                 }
5613
5614             mng_info->exists[object_id]=MagickTrue;
5615
5616             if (length > 2)
5617               mng_info->invisible[object_id]=p[2];
5618
5619             /*
5620               Extract object offset info.
5621             */
5622             if (length > 11)
5623               {
5624                 mng_info->x_off[object_id]=(ssize_t) mng_get_long(&p[4]);
5625                 mng_info->y_off[object_id]=(ssize_t) mng_get_long(&p[8]);
5626                 if (logging != MagickFalse)
5627                   {
5628                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5629                       "  x_off[%d]: %.20g,  y_off[%d]: %.20g",
5630                       object_id,(double) mng_info->x_off[object_id],
5631                       object_id,(double) mng_info->y_off[object_id]);
5632                   }
5633               }
5634
5635             /*
5636               Extract object clipping info.
5637             */
5638             if (length > 27)
5639               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5640                 &p[12]);
5641
5642             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5643             continue;
5644           }
5645         if (memcmp(type,mng_bKGD,4) == 0)
5646           {
5647             mng_info->have_global_bkgd=MagickFalse;
5648
5649             if (length > 5)
5650               {
5651                 mng_info->mng_global_bkgd.red=
5652                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5653
5654                 mng_info->mng_global_bkgd.green=
5655                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5656
5657                 mng_info->mng_global_bkgd.blue=
5658                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5659
5660                 mng_info->have_global_bkgd=MagickTrue;
5661               }
5662
5663             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5664             continue;
5665           }
5666         if (memcmp(type,mng_BACK,4) == 0)
5667           {
5668 #if defined(MNG_INSERT_LAYERS)
5669             if (length > 6)
5670               mandatory_back=p[6];
5671
5672             else
5673               mandatory_back=0;
5674
5675             if (mandatory_back && length > 5)
5676               {
5677                 mng_background_color.red=
5678                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5679
5680                 mng_background_color.green=
5681                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5682
5683                 mng_background_color.blue=
5684                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5685
5686                 mng_background_color.alpha=OpaqueAlpha;
5687               }
5688
5689 #ifdef MNG_OBJECT_BUFFERS
5690             if (length > 8)
5691               mng_background_object=(p[7] << 8) | p[8];
5692 #endif
5693 #endif
5694             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5695             continue;
5696           }
5697
5698         if (memcmp(type,mng_PLTE,4) == 0)
5699           {
5700             /* Read global PLTE.  */
5701
5702             if (length && (length < 769))
5703               {
5704                 if (mng_info->global_plte == (png_colorp) NULL)
5705                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5706                     sizeof(*mng_info->global_plte));
5707
5708                 for (i=0; i < (ssize_t) (length/3); i++)
5709                 {
5710                   mng_info->global_plte[i].red=p[3*i];
5711                   mng_info->global_plte[i].green=p[3*i+1];
5712                   mng_info->global_plte[i].blue=p[3*i+2];
5713                 }
5714
5715                 mng_info->global_plte_length=(unsigned int) (length/3);
5716               }
5717 #ifdef MNG_LOOSE
5718             for ( ; i < 256; i++)
5719             {
5720               mng_info->global_plte[i].red=i;
5721               mng_info->global_plte[i].green=i;
5722               mng_info->global_plte[i].blue=i;
5723             }
5724
5725             if (length != 0)
5726               mng_info->global_plte_length=256;
5727 #endif
5728             else
5729               mng_info->global_plte_length=0;
5730
5731             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5732             continue;
5733           }
5734
5735         if (memcmp(type,mng_tRNS,4) == 0)
5736           {
5737             /* read global tRNS */
5738
5739             if (length > 0 && length < 257)
5740               for (i=0; i < (ssize_t) length; i++)
5741                 mng_info->global_trns[i]=p[i];
5742
5743 #ifdef MNG_LOOSE
5744             for ( ; i < 256; i++)
5745               mng_info->global_trns[i]=255;
5746 #endif
5747             mng_info->global_trns_length=(unsigned int) length;
5748             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5749             continue;
5750           }
5751         if (memcmp(type,mng_gAMA,4) == 0)
5752           {
5753             if (length == 4)
5754               {
5755                 ssize_t
5756                   igamma;
5757
5758                 igamma=mng_get_long(p);
5759                 mng_info->global_gamma=((float) igamma)*0.00001;
5760                 mng_info->have_global_gama=MagickTrue;
5761               }
5762
5763             else
5764               mng_info->have_global_gama=MagickFalse;
5765
5766             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5767             continue;
5768           }
5769
5770         if (memcmp(type,mng_cHRM,4) == 0)
5771           {
5772             /* Read global cHRM */
5773
5774             if (length == 32)
5775               {
5776                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5777                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5778                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5779                 mng_info->global_chrm.red_primary.y=0.00001*
5780                   mng_get_long(&p[12]);
5781                 mng_info->global_chrm.green_primary.x=0.00001*
5782                   mng_get_long(&p[16]);
5783                 mng_info->global_chrm.green_primary.y=0.00001*
5784                   mng_get_long(&p[20]);
5785                 mng_info->global_chrm.blue_primary.x=0.00001*
5786                   mng_get_long(&p[24]);
5787                 mng_info->global_chrm.blue_primary.y=0.00001*
5788                   mng_get_long(&p[28]);
5789                 mng_info->have_global_chrm=MagickTrue;
5790               }
5791             else
5792               mng_info->have_global_chrm=MagickFalse;
5793
5794             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5795             continue;
5796           }
5797
5798         if (memcmp(type,mng_sRGB,4) == 0)
5799           {
5800             /*
5801               Read global sRGB.
5802             */
5803             if (length != 0)
5804               {
5805                 mng_info->global_srgb_intent=
5806                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5807                 mng_info->have_global_srgb=MagickTrue;
5808               }
5809             else
5810               mng_info->have_global_srgb=MagickFalse;
5811
5812             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5813             continue;
5814           }
5815
5816         if (memcmp(type,mng_iCCP,4) == 0)
5817           {
5818             /* To do: */
5819
5820             /*
5821               Read global iCCP.
5822             */
5823             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5824
5825             continue;
5826           }
5827
5828         if (memcmp(type,mng_FRAM,4) == 0)
5829           {
5830             if (mng_type == 3)
5831               (void) ThrowMagickException(exception,GetMagickModule(),
5832                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5833                 image->filename);
5834
5835             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5836               image->delay=frame_delay;
5837
5838             frame_delay=default_frame_delay;
5839             frame_timeout=default_frame_timeout;
5840             fb=default_fb;
5841
5842             if (length != 0)
5843               if (p[0])
5844                 mng_info->framing_mode=p[0];
5845
5846             if (logging != MagickFalse)
5847               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5848                 "    Framing_mode=%d",mng_info->framing_mode);
5849
5850             if (length > 6)
5851               {
5852                 /* Note the delay and frame clipping boundaries.  */
5853
5854                 p++; /* framing mode */
5855
5856                 while (*p && ((p-chunk) < (ssize_t) length))
5857                   p++;  /* frame name */
5858
5859                 p++;  /* frame name terminator */
5860
5861                 if ((p-chunk) < (ssize_t) (length-4))
5862                   {
5863                     int
5864                       change_delay,
5865                       change_timeout,
5866                       change_clipping;
5867
5868                     change_delay=(*p++);
5869                     change_timeout=(*p++);
5870                     change_clipping=(*p++);
5871                     p++; /* change_sync */
5872
5873                     if (change_delay && ((p-chunk) < (ssize_t) (length-4)))
5874                       {
5875                         frame_delay=1UL*image->ticks_per_second*
5876                           mng_get_long(p);
5877
5878                         if (mng_info->ticks_per_second != 0)
5879                           frame_delay/=mng_info->ticks_per_second;
5880
5881                         else
5882                           frame_delay=PNG_UINT_31_MAX;
5883
5884                         if (change_delay == 2)
5885                           default_frame_delay=frame_delay;
5886
5887                         p+=4;
5888
5889                         if (logging != MagickFalse)
5890                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5891                             "    Framing_delay=%.20g",(double) frame_delay);
5892                       }
5893
5894                     if (change_timeout && ((p-chunk) < (ssize_t) (length-4)))
5895                       {
5896                         frame_timeout=1UL*image->ticks_per_second*
5897                           mng_get_long(p);
5898
5899                         if (mng_info->ticks_per_second != 0)
5900                           frame_timeout/=mng_info->ticks_per_second;
5901
5902                         else
5903                           frame_timeout=PNG_UINT_31_MAX;
5904
5905                         if (change_timeout == 2)
5906                           default_frame_timeout=frame_timeout;
5907
5908                         p+=4;
5909
5910                         if (logging != MagickFalse)
5911                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5912                             "    Framing_timeout=%.20g",(double) frame_timeout);
5913                       }
5914
5915                     if (change_clipping && ((p-chunk) < (ssize_t) (length-16)))
5916                       {
5917                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5918                         p+=16;
5919                         previous_fb=fb;
5920
5921                         if (logging != MagickFalse)
5922                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5923                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5924                             (double) fb.left,(double) fb.right,(double) fb.top,
5925                             (double) fb.bottom);
5926
5927                         if (change_clipping == 2)
5928                           default_fb=fb;
5929                       }
5930                   }
5931               }
5932             mng_info->clip=fb;
5933             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5934
5935             subframe_width=(size_t) (mng_info->clip.right
5936                -mng_info->clip.left);
5937
5938             subframe_height=(size_t) (mng_info->clip.bottom
5939                -mng_info->clip.top);
5940             /*
5941               Insert a background layer behind the frame if framing_mode is 4.
5942             */
5943 #if defined(MNG_INSERT_LAYERS)
5944             if (logging != MagickFalse)
5945               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5946                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
5947                 subframe_width,(double) subframe_height);
5948
5949             if (insert_layers && (mng_info->framing_mode == 4) &&
5950                 (subframe_width) && (subframe_height))
5951               {
5952                 /* Allocate next image structure.  */
5953                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5954                   {
5955                     AcquireNextImage(image_info,image,exception);
5956
5957                     if (GetNextImageInList(image) == (Image *) NULL)
5958                       return(DestroyImageList(image));
5959
5960                     image=SyncNextImageInList(image);
5961                   }
5962
5963                 mng_info->image=image;
5964
5965                 if (term_chunk_found)
5966                   {
5967                     image->start_loop=MagickTrue;
5968                     image->iterations=mng_iterations;
5969                     term_chunk_found=MagickFalse;
5970                   }
5971
5972                 else
5973                     image->start_loop=MagickFalse;
5974
5975                 image->columns=subframe_width;
5976                 image->rows=subframe_height;
5977                 image->page.width=subframe_width;
5978                 image->page.height=subframe_height;
5979                 image->page.x=mng_info->clip.left;
5980                 image->page.y=mng_info->clip.top;
5981                 image->background_color=mng_background_color;
5982                 image->alpha_trait=UndefinedPixelTrait;
5983                 image->delay=0;
5984                 (void) SetImageBackgroundColor(image,exception);
5985
5986                 if (logging != MagickFalse)
5987                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5988                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5989                     (double) mng_info->clip.left,
5990                     (double) mng_info->clip.right,
5991                     (double) mng_info->clip.top,
5992                     (double) mng_info->clip.bottom);
5993               }
5994 #endif
5995             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5996             continue;
5997           }
5998
5999         if (memcmp(type,mng_CLIP,4) == 0)
6000           {
6001             unsigned int
6002               first_object,
6003               last_object;
6004
6005             /*
6006               Read CLIP.
6007             */
6008             if (length > 3)
6009               {
6010                 first_object=(p[0] << 8) | p[1];
6011                 last_object=(p[2] << 8) | p[3];
6012                 p+=4;
6013
6014                 for (i=(int) first_object; i <= (int) last_object; i++)
6015                 {
6016                   if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6017                     continue;
6018
6019                   if (mng_info->exists[i] && !mng_info->frozen[i])
6020                     {
6021                       MngBox
6022                         box;
6023
6024                       box=mng_info->object_clip[i];
6025                       if ((p-chunk) < (ssize_t) (length-17))
6026                         mng_info->object_clip[i]=
6027                            mng_read_box(box,(char) p[0],&p[1]);
6028                     }
6029                 }
6030
6031               }
6032             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6033             continue;
6034           }
6035
6036         if (memcmp(type,mng_SAVE,4) == 0)
6037           {
6038             for (i=1; i < MNG_MAX_OBJECTS; i++)
6039               if (mng_info->exists[i])
6040                 {
6041                  mng_info->frozen[i]=MagickTrue;
6042 #ifdef MNG_OBJECT_BUFFERS
6043                  if (mng_info->ob[i] != (MngBuffer *) NULL)
6044                     mng_info->ob[i]->frozen=MagickTrue;
6045 #endif
6046                 }
6047
6048             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6049
6050             continue;
6051           }
6052
6053         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
6054           {
6055             /* Read DISC or SEEK.  */
6056
6057             if ((length == 0) || !memcmp(type,mng_SEEK,4))
6058               {
6059                 for (i=1; i < MNG_MAX_OBJECTS; i++)
6060                   MngInfoDiscardObject(mng_info,i);
6061               }
6062
6063             else
6064               {
6065                 register ssize_t
6066                   j;
6067
6068                 for (j=1; j < (ssize_t) length; j+=2)
6069                 {
6070                   i=p[j-1] << 8 | p[j];
6071                   MngInfoDiscardObject(mng_info,i);
6072                 }
6073               }
6074
6075             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6076
6077             continue;
6078           }
6079
6080         if (memcmp(type,mng_MOVE,4) == 0)
6081           {
6082             size_t
6083               first_object,
6084               last_object;
6085
6086             /* read MOVE */
6087
6088             if (length > 3)
6089             {
6090               first_object=(p[0] << 8) | p[1];
6091               last_object=(p[2] << 8) | p[3];
6092               p+=4;
6093
6094               for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
6095               {
6096                 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6097                   continue;
6098
6099                 if (mng_info->exists[i] && !mng_info->frozen[i] &&
6100                     (p-chunk) < (ssize_t) (length-8))
6101                   {
6102                     MngPair
6103                       new_pair;
6104
6105                     MngPair
6106                       old_pair;
6107
6108                     old_pair.a=mng_info->x_off[i];
6109                     old_pair.b=mng_info->y_off[i];
6110                     new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
6111                     mng_info->x_off[i]=new_pair.a;
6112                     mng_info->y_off[i]=new_pair.b;
6113                   }
6114               }
6115             }
6116
6117             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6118             continue;
6119           }
6120
6121         if (memcmp(type,mng_LOOP,4) == 0)
6122           {
6123             ssize_t loop_iters=1;
6124             if (length > 4)
6125               {
6126                 loop_level=chunk[0];
6127                 mng_info->loop_active[loop_level]=1;  /* mark loop active */
6128
6129                 /* Record starting point.  */
6130                 loop_iters=mng_get_long(&chunk[1]);
6131
6132                 if (logging != MagickFalse)
6133                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6134                     "  LOOP level %.20g has %.20g iterations ",
6135                     (double) loop_level, (double) loop_iters);
6136
6137                 if (loop_iters == 0)
6138                   skipping_loop=loop_level;
6139
6140                 else
6141                   {
6142                     mng_info->loop_jump[loop_level]=TellBlob(image);
6143                     mng_info->loop_count[loop_level]=loop_iters;
6144                   }
6145
6146                 mng_info->loop_iteration[loop_level]=0;
6147               }
6148             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6149             continue;
6150           }
6151
6152         if (memcmp(type,mng_ENDL,4) == 0)
6153           {
6154             if (length > 0)
6155               {
6156                 loop_level=chunk[0];
6157
6158                 if (skipping_loop > 0)
6159                   {
6160                     if (skipping_loop == loop_level)
6161                       {
6162                         /*
6163                           Found end of zero-iteration loop.
6164                         */
6165                         skipping_loop=(-1);
6166                         mng_info->loop_active[loop_level]=0;
6167                       }
6168                   }
6169
6170                 else
6171                   {
6172                     if (mng_info->loop_active[loop_level] == 1)
6173                       {
6174                         mng_info->loop_count[loop_level]--;
6175                         mng_info->loop_iteration[loop_level]++;
6176
6177                         if (logging != MagickFalse)
6178                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6179                           "  ENDL: LOOP level %.20g has %.20g remaining iters",
6180                             (double) loop_level,(double)
6181                             mng_info->loop_count[loop_level]);
6182
6183                         if (mng_info->loop_count[loop_level] != 0)
6184                           {
6185                             offset=
6186                               SeekBlob(image,mng_info->loop_jump[loop_level],
6187                               SEEK_SET);
6188
6189                             if (offset < 0)
6190                               {
6191                                 chunk=(unsigned char *) RelinquishMagickMemory(
6192                                   chunk);
6193                                 ThrowReaderException(CorruptImageError,
6194                                   "ImproperImageHeader");
6195                               }
6196                           }
6197
6198                         else
6199                           {
6200                             short
6201                               last_level;
6202
6203                             /*
6204                               Finished loop.
6205                             */
6206                             mng_info->loop_active[loop_level]=0;
6207                             last_level=(-1);
6208                             for (i=0; i < loop_level; i++)
6209                               if (mng_info->loop_active[i] == 1)
6210                                 last_level=(short) i;
6211                             loop_level=last_level;
6212                           }
6213                       }
6214                   }
6215               }
6216
6217             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6218             continue;
6219           }
6220
6221         if (memcmp(type,mng_CLON,4) == 0)
6222           {
6223             if (mng_info->clon_warning == 0)
6224               (void) ThrowMagickException(exception,GetMagickModule(),
6225                 CoderError,"CLON is not implemented yet","`%s'",
6226                 image->filename);
6227
6228             mng_info->clon_warning++;
6229           }
6230
6231         if (memcmp(type,mng_MAGN,4) == 0)
6232           {
6233             png_uint_16
6234               magn_first,
6235               magn_last,
6236               magn_mb,
6237               magn_ml,
6238               magn_mr,
6239               magn_mt,
6240               magn_mx,
6241               magn_my,
6242               magn_methx,
6243               magn_methy;
6244
6245             if (length > 1)
6246               magn_first=(p[0] << 8) | p[1];
6247
6248             else
6249               magn_first=0;
6250
6251             if (length > 3)
6252               magn_last=(p[2] << 8) | p[3];
6253
6254             else
6255               magn_last=magn_first;
6256 #ifndef MNG_OBJECT_BUFFERS
6257             if (magn_first || magn_last)
6258               if (mng_info->magn_warning == 0)
6259                 {
6260                   (void) ThrowMagickException(exception,
6261                      GetMagickModule(),CoderError,
6262                      "MAGN is not implemented yet for nonzero objects",
6263                      "`%s'",image->filename);
6264
6265                    mng_info->magn_warning++;
6266                 }
6267 #endif
6268             if (length > 4)
6269               magn_methx=p[4];
6270
6271             else
6272               magn_methx=0;
6273
6274             if (length > 6)
6275               magn_mx=(p[5] << 8) | p[6];
6276
6277             else
6278               magn_mx=1;
6279
6280             if (magn_mx == 0)
6281               magn_mx=1;
6282
6283             if (length > 8)
6284               magn_my=(p[7] << 8) | p[8];
6285
6286             else
6287               magn_my=magn_mx;
6288
6289             if (magn_my == 0)
6290               magn_my=1;
6291
6292             if (length > 10)
6293               magn_ml=(p[9] << 8) | p[10];
6294
6295             else
6296               magn_ml=magn_mx;
6297
6298             if (magn_ml == 0)
6299               magn_ml=1;
6300
6301             if (length > 12)
6302               magn_mr=(p[11] << 8) | p[12];
6303
6304             else
6305               magn_mr=magn_mx;
6306
6307             if (magn_mr == 0)
6308               magn_mr=1;
6309
6310             if (length > 14)
6311               magn_mt=(p[13] << 8) | p[14];
6312
6313             else
6314               magn_mt=magn_my;
6315
6316             if (magn_mt == 0)
6317               magn_mt=1;
6318
6319             if (length > 16)
6320               magn_mb=(p[15] << 8) | p[16];
6321
6322             else
6323               magn_mb=magn_my;
6324
6325             if (magn_mb == 0)
6326               magn_mb=1;
6327
6328             if (length > 17)
6329               magn_methy=p[17];
6330
6331             else
6332               magn_methy=magn_methx;
6333
6334
6335             if (magn_methx > 5 || magn_methy > 5)
6336               if (mng_info->magn_warning == 0)
6337                 {
6338                   (void) ThrowMagickException(exception,
6339                      GetMagickModule(),CoderError,
6340                      "Unknown MAGN method in MNG datastream","`%s'",
6341                      image->filename);
6342
6343                    mng_info->magn_warning++;
6344                 }
6345 #ifdef MNG_OBJECT_BUFFERS
6346           /* Magnify existing objects in the range magn_first to magn_last */
6347 #endif
6348             if (magn_first == 0 || magn_last == 0)
6349               {
6350                 /* Save the magnification factors for object 0 */
6351                 mng_info->magn_mb=magn_mb;
6352                 mng_info->magn_ml=magn_ml;
6353                 mng_info->magn_mr=magn_mr;
6354                 mng_info->magn_mt=magn_mt;
6355                 mng_info->magn_mx=magn_mx;
6356                 mng_info->magn_my=magn_my;
6357                 mng_info->magn_methx=magn_methx;
6358                 mng_info->magn_methy=magn_methy;
6359               }
6360           }
6361
6362         if (memcmp(type,mng_PAST,4) == 0)
6363           {
6364             if (mng_info->past_warning == 0)
6365               (void) ThrowMagickException(exception,GetMagickModule(),
6366                 CoderError,"PAST is not implemented yet","`%s'",
6367                 image->filename);
6368
6369             mng_info->past_warning++;
6370           }
6371
6372         if (memcmp(type,mng_SHOW,4) == 0)
6373           {
6374             if (mng_info->show_warning == 0)
6375               (void) ThrowMagickException(exception,GetMagickModule(),
6376                 CoderError,"SHOW is not implemented yet","`%s'",
6377                 image->filename);
6378
6379             mng_info->show_warning++;
6380           }
6381
6382         if (memcmp(type,mng_sBIT,4) == 0)
6383           {
6384             if (length < 4)
6385               mng_info->have_global_sbit=MagickFalse;
6386
6387             else
6388               {
6389                 mng_info->global_sbit.gray=p[0];
6390                 mng_info->global_sbit.red=p[0];
6391                 mng_info->global_sbit.green=p[1];
6392                 mng_info->global_sbit.blue=p[2];
6393                 mng_info->global_sbit.alpha=p[3];
6394                 mng_info->have_global_sbit=MagickTrue;
6395              }
6396           }
6397         if (memcmp(type,mng_pHYs,4) == 0)
6398           {
6399             if (length > 8)
6400               {
6401                 mng_info->global_x_pixels_per_unit=
6402                     (size_t) mng_get_long(p);
6403                 mng_info->global_y_pixels_per_unit=
6404                     (size_t) mng_get_long(&p[4]);
6405                 mng_info->global_phys_unit_type=p[8];
6406                 mng_info->have_global_phys=MagickTrue;
6407               }
6408
6409             else
6410               mng_info->have_global_phys=MagickFalse;
6411           }
6412         if (memcmp(type,mng_pHYg,4) == 0)
6413           {
6414             if (mng_info->phyg_warning == 0)
6415               (void) ThrowMagickException(exception,GetMagickModule(),
6416                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6417
6418             mng_info->phyg_warning++;
6419           }
6420         if (memcmp(type,mng_BASI,4) == 0)
6421           {
6422             skip_to_iend=MagickTrue;
6423
6424             if (mng_info->basi_warning == 0)
6425               (void) ThrowMagickException(exception,GetMagickModule(),
6426                 CoderError,"BASI is not implemented yet","`%s'",
6427                 image->filename);
6428
6429             mng_info->basi_warning++;
6430 #ifdef MNG_BASI_SUPPORTED
6431             basi_width=(unsigned long) mng_get_long(p);
6432             basi_width=(unsigned long) mng_get_long(&p[4]);
6433             basi_color_type=p[8];
6434             basi_compression_method=p[9];
6435             basi_filter_type=p[10];
6436             basi_interlace_method=p[11];
6437             if (length > 11)
6438               basi_red=((png_uint_32) p[12] << 8) & (png_uint_32) p[13];
6439
6440             else
6441               basi_red=0;
6442
6443             if (length > 13)
6444               basi_green=((png_uint_32) p[14] << 8) & (png_uint_32) p[15];
6445
6446             else
6447               basi_green=0;
6448
6449             if (length > 15)
6450               basi_blue=((png_uint_32) p[16] << 8) & (png_uint_32) p[17];
6451
6452             else
6453               basi_blue=0;
6454
6455             if (length > 17)
6456               basi_alpha=((png_uint_32) p[18] << 8) & (png_uint_32) p[19];
6457
6458             else
6459               {
6460                 if (basi_sample_depth == 16)
6461                   basi_alpha=65535L;
6462                 else
6463                   basi_alpha=255;
6464               }
6465
6466             if (length > 19)
6467               basi_viewable=p[20];
6468
6469             else
6470               basi_viewable=0;
6471
6472 #endif
6473             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6474             continue;
6475           }
6476
6477         if (memcmp(type,mng_IHDR,4)
6478 #if defined(JNG_SUPPORTED)
6479             && memcmp(type,mng_JHDR,4)
6480 #endif
6481             )
6482           {
6483             /* Not an IHDR or JHDR chunk */
6484             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6485
6486             continue;
6487           }
6488 /* Process IHDR */
6489         if (logging != MagickFalse)
6490           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6491             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6492
6493         mng_info->exists[object_id]=MagickTrue;
6494         mng_info->viewable[object_id]=MagickTrue;
6495
6496         if (mng_info->invisible[object_id])
6497           {
6498             if (logging != MagickFalse)
6499               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6500                 "  Skipping invisible object");
6501
6502             skip_to_iend=MagickTrue;
6503             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6504             continue;
6505           }
6506 #if defined(MNG_INSERT_LAYERS)
6507         if (length < 8)
6508           {
6509             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6510             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6511           }
6512
6513         image_width=(size_t) mng_get_long(p);
6514         image_height=(size_t) mng_get_long(&p[4]);
6515 #endif
6516         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6517
6518         /*
6519           Insert a transparent background layer behind the entire animation
6520           if it is not full screen.
6521         */
6522 #if defined(MNG_INSERT_LAYERS)
6523         if (insert_layers && mng_type && first_mng_object)
6524           {
6525             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6526                 (image_width < mng_info->mng_width) ||
6527                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6528                 (image_height < mng_info->mng_height) ||
6529                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6530               {
6531                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6532                   {
6533                     /*
6534                       Allocate next image structure.
6535                     */
6536                     AcquireNextImage(image_info,image,exception);
6537
6538                     if (GetNextImageInList(image) == (Image *) NULL)
6539                       return(DestroyImageList(image));
6540
6541                     image=SyncNextImageInList(image);
6542                   }
6543                 mng_info->image=image;
6544
6545                 if (term_chunk_found)
6546                   {
6547                     image->start_loop=MagickTrue;
6548                     image->iterations=mng_iterations;
6549                     term_chunk_found=MagickFalse;
6550                   }
6551
6552                 else
6553                     image->start_loop=MagickFalse;
6554
6555                 /* Make a background rectangle.  */
6556
6557                 image->delay=0;
6558                 image->columns=mng_info->mng_width;
6559                 image->rows=mng_info->mng_height;
6560                 image->page.width=mng_info->mng_width;
6561                 image->page.height=mng_info->mng_height;
6562                 image->page.x=0;
6563                 image->page.y=0;
6564                 image->background_color=mng_background_color;
6565                 (void) SetImageBackgroundColor(image,exception);
6566                 if (logging != MagickFalse)
6567                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6568                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6569                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6570               }
6571           }
6572         /*
6573           Insert a background layer behind the upcoming image if
6574           framing_mode is 3, and we haven't already inserted one.
6575         */
6576         if (insert_layers && (mng_info->framing_mode == 3) &&
6577                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6578                 (simplicity & 0x08)))
6579           {
6580             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6581             {
6582               /*
6583                 Allocate next image structure.
6584               */
6585               AcquireNextImage(image_info,image,exception);
6586
6587               if (GetNextImageInList(image) == (Image *) NULL)
6588                 return(DestroyImageList(image));
6589
6590               image=SyncNextImageInList(image);
6591             }
6592
6593             mng_info->image=image;
6594
6595             if (term_chunk_found)
6596               {
6597                 image->start_loop=MagickTrue;
6598                 image->iterations=mng_iterations;
6599                 term_chunk_found=MagickFalse;
6600               }
6601
6602             else
6603                 image->start_loop=MagickFalse;
6604
6605             image->delay=0;
6606             image->columns=subframe_width;
6607             image->rows=subframe_height;
6608             image->page.width=subframe_width;
6609             image->page.height=subframe_height;
6610             image->page.x=mng_info->clip.left;
6611             image->page.y=mng_info->clip.top;
6612             image->background_color=mng_background_color;
6613             image->alpha_trait=UndefinedPixelTrait;
6614             (void) SetImageBackgroundColor(image,exception);
6615
6616             if (logging != MagickFalse)
6617               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6618                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6619                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6620                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6621           }
6622 #endif /* MNG_INSERT_LAYERS */
6623         first_mng_object=MagickFalse;
6624
6625         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6626           {
6627             /*
6628               Allocate next image structure.
6629             */
6630             AcquireNextImage(image_info,image,exception);
6631
6632             if (GetNextImageInList(image) == (Image *) NULL)
6633               return(DestroyImageList(image));
6634
6635             image=SyncNextImageInList(image);
6636           }
6637         mng_info->image=image;
6638         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6639           GetBlobSize(image));
6640
6641         if (status == MagickFalse)
6642           break;
6643
6644         if (term_chunk_found)
6645           {
6646             image->start_loop=MagickTrue;
6647             term_chunk_found=MagickFalse;
6648           }
6649
6650         else
6651             image->start_loop=MagickFalse;
6652
6653         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6654           {
6655             image->delay=frame_delay;
6656             frame_delay=default_frame_delay;
6657           }
6658
6659         else
6660           image->delay=0;
6661
6662         image->page.width=mng_info->mng_width;
6663         image->page.height=mng_info->mng_height;
6664         image->page.x=mng_info->x_off[object_id];
6665         image->page.y=mng_info->y_off[object_id];
6666         image->iterations=mng_iterations;
6667
6668         /*
6669           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6670         */
6671
6672         if (logging != MagickFalse)
6673           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6674             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6675             type[2],type[3]);
6676
6677         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6678
6679         if (offset < 0)
6680           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6681       }
6682
6683     mng_info->image=image;
6684     mng_info->mng_type=mng_type;
6685     mng_info->object_id=object_id;
6686
6687     if (memcmp(type,mng_IHDR,4) == 0)
6688       image=ReadOnePNGImage(mng_info,image_info,exception);
6689
6690 #if defined(JNG_SUPPORTED)
6691     else
6692       image=ReadOneJNGImage(mng_info,image_info,exception);
6693 #endif
6694
6695     if (image == (Image *) NULL)
6696       {
6697         if (logging != MagickFalse)
6698           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6699             "exit ReadJNGImage() with error");
6700
6701         return((Image *) NULL);
6702       }
6703
6704     if (image->columns == 0 || image->rows == 0)
6705       {
6706         (void) CloseBlob(image);
6707         return(DestroyImageList(image));
6708       }
6709
6710     mng_info->image=image;
6711
6712     if (mng_type)
6713       {
6714         MngBox
6715           crop_box;
6716
6717         if (mng_info->magn_methx || mng_info->magn_methy)
6718           {
6719             png_uint_32
6720                magnified_height,
6721                magnified_width;
6722
6723             if (logging != MagickFalse)
6724               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6725                 "  Processing MNG MAGN chunk");
6726
6727             if (mng_info->magn_methx == 1)
6728               {
6729                 magnified_width=mng_info->magn_ml;
6730
6731                 if (image->columns > 1)
6732                    magnified_width += mng_info->magn_mr;
6733
6734                 if (image->columns > 2)
6735                    magnified_width += (png_uint_32)
6736                       ((image->columns-2)*(mng_info->magn_mx));
6737               }
6738
6739             else
6740               {
6741                 magnified_width=(png_uint_32) image->columns;
6742
6743                 if (image->columns > 1)
6744                    magnified_width += mng_info->magn_ml-1;
6745
6746                 if (image->columns > 2)
6747                    magnified_width += mng_info->magn_mr-1;
6748
6749                 if (image->columns > 3)
6750                    magnified_width += (png_uint_32)
6751                       ((image->columns-3)*(mng_info->magn_mx-1));
6752               }
6753
6754             if (mng_info->magn_methy == 1)
6755               {
6756                 magnified_height=mng_info->magn_mt;
6757
6758                 if (image->rows > 1)
6759                    magnified_height += mng_info->magn_mb;
6760
6761                 if (image->rows > 2)
6762                    magnified_height += (png_uint_32)
6763                       ((image->rows-2)*(mng_info->magn_my));
6764               }
6765
6766             else
6767               {
6768                 magnified_height=(png_uint_32) image->rows;
6769
6770                 if (image->rows > 1)
6771                    magnified_height += mng_info->magn_mt-1;
6772
6773                 if (image->rows > 2)
6774                    magnified_height += mng_info->magn_mb-1;
6775
6776                 if (image->rows > 3)
6777                    magnified_height += (png_uint_32)
6778                       ((image->rows-3)*(mng_info->magn_my-1));
6779               }
6780
6781             if (magnified_height > image->rows ||
6782                 magnified_width > image->columns)
6783               {
6784                 Image
6785                   *large_image;
6786
6787                 int
6788                   yy;
6789
6790                 Quantum
6791                   *next,
6792                   *prev;
6793
6794                 png_uint_16
6795                   magn_methx,
6796                   magn_methy;
6797
6798                 ssize_t
6799                   m,
6800                   y;
6801
6802                 register Quantum
6803                   *n,
6804                   *q;
6805
6806                 register ssize_t
6807                   x;
6808
6809                 /* Allocate next image structure.  */
6810
6811                 if (logging != MagickFalse)
6812                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6813                     "    Allocate magnified image");
6814
6815                 AcquireNextImage(image_info,image,exception);
6816
6817                 if (GetNextImageInList(image) == (Image *) NULL)
6818                   return(DestroyImageList(image));
6819
6820                 large_image=SyncNextImageInList(image);
6821
6822                 large_image->columns=magnified_width;
6823                 large_image->rows=magnified_height;
6824
6825                 magn_methx=mng_info->magn_methx;
6826                 magn_methy=mng_info->magn_methy;
6827
6828 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6829 #define QM unsigned short
6830                 if (magn_methx != 1 || magn_methy != 1)
6831                   {
6832                   /*
6833                      Scale pixels to unsigned shorts to prevent
6834                      overflow of intermediate values of interpolations
6835                   */
6836                      for (y=0; y < (ssize_t) image->rows; y++)
6837                      {
6838                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6839                           exception);
6840
6841                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6842                        {
6843                           SetPixelRed(image,ScaleQuantumToShort(
6844                             GetPixelRed(image,q)),q);
6845                           SetPixelGreen(image,ScaleQuantumToShort(
6846                             GetPixelGreen(image,q)),q);
6847                           SetPixelBlue(image,ScaleQuantumToShort(
6848                             GetPixelBlue(image,q)),q);
6849                           SetPixelAlpha(image,ScaleQuantumToShort(
6850                             GetPixelAlpha(image,q)),q);
6851                           q+=GetPixelChannels(image);
6852                        }
6853
6854                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6855                          break;
6856                      }
6857                   }
6858 #else
6859 #define QM Quantum
6860 #endif
6861
6862                 if (image->alpha_trait != UndefinedPixelTrait)
6863                    (void) SetImageBackgroundColor(large_image,exception);
6864
6865                 else
6866                   {
6867                     large_image->background_color.alpha=OpaqueAlpha;
6868                     (void) SetImageBackgroundColor(large_image,exception);
6869
6870                     if (magn_methx == 4)
6871                       magn_methx=2;
6872
6873                     if (magn_methx == 5)
6874                       magn_methx=3;
6875
6876                     if (magn_methy == 4)
6877                       magn_methy=2;
6878
6879                     if (magn_methy == 5)
6880                       magn_methy=3;
6881                   }
6882
6883                 /* magnify the rows into the right side of the large image */
6884
6885                 if (logging != MagickFalse)
6886                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6887                     "    Magnify the rows to %.20g",
6888                     (double) large_image->rows);
6889                 m=(ssize_t) mng_info->magn_mt;
6890                 yy=0;
6891                 length=(size_t) GetPixelChannels(image)*image->columns;
6892                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6893                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6894
6895                 if ((prev == (Quantum *) NULL) ||
6896                     (next == (Quantum *) NULL))
6897                   {
6898                      image=DestroyImageList(image);
6899                      ThrowReaderException(ResourceLimitError,
6900                        "MemoryAllocationFailed");
6901                   }
6902
6903                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6904                 (void) CopyMagickMemory(next,n,length);
6905
6906                 for (y=0; y < (ssize_t) image->rows; y++)
6907                 {
6908                   if (y == 0)
6909                     m=(ssize_t) mng_info->magn_mt;
6910
6911                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6912                     m=(ssize_t) mng_info->magn_mb;
6913
6914                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6915                     m=(ssize_t) mng_info->magn_mb;
6916
6917                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6918                     m=1;
6919
6920                   else
6921                     m=(ssize_t) mng_info->magn_my;
6922
6923                   n=prev;
6924                   prev=next;
6925                   next=n;
6926
6927                   if (y < (ssize_t) image->rows-1)
6928                     {
6929                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6930                           exception);
6931                       (void) CopyMagickMemory(next,n,length);
6932                     }
6933
6934                   for (i=0; i < m; i++, yy++)
6935                   {
6936                     register Quantum
6937                       *pixels;
6938
6939                     assert(yy < (ssize_t) large_image->rows);
6940                     pixels=prev;
6941                     n=next;
6942                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6943                       1,exception);
6944                     q+=(large_image->columns-image->columns)*
6945                       GetPixelChannels(large_image);
6946
6947                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
6948                     {
6949                       /* To do: get color as function of indexes[x] */
6950                       /*
6951                       if (image->storage_class == PseudoClass)
6952                         {
6953                         }
6954                       */
6955
6956                       if (magn_methy <= 1)
6957                         {
6958                           /* replicate previous */
6959                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6960                           SetPixelGreen(large_image,GetPixelGreen(image,
6961                              pixels),q);
6962                           SetPixelBlue(large_image,GetPixelBlue(image,
6963                              pixels),q);
6964                           SetPixelAlpha(large_image,GetPixelAlpha(image,
6965                              pixels),q);
6966                         }
6967
6968                       else if (magn_methy == 2 || magn_methy == 4)
6969                         {
6970                           if (i == 0)
6971                             {
6972                               SetPixelRed(large_image,GetPixelRed(image,
6973                                  pixels),q);
6974                               SetPixelGreen(large_image,GetPixelGreen(image,
6975                                  pixels),q);
6976                               SetPixelBlue(large_image,GetPixelBlue(image,
6977                                  pixels),q);
6978                               SetPixelAlpha(large_image,GetPixelAlpha(image,
6979                                  pixels),q);
6980                             }
6981
6982                           else
6983                             {
6984                               /* Interpolate */
6985                               SetPixelRed(large_image,((QM) (((ssize_t)
6986                                  (2*i*(GetPixelRed(image,n)
6987                                  -GetPixelRed(image,pixels)+m))/
6988                                  ((ssize_t) (m*2))
6989                                  +GetPixelRed(image,pixels)))),q);
6990                               SetPixelGreen(large_image,((QM) (((ssize_t)
6991                                  (2*i*(GetPixelGreen(image,n)
6992                                  -GetPixelGreen(image,pixels)+m))/
6993                                  ((ssize_t) (m*2))
6994                                  +GetPixelGreen(image,pixels)))),q);
6995                               SetPixelBlue(large_image,((QM) (((ssize_t)
6996                                  (2*i*(GetPixelBlue(image,n)
6997                                  -GetPixelBlue(image,pixels)+m))/
6998                                  ((ssize_t) (m*2))
6999                                  +GetPixelBlue(image,pixels)))),q);
7000
7001                               if (image->alpha_trait != UndefinedPixelTrait)
7002                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
7003                                     (2*i*(GetPixelAlpha(image,n)
7004                                     -GetPixelAlpha(image,pixels)+m))
7005                                     /((ssize_t) (m*2))+
7006                                    GetPixelAlpha(image,pixels)))),q);
7007                             }
7008
7009                           if (magn_methy == 4)
7010                             {
7011                               /* Replicate nearest */
7012                               if (i <= ((m+1) << 1))
7013                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
7014                                     pixels),q);
7015                               else
7016                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
7017                                     n),q);
7018                             }
7019                         }
7020
7021                       else /* if (magn_methy == 3 || magn_methy == 5) */
7022                         {
7023                           /* Replicate nearest */
7024                           if (i <= ((m+1) << 1))
7025                           {
7026                              SetPixelRed(large_image,GetPixelRed(image,
7027                                     pixels),q);
7028                              SetPixelGreen(large_image,GetPixelGreen(image,
7029                                     pixels),q);
7030                              SetPixelBlue(large_image,GetPixelBlue(image,
7031                                     pixels),q);
7032                              SetPixelAlpha(large_image,GetPixelAlpha(image,
7033                                     pixels),q);
7034                           }
7035
7036                           else
7037                           {
7038                              SetPixelRed(large_image,GetPixelRed(image,n),q);
7039                              SetPixelGreen(large_image,GetPixelGreen(image,n),
7040                                     q);
7041                              SetPixelBlue(large_image,GetPixelBlue(image,n),
7042                                     q);
7043                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
7044                                     q);
7045                           }
7046
7047                           if (magn_methy == 5)
7048                             {
7049                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
7050                                  (GetPixelAlpha(image,n)
7051                                  -GetPixelAlpha(image,pixels))
7052                                  +m))/((ssize_t) (m*2))
7053                                  +GetPixelAlpha(image,pixels)),q);
7054                             }
7055                         }
7056                       n+=GetPixelChannels(image);
7057                       q+=GetPixelChannels(large_image);
7058                       pixels+=GetPixelChannels(image);
7059                     } /* x */
7060
7061                     if (SyncAuthenticPixels(large_image,exception) == 0)
7062                       break;
7063
7064                   } /* i */
7065                 } /* y */
7066
7067                 prev=(Quantum *) RelinquishMagickMemory(prev);
7068                 next=(Quantum *) RelinquishMagickMemory(next);
7069
7070                 length=image->columns;
7071
7072                 if (logging != MagickFalse)
7073                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7074                     "    Delete original image");
7075
7076                 DeleteImageFromList(&image);
7077
7078                 image=large_image;
7079
7080                 mng_info->image=image;
7081
7082                 /* magnify the columns */
7083                 if (logging != MagickFalse)
7084                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7085                     "    Magnify the columns to %.20g",
7086                     (double) image->columns);
7087
7088                 for (y=0; y < (ssize_t) image->rows; y++)
7089                 {
7090                   register Quantum
7091                     *pixels;
7092
7093                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7094                   pixels=q+(image->columns-length)*GetPixelChannels(image);
7095                   n=pixels+GetPixelChannels(image);
7096
7097                   for (x=(ssize_t) (image->columns-length);
7098                     x < (ssize_t) image->columns; x++)
7099                   {
7100                     /* To do: Rewrite using Get/Set***PixelChannel() */
7101
7102                     if (x == (ssize_t) (image->columns-length))
7103                       m=(ssize_t) mng_info->magn_ml;
7104
7105                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
7106                       m=(ssize_t) mng_info->magn_mr;
7107
7108                     else if (magn_methx <= 1 &&
7109                         x == (ssize_t) image->columns-1)
7110                       m=(ssize_t) mng_info->magn_mr;
7111
7112                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
7113                       m=1;
7114
7115                     else
7116                       m=(ssize_t) mng_info->magn_mx;
7117
7118                     for (i=0; i < m; i++)
7119                     {
7120                       if (magn_methx <= 1)
7121                         {
7122                           /* replicate previous */
7123                           SetPixelRed(image,GetPixelRed(image,pixels),q);
7124                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7125                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7126                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7127                         }
7128
7129                       else if (magn_methx == 2 || magn_methx == 4)
7130                         {
7131                           if (i == 0)
7132                           {
7133                             SetPixelRed(image,GetPixelRed(image,pixels),q);
7134                             SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7135                             SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7136                             SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7137                           }
7138
7139                           /* To do: Rewrite using Get/Set***PixelChannel() */
7140                           else
7141                             {
7142                               /* Interpolate */
7143                               SetPixelRed(image,(QM) ((2*i*(
7144                                  GetPixelRed(image,n)
7145                                  -GetPixelRed(image,pixels))+m)
7146                                  /((ssize_t) (m*2))+
7147                                  GetPixelRed(image,pixels)),q);
7148
7149                               SetPixelGreen(image,(QM) ((2*i*(
7150                                  GetPixelGreen(image,n)
7151                                  -GetPixelGreen(image,pixels))+m)
7152                                  /((ssize_t) (m*2))+
7153                                  GetPixelGreen(image,pixels)),q);
7154
7155                               SetPixelBlue(image,(QM) ((2*i*(
7156                                  GetPixelBlue(image,n)
7157                                  -GetPixelBlue(image,pixels))+m)
7158                                  /((ssize_t) (m*2))+
7159                                  GetPixelBlue(image,pixels)),q);
7160                               if (image->alpha_trait != UndefinedPixelTrait)
7161                                  SetPixelAlpha(image,(QM) ((2*i*(
7162                                    GetPixelAlpha(image,n)
7163                                    -GetPixelAlpha(image,pixels))+m)
7164                                    /((ssize_t) (m*2))+
7165                                    GetPixelAlpha(image,pixels)),q);
7166                             }
7167
7168                           if (magn_methx == 4)
7169                             {
7170                               /* Replicate nearest */
7171                               if (i <= ((m+1) << 1))
7172                               {
7173                                  SetPixelAlpha(image,
7174                                    GetPixelAlpha(image,pixels)+0,q);
7175                               }
7176                               else
7177                               {
7178                                  SetPixelAlpha(image,
7179                                    GetPixelAlpha(image,n)+0,q);
7180                               }
7181                             }
7182                         }
7183
7184                       else /* if (magn_methx == 3 || magn_methx == 5) */
7185                         {
7186                           /* Replicate nearest */
7187                           if (i <= ((m+1) << 1))
7188                           {
7189                              SetPixelRed(image,GetPixelRed(image,pixels),q);
7190                              SetPixelGreen(image,GetPixelGreen(image,
7191                                  pixels),q);
7192                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7193                              SetPixelAlpha(image,GetPixelAlpha(image,
7194                                  pixels),q);
7195                           }
7196
7197                           else
7198                           {
7199                              SetPixelRed(image,GetPixelRed(image,n),q);
7200                              SetPixelGreen(image,GetPixelGreen(image,n),q);
7201                              SetPixelBlue(image,GetPixelBlue(image,n),q);
7202                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7203                           }
7204
7205                           if (magn_methx == 5)
7206                             {
7207                               /* Interpolate */
7208                               SetPixelAlpha(image,
7209                                  (QM) ((2*i*( GetPixelAlpha(image,n)
7210                                  -GetPixelAlpha(image,pixels))+m)/
7211                                  ((ssize_t) (m*2))
7212                                  +GetPixelAlpha(image,pixels)),q);
7213                             }
7214                         }
7215                       q+=GetPixelChannels(image);
7216                     }
7217                     n+=GetPixelChannels(image);
7218                   }
7219
7220                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
7221                     break;
7222                 }
7223 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7224               if (magn_methx != 1 || magn_methy != 1)
7225                 {
7226                 /*
7227                    Rescale pixels to Quantum
7228                 */
7229                    for (y=0; y < (ssize_t) image->rows; y++)
7230                    {
7231                      q=GetAuthenticPixels(image,0,y,image->columns,1,
7232                        exception);
7233
7234                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
7235                      {
7236                         SetPixelRed(image,ScaleShortToQuantum(
7237                           GetPixelRed(image,q)),q);
7238                         SetPixelGreen(image,ScaleShortToQuantum(
7239                           GetPixelGreen(image,q)),q);
7240                         SetPixelBlue(image,ScaleShortToQuantum(
7241                           GetPixelBlue(image,q)),q);
7242                         SetPixelAlpha(image,ScaleShortToQuantum(
7243                           GetPixelAlpha(image,q)),q);
7244                         q+=GetPixelChannels(image);
7245                      }
7246
7247                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
7248                        break;
7249                    }
7250                 }
7251 #endif
7252                 if (logging != MagickFalse)
7253                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7254                     "  Finished MAGN processing");
7255               }
7256           }
7257
7258         /*
7259           Crop_box is with respect to the upper left corner of the MNG.
7260         */
7261         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7262         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7263         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7264         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7265         crop_box=mng_minimum_box(crop_box,mng_info->clip);
7266         crop_box=mng_minimum_box(crop_box,mng_info->frame);
7267         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7268         if ((crop_box.left != (mng_info->image_box.left
7269             +mng_info->x_off[object_id])) ||
7270             (crop_box.right != (mng_info->image_box.right
7271             +mng_info->x_off[object_id])) ||
7272             (crop_box.top != (mng_info->image_box.top
7273             +mng_info->y_off[object_id])) ||
7274             (crop_box.bottom != (mng_info->image_box.bottom
7275             +mng_info->y_off[object_id])))
7276           {
7277             if (logging != MagickFalse)
7278               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7279                 "  Crop the PNG image");
7280
7281             if ((crop_box.left < crop_box.right) &&
7282                 (crop_box.top < crop_box.bottom))
7283               {
7284                 Image
7285                   *im;
7286
7287                 RectangleInfo
7288                   crop_info;
7289
7290                 /*
7291                   Crop_info is with respect to the upper left corner of
7292                   the image.
7293                 */
7294                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7295                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7296                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7297                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7298                 image->page.width=image->columns;
7299                 image->page.height=image->rows;
7300                 image->page.x=0;
7301                 image->page.y=0;
7302                 im=CropImage(image,&crop_info,exception);
7303
7304                 if (im != (Image *) NULL)
7305                   {
7306                     image->columns=im->columns;
7307                     image->rows=im->rows;
7308                     im=DestroyImage(im);
7309                     image->page.width=image->columns;
7310                     image->page.height=image->rows;
7311                     image->page.x=crop_box.left;
7312                     image->page.y=crop_box.top;
7313                   }
7314               }
7315
7316             else
7317               {
7318                 /*
7319                   No pixels in crop area.  The MNG spec still requires
7320                   a layer, though, so make a single transparent pixel in
7321                   the top left corner.
7322                 */
7323                 image->columns=1;
7324                 image->rows=1;
7325                 image->colors=2;
7326                 (void) SetImageBackgroundColor(image,exception);
7327                 image->page.width=1;
7328                 image->page.height=1;
7329                 image->page.x=0;
7330                 image->page.y=0;
7331               }
7332           }
7333 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7334         image=mng_info->image;
7335 #endif
7336       }
7337
7338 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7339       /* PNG does not handle depths greater than 16 so reduce it even
7340        * if lossy.
7341        */
7342       if (image->depth > 16)
7343          image->depth=16;
7344 #endif
7345
7346 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7347       if (image->depth > 8)
7348         {
7349           /* To do: fill low byte properly */
7350           image->depth=16;
7351         }
7352
7353       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7354          image->depth = 8;
7355 #endif
7356
7357       if (image_info->number_scenes != 0)
7358         {
7359           if (mng_info->scenes_found >
7360              (ssize_t) (image_info->first_scene+image_info->number_scenes))
7361             break;
7362         }
7363
7364       if (logging != MagickFalse)
7365         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7366           "  Finished reading image datastream.");
7367
7368   } while (LocaleCompare(image_info->magick,"MNG") == 0);
7369
7370   (void) CloseBlob(image);
7371
7372   if (logging != MagickFalse)
7373     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7374       "  Finished reading all image datastreams.");
7375
7376 #if defined(MNG_INSERT_LAYERS)
7377   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7378        (mng_info->mng_height))
7379     {
7380       /*
7381         Insert a background layer if nothing else was found.
7382       */
7383       if (logging != MagickFalse)
7384         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7385           "  No images found.  Inserting a background layer.");
7386
7387       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7388         {
7389           /*
7390             Allocate next image structure.
7391           */
7392           AcquireNextImage(image_info,image,exception);
7393           if (GetNextImageInList(image) == (Image *) NULL)
7394             {
7395               if (logging != MagickFalse)
7396                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7397                   "  Allocation failed, returning NULL.");
7398
7399               return(DestroyImageList(image));;
7400             }
7401           image=SyncNextImageInList(image);
7402         }
7403       image->columns=mng_info->mng_width;
7404       image->rows=mng_info->mng_height;
7405       image->page.width=mng_info->mng_width;
7406       image->page.height=mng_info->mng_height;
7407       image->page.x=0;
7408       image->page.y=0;
7409       image->background_color=mng_background_color;
7410       image->alpha_trait=UndefinedPixelTrait;
7411
7412       if (image_info->ping == MagickFalse)
7413         (void) SetImageBackgroundColor(image,exception);
7414
7415       mng_info->image_found++;
7416     }
7417 #endif
7418   image->iterations=mng_iterations;
7419
7420   if (mng_iterations == 1)
7421     image->start_loop=MagickTrue;
7422
7423   while (GetPreviousImageInList(image) != (Image *) NULL)
7424   {
7425     image_count++;
7426     if (image_count > 10*mng_info->image_found)
7427       {
7428         if (logging != MagickFalse)
7429           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7430
7431         (void) ThrowMagickException(exception,GetMagickModule(),
7432           CoderError,"Linked list is corrupted, beginning of list not found",
7433           "`%s'",image_info->filename);
7434
7435         return(DestroyImageList(image));
7436       }
7437
7438     image=GetPreviousImageInList(image);
7439
7440     if (GetNextImageInList(image) == (Image *) NULL)
7441       {
7442         if (logging != MagickFalse)
7443           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7444
7445         (void) ThrowMagickException(exception,GetMagickModule(),
7446           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7447           image_info->filename);
7448       }
7449   }
7450
7451   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7452              GetNextImageInList(image) ==
7453      (Image *) NULL)
7454     {
7455       if (logging != MagickFalse)
7456         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7457             "  First image null");
7458
7459       (void) ThrowMagickException(exception,GetMagickModule(),
7460         CoderError,"image->next for first image is NULL but shouldn't be.",
7461         "`%s'",image_info->filename);
7462     }
7463
7464   if (mng_info->image_found == 0)
7465     {
7466       if (logging != MagickFalse)
7467         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7468           "  No visible images found.");
7469
7470       (void) ThrowMagickException(exception,GetMagickModule(),
7471         CoderError,"No visible images in file","`%s'",image_info->filename);
7472
7473       return(DestroyImageList(image));
7474     }
7475
7476   if (mng_info->ticks_per_second)
7477     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7478             final_delay/mng_info->ticks_per_second;
7479
7480   else
7481     image->start_loop=MagickTrue;
7482
7483   /* Find final nonzero image delay */
7484   final_image_delay=0;
7485
7486   while (GetNextImageInList(image) != (Image *) NULL)
7487     {
7488       if (image->delay)
7489         final_image_delay=image->delay;
7490
7491       image=GetNextImageInList(image);
7492     }
7493
7494   if (final_delay < final_image_delay)
7495     final_delay=final_image_delay;
7496
7497   image->delay=final_delay;
7498
7499   if (logging != MagickFalse)
7500       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7501         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7502         (double) final_delay);
7503
7504   if (logging != MagickFalse)
7505     {
7506       int
7507         scene;
7508
7509       scene=0;
7510       image=GetFirstImageInList(image);
7511
7512       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7513         "  Before coalesce:");
7514
7515       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7516         "    scene 0 delay=%.20g",(double) image->delay);
7517
7518       while (GetNextImageInList(image) != (Image *) NULL)
7519       {
7520         image=GetNextImageInList(image);
7521         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7522           "    scene %.20g delay=%.20g",(double) scene++,
7523           (double) image->delay);
7524       }
7525     }
7526
7527   image=GetFirstImageInList(image);
7528 #ifdef MNG_COALESCE_LAYERS
7529   if (insert_layers)
7530     {
7531       Image
7532         *next_image,
7533         *next;
7534
7535       size_t
7536         scene;
7537
7538       if (logging != MagickFalse)
7539         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7540           "  Coalesce Images");
7541
7542       scene=image->scene;
7543       next_image=CoalesceImages(image,exception);
7544
7545       if (next_image == (Image *) NULL)
7546         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7547
7548       image=DestroyImageList(image);
7549       image=next_image;
7550
7551       for (next=image; next != (Image *) NULL; next=next_image)
7552       {
7553          next->page.width=mng_info->mng_width;
7554          next->page.height=mng_info->mng_height;
7555          next->page.x=0;
7556          next->page.y=0;
7557          next->scene=scene++;
7558          next_image=GetNextImageInList(next);
7559
7560          if (next_image == (Image *) NULL)
7561            break;
7562
7563          if (next->delay == 0)
7564            {
7565              scene--;
7566              next_image->previous=GetPreviousImageInList(next);
7567              if (GetPreviousImageInList(next) == (Image *) NULL)
7568                image=next_image;
7569              else
7570                next->previous->next=next_image;
7571              next=DestroyImage(next);
7572            }
7573       }
7574     }
7575 #endif
7576
7577   while (GetNextImageInList(image) != (Image *) NULL)
7578       image=GetNextImageInList(image);
7579
7580   image->dispose=BackgroundDispose;
7581
7582   if (logging != MagickFalse)
7583     {
7584       int
7585         scene;
7586
7587       scene=0;
7588       image=GetFirstImageInList(image);
7589
7590       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7591         "  After coalesce:");
7592
7593       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7594         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7595         (double) image->dispose);
7596
7597       while (GetNextImageInList(image) != (Image *) NULL)
7598       {
7599         image=GetNextImageInList(image);
7600
7601         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7602           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7603           (double) image->delay,(double) image->dispose);
7604       }
7605    }
7606
7607   if (logging != MagickFalse)
7608     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7609       "  exit ReadOneMNGImage();");
7610
7611   return(image);
7612 }
7613
7614 static Image *ReadMNGImage(const ImageInfo *image_info,
7615      ExceptionInfo *exception)
7616 {
7617   Image
7618     *image;
7619
7620   MagickBooleanType
7621     logging,
7622     status;
7623
7624   MngInfo
7625     *mng_info;
7626
7627   /* Open image file.  */
7628
7629   assert(image_info != (const ImageInfo *) NULL);
7630   assert(image_info->signature == MagickCoreSignature);
7631   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
7632      image_info->filename);
7633   assert(exception != (ExceptionInfo *) NULL);
7634   assert(exception->signature == MagickCoreSignature);
7635   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
7636   image=AcquireImage(image_info,exception);
7637   mng_info=(MngInfo *) NULL;
7638   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
7639
7640   if (status == MagickFalse)
7641     return((Image *) NULL);
7642
7643   /* Allocate a MngInfo structure.  */
7644
7645   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7646
7647   if (mng_info == (MngInfo *) NULL)
7648     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7649
7650   /* Initialize members of the MngInfo structure.  */
7651
7652   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
7653   mng_info->image=image;
7654   image=ReadOneMNGImage(mng_info,image_info,exception);
7655   mng_info=MngInfoFreeStruct(mng_info);
7656
7657   if (image == (Image *) NULL)
7658     {
7659       if (logging != MagickFalse)
7660         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7661           "exit ReadMNGImage() with error");
7662
7663       return((Image *) NULL);
7664     }
7665   (void) CloseBlob(image);
7666
7667   if (logging != MagickFalse)
7668     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7669
7670   return(GetFirstImageInList(image));
7671 }
7672 #else /* PNG_LIBPNG_VER > 10011 */
7673 static Image *ReadPNGImage(const ImageInfo *image_info,
7674    ExceptionInfo *exception)
7675 {
7676   printf("Your PNG library is too old: You have libpng-%s\n",
7677      PNG_LIBPNG_VER_STRING);
7678
7679   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7680     "PNG library is too old","`%s'",image_info->filename);
7681
7682   return(Image *) NULL;
7683 }
7684
7685 static Image *ReadMNGImage(const ImageInfo *image_info,
7686    ExceptionInfo *exception)
7687 {
7688   return(ReadPNGImage(image_info,exception));
7689 }
7690 #endif /* PNG_LIBPNG_VER > 10011 */
7691 #endif
7692 \f
7693 /*
7694 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7695 %                                                                             %
7696 %                                                                             %
7697 %                                                                             %
7698 %   R e g i s t e r P N G I m a g e                                           %
7699 %                                                                             %
7700 %                                                                             %
7701 %                                                                             %
7702 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7703 %
7704 %  RegisterPNGImage() adds properties for the PNG image format to
7705 %  the list of supported formats.  The properties include the image format
7706 %  tag, a method to read and/or write the format, whether the format
7707 %  supports the saving of more than one frame to the same file or blob,
7708 %  whether the format supports native in-memory I/O, and a brief
7709 %  description of the format.
7710 %
7711 %  The format of the RegisterPNGImage method is:
7712 %
7713 %      size_t RegisterPNGImage(void)
7714 %
7715 */
7716 ModuleExport size_t RegisterPNGImage(void)
7717 {
7718   char
7719     version[MagickPathExtent];
7720
7721   MagickInfo
7722     *entry;
7723
7724   static const char
7725     *PNGNote=
7726     {
7727       "See http://www.libpng.org/ for details about the PNG format."
7728     },
7729
7730     *JNGNote=
7731     {
7732       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7733       "format."
7734     },
7735
7736     *MNGNote=
7737     {
7738       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7739       "format."
7740     };
7741
7742   *version='\0';
7743
7744 #if defined(PNG_LIBPNG_VER_STRING)
7745   (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7746   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
7747    MagickPathExtent);
7748
7749   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7750     {
7751       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7752       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7753             MagickPathExtent);
7754     }
7755 #endif
7756
7757   entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7758   entry->flags|=CoderDecoderSeekableStreamFlag;
7759
7760 #if defined(MAGICKCORE_PNG_DELEGATE)
7761   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7762   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7763 #endif
7764
7765   entry->magick=(IsImageFormatHandler *) IsMNG;
7766
7767   if (*version != '\0')
7768     entry->version=ConstantString(version);
7769
7770   entry->mime_type=ConstantString("video/x-mng");
7771   entry->note=ConstantString(MNGNote);
7772   (void) RegisterMagickInfo(entry);
7773
7774   entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7775
7776 #if defined(MAGICKCORE_PNG_DELEGATE)
7777   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7778   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7779 #endif
7780
7781   entry->magick=(IsImageFormatHandler *) IsPNG;
7782   entry->flags|=CoderDecoderSeekableStreamFlag;
7783   entry->flags^=CoderAdjoinFlag;
7784   entry->mime_type=ConstantString("image/png");
7785
7786   if (*version != '\0')
7787     entry->version=ConstantString(version);
7788
7789   entry->note=ConstantString(PNGNote);
7790   (void) RegisterMagickInfo(entry);
7791
7792   entry=AcquireMagickInfo("PNG","PNG8",
7793     "8-bit indexed with optional binary transparency");
7794
7795 #if defined(MAGICKCORE_PNG_DELEGATE)
7796   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7797   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7798 #endif
7799
7800   entry->magick=(IsImageFormatHandler *) IsPNG;
7801   entry->flags|=CoderDecoderSeekableStreamFlag;
7802   entry->flags^=CoderAdjoinFlag;
7803   entry->mime_type=ConstantString("image/png");
7804   (void) RegisterMagickInfo(entry);
7805
7806   entry=AcquireMagickInfo("PNG","PNG24",
7807     "opaque or binary transparent 24-bit RGB");
7808   *version='\0';
7809
7810 #if defined(ZLIB_VERSION)
7811   (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7812   (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7813
7814   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7815     {
7816       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7817       (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7818     }
7819 #endif
7820
7821   if (*version != '\0')
7822     entry->version=ConstantString(version);
7823
7824 #if defined(MAGICKCORE_PNG_DELEGATE)
7825   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7826   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7827 #endif
7828
7829   entry->magick=(IsImageFormatHandler *) IsPNG;
7830   entry->flags|=CoderDecoderSeekableStreamFlag;
7831   entry->flags^=CoderAdjoinFlag;
7832   entry->mime_type=ConstantString("image/png");
7833   (void) RegisterMagickInfo(entry);
7834
7835   entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7836
7837 #if defined(MAGICKCORE_PNG_DELEGATE)
7838   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7839   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7840 #endif
7841
7842   entry->magick=(IsImageFormatHandler *) IsPNG;
7843   entry->flags|=CoderDecoderSeekableStreamFlag;
7844   entry->flags^=CoderAdjoinFlag;
7845   entry->mime_type=ConstantString("image/png");
7846   (void) RegisterMagickInfo(entry);
7847
7848   entry=AcquireMagickInfo("PNG","PNG48",
7849     "opaque or binary transparent 48-bit RGB");
7850
7851 #if defined(MAGICKCORE_PNG_DELEGATE)
7852   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7853   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7854 #endif
7855
7856   entry->magick=(IsImageFormatHandler *) IsPNG;
7857   entry->flags|=CoderDecoderSeekableStreamFlag;
7858   entry->flags^=CoderAdjoinFlag;
7859   entry->mime_type=ConstantString("image/png");
7860   (void) RegisterMagickInfo(entry);
7861
7862   entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7863
7864 #if defined(MAGICKCORE_PNG_DELEGATE)
7865   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7866   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7867 #endif
7868
7869   entry->magick=(IsImageFormatHandler *) IsPNG;
7870   entry->flags|=CoderDecoderSeekableStreamFlag;
7871   entry->flags^=CoderAdjoinFlag;
7872   entry->mime_type=ConstantString("image/png");
7873   (void) RegisterMagickInfo(entry);
7874
7875   entry=AcquireMagickInfo("PNG","PNG00",
7876     "PNG inheriting bit-depth, color-type from original, if possible");
7877
7878 #if defined(MAGICKCORE_PNG_DELEGATE)
7879   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7880   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7881 #endif
7882
7883   entry->magick=(IsImageFormatHandler *) IsPNG;
7884   entry->flags|=CoderDecoderSeekableStreamFlag;
7885   entry->flags^=CoderAdjoinFlag;
7886   entry->mime_type=ConstantString("image/png");
7887   (void) RegisterMagickInfo(entry);
7888
7889   entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7890
7891 #if defined(JNG_SUPPORTED)
7892 #if defined(MAGICKCORE_PNG_DELEGATE)
7893   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7894   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7895 #endif
7896 #endif
7897
7898   entry->magick=(IsImageFormatHandler *) IsJNG;
7899   entry->flags|=CoderDecoderSeekableStreamFlag;
7900   entry->flags^=CoderAdjoinFlag;
7901   entry->mime_type=ConstantString("image/x-jng");
7902   entry->note=ConstantString(JNGNote);
7903   (void) RegisterMagickInfo(entry);
7904
7905 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7906   ping_semaphore=AcquireSemaphoreInfo();
7907 #endif
7908
7909   return(MagickImageCoderSignature);
7910 }
7911 \f
7912 /*
7913 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7914 %                                                                             %
7915 %                                                                             %
7916 %                                                                             %
7917 %   U n r e g i s t e r P N G I m a g e                                       %
7918 %                                                                             %
7919 %                                                                             %
7920 %                                                                             %
7921 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7922 %
7923 %  UnregisterPNGImage() removes format registrations made by the
7924 %  PNG module from the list of supported formats.
7925 %
7926 %  The format of the UnregisterPNGImage method is:
7927 %
7928 %      UnregisterPNGImage(void)
7929 %
7930 */
7931 ModuleExport void UnregisterPNGImage(void)
7932 {
7933   (void) UnregisterMagickInfo("MNG");
7934   (void) UnregisterMagickInfo("PNG");
7935   (void) UnregisterMagickInfo("PNG8");
7936   (void) UnregisterMagickInfo("PNG24");
7937   (void) UnregisterMagickInfo("PNG32");
7938   (void) UnregisterMagickInfo("PNG48");
7939   (void) UnregisterMagickInfo("PNG64");
7940   (void) UnregisterMagickInfo("PNG00");
7941   (void) UnregisterMagickInfo("JNG");
7942
7943 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7944   if (ping_semaphore != (SemaphoreInfo *) NULL)
7945     RelinquishSemaphoreInfo(&ping_semaphore);
7946 #endif
7947 }
7948 \f
7949 #if defined(MAGICKCORE_PNG_DELEGATE)
7950 #if PNG_LIBPNG_VER > 10011
7951 /*
7952 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7953 %                                                                             %
7954 %                                                                             %
7955 %                                                                             %
7956 %   W r i t e M N G I m a g e                                                 %
7957 %                                                                             %
7958 %                                                                             %
7959 %                                                                             %
7960 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7961 %
7962 %  WriteMNGImage() writes an image in the Portable Network Graphics
7963 %  Group's "Multiple-image Network Graphics" encoded image format.
7964 %
7965 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
7966 %
7967 %  The format of the WriteMNGImage method is:
7968 %
7969 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7970 %        Image *image,ExceptionInfo *exception)
7971 %
7972 %  A description of each parameter follows.
7973 %
7974 %    o image_info: the image info.
7975 %
7976 %    o image:  The image.
7977 %
7978 %    o exception: return any errors or warnings in this structure.
7979 %
7980 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7981 %    "To do" under ReadPNGImage):
7982 %
7983 %    Preserve all unknown and not-yet-handled known chunks found in input
7984 %    PNG file and copy them  into output PNG files according to the PNG
7985 %    copying rules.
7986 %
7987 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
7988 %
7989 %    Improve selection of color type (use indexed-colour or indexed-colour
7990 %    with tRNS when 256 or fewer unique RGBA values are present).
7991 %
7992 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7993 %    This will be complicated if we limit ourselves to generating MNG-LC
7994 %    files.  For now we ignore disposal method 3 and simply overlay the next
7995 %    image on it.
7996 %
7997 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
7998 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
7999 %    [mostly done 15 June 1999 but still need to take care of tRNS]
8000 %
8001 %    Check for identical sRGB and replace with a global sRGB (and remove
8002 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
8003 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
8004 %    local gAMA/cHRM with local sRGB if appropriate).
8005 %
8006 %    Check for identical sBIT chunks and write global ones.
8007 %
8008 %    Provide option to skip writing the signature tEXt chunks.
8009 %
8010 %    Use signatures to detect identical objects and reuse the first
8011 %    instance of such objects instead of writing duplicate objects.
8012 %
8013 %    Use a smaller-than-32k value of compression window size when
8014 %    appropriate.
8015 %
8016 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
8017 %    ancillary text chunks and save profiles.
8018 %
8019 %    Provide an option to force LC files (to ensure exact framing rate)
8020 %    instead of VLC.
8021 %
8022 %    Provide an option to force VLC files instead of LC, even when offsets
8023 %    are present.  This will involve expanding the embedded images with a
8024 %    transparent region at the top and/or left.
8025 */
8026
8027 static void
8028 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
8029    png_info *ping_info, unsigned char *profile_type, unsigned char
8030    *profile_description, unsigned char *profile_data, png_uint_32 length)
8031 {
8032    png_textp
8033      text;
8034
8035    register ssize_t
8036      i;
8037
8038    unsigned char
8039      *sp;
8040
8041    png_charp
8042      dp;
8043
8044    png_uint_32
8045      allocated_length,
8046      description_length;
8047
8048    unsigned char
8049      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
8050
8051    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
8052       return;
8053
8054    if (image_info->verbose)
8055      {
8056        (void) printf("writing raw profile: type=%s, length=%.20g\n",
8057          (char *) profile_type, (double) length);
8058      }
8059
8060 #if PNG_LIBPNG_VER >= 10400
8061    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
8062 #else
8063    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
8064 #endif
8065    description_length=(png_uint_32) strlen((const char *) profile_description);
8066    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
8067       + description_length);
8068 #if PNG_LIBPNG_VER >= 10400
8069    text[0].text=(png_charp) png_malloc(ping,
8070       (png_alloc_size_t) allocated_length);
8071    text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
8072 #else
8073    text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
8074    text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
8075 #endif
8076    text[0].key[0]='\0';
8077    (void) ConcatenateMagickString(text[0].key,
8078       "Raw profile type ",MagickPathExtent);
8079    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
8080    sp=profile_data;
8081    dp=text[0].text;
8082    *dp++='\n';
8083    (void) CopyMagickString(dp,(const char *) profile_description,
8084      allocated_length);
8085    dp+=description_length;
8086    *dp++='\n';
8087    (void) FormatLocaleString(dp,allocated_length-
8088      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
8089    dp+=8;
8090
8091    for (i=0; i < (ssize_t) length; i++)
8092    {
8093      if (i%36 == 0)
8094        *dp++='\n';
8095      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
8096      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
8097    }
8098
8099    *dp++='\n';
8100    *dp='\0';
8101    text[0].text_length=(png_size_t) (dp-text[0].text);
8102    text[0].compression=image_info->compression == NoCompression ||
8103      (image_info->compression == UndefinedCompression &&
8104      text[0].text_length < 128) ? -1 : 0;
8105
8106    if (text[0].text_length <= allocated_length)
8107      png_set_text(ping,ping_info,text,1);
8108
8109    png_free(ping,text[0].text);
8110    png_free(ping,text[0].key);
8111    png_free(ping,text);
8112 }
8113
8114 static inline MagickBooleanType Magick_png_color_equal(const Image *image,
8115   const Quantum *p, const PixelInfo *q)
8116 {
8117   MagickRealType
8118     value;
8119
8120   value=(MagickRealType) p[image->channel_map[RedPixelChannel].offset];
8121   if (AbsolutePixelValue(value-q->red) >= MagickEpsilon)
8122     return(MagickFalse);
8123   value=(MagickRealType) p[image->channel_map[GreenPixelChannel].offset];
8124   if (AbsolutePixelValue(value-q->green) >= MagickEpsilon)
8125     return(MagickFalse);
8126   value=(MagickRealType) p[image->channel_map[BluePixelChannel].offset];
8127   if (AbsolutePixelValue(value-q->blue) >= MagickEpsilon)
8128     return(MagickFalse);
8129
8130   return(MagickTrue);
8131 }
8132
8133 #if defined(PNG_tIME_SUPPORTED)
8134 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
8135   const char *date,ExceptionInfo *exception)
8136 {
8137   unsigned int
8138     day,
8139     hour,
8140     minute,
8141     month,
8142     second,
8143     year;
8144
8145   png_time
8146     ptime;
8147
8148   time_t
8149     ttime;
8150
8151   if (date != (const char *) NULL)
8152     {
8153       if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
8154           &second) != 6)
8155         {
8156           (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8157             "Invalid date format specified for png:tIME","`%s'",
8158             image->filename);
8159           return;
8160         }
8161       ptime.year=(png_uint_16) year;
8162       ptime.month=(png_byte) month;
8163       ptime.day=(png_byte) day;
8164       ptime.hour=(png_byte) hour;
8165       ptime.minute=(png_byte) minute;
8166       ptime.second=(png_byte) second;
8167     }
8168   else
8169   {
8170     time(&ttime);
8171     png_convert_from_time_t(&ptime,ttime);
8172   }
8173   png_set_tIME(ping,info,&ptime);
8174 }
8175 #endif
8176
8177 /* Write one PNG image */
8178 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
8179   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
8180 {
8181   char
8182     im_vers[32],
8183     libpng_runv[32],
8184     libpng_vers[32],
8185     zlib_runv[32],
8186     zlib_vers[32];
8187
8188   Image
8189     *image;
8190
8191   ImageInfo
8192     *image_info;
8193
8194   char
8195     s[2];
8196
8197   const char
8198     *name,
8199     *property,
8200     *value;
8201
8202   const StringInfo
8203     *profile;
8204
8205   int
8206     num_passes,
8207     pass,
8208     ping_wrote_caNv;
8209
8210   png_byte
8211      ping_trans_alpha[256];
8212
8213   png_color
8214      palette[257];
8215
8216   png_color_16
8217     ping_background,
8218     ping_trans_color;
8219
8220   png_info
8221     *ping_info;
8222
8223   png_struct
8224     *ping;
8225
8226   png_uint_32
8227     ping_height,
8228     ping_width;
8229
8230   ssize_t
8231     y;
8232
8233   MagickBooleanType
8234     image_matte,
8235     logging,
8236     matte,
8237
8238     ping_have_blob,
8239     ping_have_cheap_transparency,
8240     ping_have_color,
8241     ping_have_non_bw,
8242     ping_have_PLTE,
8243     ping_have_bKGD,
8244     ping_have_eXIf,
8245     ping_have_iCCP,
8246     ping_have_pHYs,
8247     ping_have_sRGB,
8248     ping_have_tRNS,
8249
8250     ping_exclude_bKGD,
8251     ping_exclude_cHRM,
8252     ping_exclude_date,
8253     /* ping_exclude_EXIF, */
8254     ping_exclude_eXIf,
8255     ping_exclude_gAMA,
8256     ping_exclude_iCCP,
8257     /* ping_exclude_iTXt, */
8258     ping_exclude_oFFs,
8259     ping_exclude_pHYs,
8260     ping_exclude_sRGB,
8261     ping_exclude_tEXt,
8262     ping_exclude_tIME,
8263     /* ping_exclude_tRNS, */
8264     ping_exclude_caNv,
8265     ping_exclude_zCCP, /* hex-encoded iCCP */
8266     ping_exclude_zTXt,
8267
8268     ping_preserve_colormap,
8269     ping_preserve_iCCP,
8270     ping_need_colortype_warning,
8271
8272     status,
8273     tried_332,
8274     tried_333,
8275     tried_444;
8276
8277   MemoryInfo
8278     *volatile pixel_info;
8279
8280   QuantumInfo
8281     *quantum_info;
8282
8283   PNGErrorInfo
8284     error_info;
8285
8286   register ssize_t
8287     i,
8288     x;
8289
8290   unsigned char
8291     *ping_pixels;
8292
8293   volatile int
8294     image_colors,
8295     ping_bit_depth,
8296     ping_color_type,
8297     ping_interlace_method,
8298     ping_compression_method,
8299     ping_filter_method,
8300     ping_num_trans;
8301
8302   volatile size_t
8303     image_depth,
8304     old_bit_depth;
8305
8306   size_t
8307     quality,
8308     rowbytes,
8309     save_image_depth;
8310
8311   int
8312     j,
8313     number_colors,
8314     number_opaque,
8315     number_semitransparent,
8316     number_transparent,
8317     ping_pHYs_unit_type;
8318
8319   png_uint_32
8320     ping_pHYs_x_resolution,
8321     ping_pHYs_y_resolution;
8322
8323   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8324     "  Enter WriteOnePNGImage()");
8325
8326   image = CloneImage(IMimage,0,0,MagickFalse,exception);
8327   if (image == (Image *) NULL)
8328     return(MagickFalse);
8329   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8330   if (image_info == (ImageInfo *) NULL)
8331     ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8332
8333   /* Define these outside of the following "if logging()" block so they will
8334    * show in debuggers.
8335    */
8336   *im_vers='\0';
8337   (void) ConcatenateMagickString(im_vers,
8338          MagickLibVersionText,MagickPathExtent);
8339   (void) ConcatenateMagickString(im_vers,
8340          MagickLibAddendum,MagickPathExtent);
8341
8342   *libpng_vers='\0';
8343   (void) ConcatenateMagickString(libpng_vers,
8344          PNG_LIBPNG_VER_STRING,32);
8345   *libpng_runv='\0';
8346   (void) ConcatenateMagickString(libpng_runv,
8347          png_get_libpng_ver(NULL),32);
8348
8349   *zlib_vers='\0';
8350   (void) ConcatenateMagickString(zlib_vers,
8351          ZLIB_VERSION,32);
8352   *zlib_runv='\0';
8353   (void) ConcatenateMagickString(zlib_runv,
8354          zlib_version,32);
8355
8356   if (logging != MagickFalse)
8357     {
8358        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8359           "    IM version     = %s", im_vers);
8360        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8361           "    Libpng version = %s", libpng_vers);
8362        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8363        {
8364        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8365           "      running with   %s", libpng_runv);
8366        }
8367        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8368           "    Zlib version   = %s", zlib_vers);
8369        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8370        {
8371        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8372           "      running with   %s", zlib_runv);
8373        }
8374     }
8375
8376   /* Initialize some stuff */
8377   ping_bit_depth=0,
8378   ping_color_type=0,
8379   ping_interlace_method=0,
8380   ping_compression_method=0,
8381   ping_filter_method=0,
8382   ping_num_trans = 0;
8383
8384   ping_background.red = 0;
8385   ping_background.green = 0;
8386   ping_background.blue = 0;
8387   ping_background.gray = 0;
8388   ping_background.index = 0;
8389
8390   ping_trans_color.red=0;
8391   ping_trans_color.green=0;
8392   ping_trans_color.blue=0;
8393   ping_trans_color.gray=0;
8394
8395   ping_pHYs_unit_type = 0;
8396   ping_pHYs_x_resolution = 0;
8397   ping_pHYs_y_resolution = 0;
8398
8399   ping_have_blob=MagickFalse;
8400   ping_have_cheap_transparency=MagickFalse;
8401   ping_have_color=MagickTrue;
8402   ping_have_non_bw=MagickTrue;
8403   ping_have_PLTE=MagickFalse;
8404   ping_have_bKGD=MagickFalse;
8405   ping_have_eXIf=MagickTrue;
8406   ping_have_iCCP=MagickFalse;
8407   ping_have_pHYs=MagickFalse;
8408   ping_have_sRGB=MagickFalse;
8409   ping_have_tRNS=MagickFalse;
8410
8411   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8412   ping_exclude_caNv=mng_info->ping_exclude_caNv;
8413   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8414   ping_exclude_date=mng_info->ping_exclude_date;
8415   ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
8416   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8417   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8418   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8419   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8420   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8421   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8422   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8423   ping_exclude_tIME=mng_info->ping_exclude_tIME;
8424   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8425   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8426   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8427
8428   ping_preserve_colormap = mng_info->ping_preserve_colormap;
8429   ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8430   ping_need_colortype_warning = MagickFalse;
8431
8432   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8433    * i.e., eliminate the ICC profile and set image->rendering_intent.
8434    * Note that this will not involve any changes to the actual pixels
8435    * but merely passes information to applications that read the resulting
8436    * PNG image.
8437    *
8438    * To do: recognize other variants of the sRGB profile, using the CRC to
8439    * verify all recognized variants including the 7 already known.
8440    *
8441    * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8442    *
8443    * Use something other than image->rendering_intent to record the fact
8444    * that the sRGB profile was found.
8445    *
8446    * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8447    * profile.  Record the Blackpoint Compensation, if any.
8448    */
8449    if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8450    {
8451       char
8452         *name;
8453
8454       const StringInfo
8455         *profile;
8456
8457       ResetImageProfileIterator(image);
8458       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8459       {
8460         profile=GetImageProfile(image,name);
8461
8462         if (profile != (StringInfo *) NULL)
8463           {
8464             if ((LocaleCompare(name,"ICC") == 0) ||
8465                 (LocaleCompare(name,"ICM") == 0))
8466
8467              {
8468                  int
8469                    icheck,
8470                    got_crc=0;
8471
8472
8473                  png_uint_32
8474                    length,
8475                    profile_crc=0;
8476
8477                  unsigned char
8478                    *data;
8479
8480                  length=(png_uint_32) GetStringInfoLength(profile);
8481
8482                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8483                  {
8484                    if (length == sRGB_info[icheck].len)
8485                    {
8486                      if (got_crc == 0)
8487                      {
8488                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8489                          "    Got a %lu-byte ICC profile (potentially sRGB)",
8490                          (unsigned long) length);
8491
8492                        data=GetStringInfoDatum(profile);
8493                        profile_crc=crc32(0,data,length);
8494
8495                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8496                            "      with crc=%8x",(unsigned int) profile_crc);
8497                        got_crc++;
8498                      }
8499
8500                      if (profile_crc == sRGB_info[icheck].crc)
8501                      {
8502                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8503                             "      It is sRGB with rendering intent = %s",
8504                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
8505                              sRGB_info[icheck].intent));
8506                         if (image->rendering_intent==UndefinedIntent)
8507                         {
8508                           image->rendering_intent=
8509                           Magick_RenderingIntent_from_PNG_RenderingIntent(
8510                              sRGB_info[icheck].intent);
8511                         }
8512                         ping_exclude_iCCP = MagickTrue;
8513                         ping_exclude_zCCP = MagickTrue;
8514                         ping_have_sRGB = MagickTrue;
8515                         break;
8516                      }
8517                    }
8518                  }
8519                  if (sRGB_info[icheck].len == 0)
8520                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8521                         "    Got %lu-byte ICC profile not recognized as sRGB",
8522                         (unsigned long) length);
8523               }
8524           }
8525         name=GetNextImageProfile(image);
8526       }
8527   }
8528
8529   number_opaque = 0;
8530   number_semitransparent = 0;
8531   number_transparent = 0;
8532
8533   if (logging != MagickFalse)
8534     {
8535       if (image->storage_class == UndefinedClass)
8536           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8537           "    image->storage_class=UndefinedClass");
8538       if (image->storage_class == DirectClass)
8539           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8540           "    image->storage_class=DirectClass");
8541       if (image->storage_class == PseudoClass)
8542           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8543           "    image->storage_class=PseudoClass");
8544       (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8545           "    image->taint=MagickTrue":
8546           "    image->taint=MagickFalse");
8547       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8548           "    image->gamma=%g", image->gamma);
8549     }
8550
8551   if (image->storage_class == PseudoClass &&
8552      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8553      mng_info->write_png48 || mng_info->write_png64 ||
8554      (mng_info->write_png_colortype != 1 &&
8555      mng_info->write_png_colortype != 5)))
8556     {
8557       (void) SyncImage(image,exception);
8558       image->storage_class = DirectClass;
8559     }
8560
8561   if (ping_preserve_colormap == MagickFalse)
8562     {
8563       if (image->storage_class != PseudoClass && image->colormap != NULL)
8564         {
8565           /* Free the bogus colormap; it can cause trouble later */
8566            if (logging != MagickFalse)
8567               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8568               "    Freeing bogus colormap");
8569            (void) RelinquishMagickMemory(image->colormap);
8570            image->colormap=NULL;
8571         }
8572     }
8573
8574   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8575     (void) TransformImageColorspace(image,sRGBColorspace,exception);
8576
8577   /*
8578     Sometimes we get PseudoClass images whose RGB values don't match
8579     the colors in the colormap.  This code syncs the RGB values.
8580   */
8581   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8582      (void) SyncImage(image,exception);
8583
8584 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8585   if (image->depth > 8)
8586     {
8587       if (logging != MagickFalse)
8588         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8589           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8590
8591       image->depth=8;
8592     }
8593 #endif
8594
8595   /* Respect the -depth option */
8596   if (image->depth < 4)
8597     {
8598        register Quantum
8599          *r;
8600
8601        if (image->depth > 2)
8602          {
8603            /* Scale to 4-bit */
8604            LBR04PacketRGBO(image->background_color);
8605
8606            for (y=0; y < (ssize_t) image->rows; y++)
8607            {
8608              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8609
8610              if (r == (Quantum *) NULL)
8611                break;
8612
8613              for (x=0; x < (ssize_t) image->columns; x++)
8614              {
8615                 LBR04PixelRGBA(r);
8616                 r+=GetPixelChannels(image);
8617              }
8618
8619              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8620                 break;
8621            }
8622
8623            if (image->storage_class == PseudoClass && image->colormap != NULL)
8624            {
8625              for (i=0; i < (ssize_t) image->colors; i++)
8626              {
8627                LBR04PacketRGBO(image->colormap[i]);
8628              }
8629            }
8630          }
8631        else if (image->depth > 1)
8632          {
8633            /* Scale to 2-bit */
8634            LBR02PacketRGBO(image->background_color);
8635
8636            for (y=0; y < (ssize_t) image->rows; y++)
8637            {
8638              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8639
8640              if (r == (Quantum *) NULL)
8641                break;
8642
8643              for (x=0; x < (ssize_t) image->columns; x++)
8644              {
8645                 LBR02PixelRGBA(r);
8646                 r+=GetPixelChannels(image);
8647              }
8648
8649              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8650                 break;
8651            }
8652
8653            if (image->storage_class == PseudoClass && image->colormap != NULL)
8654            {
8655              for (i=0; i < (ssize_t) image->colors; i++)
8656              {
8657                LBR02PacketRGBO(image->colormap[i]);
8658              }
8659            }
8660          }
8661        else
8662          {
8663            /* Scale to 1-bit */
8664            LBR01PacketRGBO(image->background_color);
8665
8666            for (y=0; y < (ssize_t) image->rows; y++)
8667            {
8668              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8669
8670              if (r == (Quantum *) NULL)
8671                break;
8672
8673              for (x=0; x < (ssize_t) image->columns; x++)
8674              {
8675                 LBR01PixelRGBA(r);
8676                 r+=GetPixelChannels(image);
8677              }
8678
8679              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8680                 break;
8681            }
8682
8683            if (image->storage_class == PseudoClass && image->colormap != NULL)
8684            {
8685              for (i=0; i < (ssize_t) image->colors; i++)
8686              {
8687                LBR01PacketRGBO(image->colormap[i]);
8688              }
8689            }
8690          }
8691     }
8692
8693   /* To do: set to next higher multiple of 8 */
8694   if (image->depth < 8)
8695      image->depth=8;
8696
8697 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8698   /* PNG does not handle depths greater than 16 so reduce it even
8699    * if lossy
8700    */
8701   if (image->depth > 8)
8702       image->depth=16;
8703 #endif
8704
8705 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8706   if (image->depth > 8)
8707     {
8708       /* To do: fill low byte properly */
8709       image->depth=16;
8710     }
8711
8712   if (image->depth == 16 && mng_info->write_png_depth != 16)
8713     if (mng_info->write_png8 ||
8714         LosslessReduceDepthOK(image,exception) != MagickFalse)
8715       image->depth = 8;
8716 #endif
8717
8718   image_colors = (int) image->colors;
8719   number_opaque = (int) image->colors;
8720   number_transparent = 0;
8721   number_semitransparent = 0;
8722
8723   if (mng_info->write_png_colortype &&
8724      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8725      mng_info->write_png_colortype < 4 &&
8726      image->alpha_trait == UndefinedPixelTrait)))
8727   {
8728      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8729       * are not going to need the result.
8730       */
8731      if (mng_info->write_png_colortype == 1 ||
8732         mng_info->write_png_colortype == 5)
8733        ping_have_color=MagickFalse;
8734
8735      if (image->alpha_trait != UndefinedPixelTrait)
8736        {
8737          number_transparent = 2;
8738          number_semitransparent = 1;
8739        }
8740   }
8741
8742   if (mng_info->write_png_colortype < 7)
8743   {
8744   /* BUILD_PALETTE
8745    *
8746    * Normally we run this just once, but in the case of writing PNG8
8747    * we reduce the transparency to binary and run again, then if there
8748    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8749    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8750    * palette.  Then (To do) we take care of a final reduction that is only
8751    * needed if there are still 256 colors present and one of them has both
8752    * transparent and opaque instances.
8753    */
8754
8755   tried_332 = MagickFalse;
8756   tried_333 = MagickFalse;
8757   tried_444 = MagickFalse;
8758
8759   for (j=0; j<6; j++)
8760   {
8761     /*
8762      * Sometimes we get DirectClass images that have 256 colors or fewer.
8763      * This code will build a colormap.
8764      *
8765      * Also, sometimes we get PseudoClass images with an out-of-date
8766      * colormap.  This code will replace the colormap with a new one.
8767      * Sometimes we get PseudoClass images that have more than 256 colors.
8768      * This code will delete the colormap and change the image to
8769      * DirectClass.
8770      *
8771      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8772      * even though it sometimes contains left-over non-opaque values.
8773      *
8774      * Also we gather some information (number of opaque, transparent,
8775      * and semitransparent pixels, and whether the image has any non-gray
8776      * pixels or only black-and-white pixels) that we might need later.
8777      *
8778      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8779      * we need to check for bogus non-opaque values, at least.
8780      */
8781
8782    int
8783      n;
8784
8785    PixelInfo
8786      opaque[260],
8787      semitransparent[260],
8788      transparent[260];
8789
8790    register const Quantum
8791      *s;
8792
8793    register Quantum
8794      *q,
8795      *r;
8796
8797    if (logging != MagickFalse)
8798      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8799          "    Enter BUILD_PALETTE:");
8800
8801    if (logging != MagickFalse)
8802      {
8803        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8804              "      image->columns=%.20g",(double) image->columns);
8805        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8806              "      image->rows=%.20g",(double) image->rows);
8807        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8808              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
8809        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8810              "      image->depth=%.20g",(double) image->depth);
8811
8812        if (image->storage_class == PseudoClass && image->colormap != NULL)
8813        {
8814          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8815              "      Original colormap:");
8816          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8817              "        i    (red,green,blue,alpha)");
8818
8819          for (i=0; i < 256; i++)
8820          {
8821                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8822                    "        %d    (%d,%d,%d,%d)",
8823                     (int) i,
8824                     (int) image->colormap[i].red,
8825                     (int) image->colormap[i].green,
8826                     (int) image->colormap[i].blue,
8827                     (int) image->colormap[i].alpha);
8828          }
8829
8830          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8831          {
8832            if (i > 255)
8833              {
8834                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8835                    "        %d    (%d,%d,%d,%d)",
8836                     (int) i,
8837                     (int) image->colormap[i].red,
8838                     (int) image->colormap[i].green,
8839                     (int) image->colormap[i].blue,
8840                     (int) image->colormap[i].alpha);
8841              }
8842          }
8843        }
8844
8845        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8846            "      image->colors=%d",(int) image->colors);
8847
8848        if (image->colors == 0)
8849          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8850              "        (zero means unknown)");
8851
8852        if (ping_preserve_colormap == MagickFalse)
8853          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8854               "      Regenerate the colormap");
8855      }
8856
8857      image_colors=0;
8858      number_opaque = 0;
8859      number_semitransparent = 0;
8860      number_transparent = 0;
8861
8862      for (y=0; y < (ssize_t) image->rows; y++)
8863      {
8864        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8865
8866        if (q == (Quantum *) NULL)
8867          break;
8868
8869        for (x=0; x < (ssize_t) image->columns; x++)
8870        {
8871            if (image->alpha_trait == UndefinedPixelTrait ||
8872               GetPixelAlpha(image,q) == OpaqueAlpha)
8873              {
8874                if (number_opaque < 259)
8875                  {
8876                    if (number_opaque == 0)
8877                      {
8878                        GetPixelInfoPixel(image, q, opaque);
8879                        opaque[0].alpha=OpaqueAlpha;
8880                        number_opaque=1;
8881                      }
8882
8883                    for (i=0; i< (ssize_t) number_opaque; i++)
8884                      {
8885                        if (Magick_png_color_equal(image,q,opaque+i))
8886                          break;
8887                      }
8888
8889                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
8890                      {
8891                        number_opaque++;
8892                        GetPixelInfoPixel(image, q, opaque+i);
8893                        opaque[i].alpha=OpaqueAlpha;
8894                      }
8895                  }
8896              }
8897            else if (GetPixelAlpha(image,q) == TransparentAlpha)
8898              {
8899                if (number_transparent < 259)
8900                  {
8901                    if (number_transparent == 0)
8902                      {
8903                        GetPixelInfoPixel(image, q, transparent);
8904                        ping_trans_color.red=(unsigned short)
8905                          GetPixelRed(image,q);
8906                        ping_trans_color.green=(unsigned short)
8907                          GetPixelGreen(image,q);
8908                        ping_trans_color.blue=(unsigned short)
8909                          GetPixelBlue(image,q);
8910                        ping_trans_color.gray=(unsigned short)
8911                          GetPixelGray(image,q);
8912                        number_transparent = 1;
8913                      }
8914
8915                    for (i=0; i< (ssize_t) number_transparent; i++)
8916                      {
8917                        if (Magick_png_color_equal(image,q,transparent+i))
8918                          break;
8919                      }
8920
8921                    if (i ==  (ssize_t) number_transparent &&
8922                        number_transparent < 259)
8923                      {
8924                        number_transparent++;
8925                        GetPixelInfoPixel(image,q,transparent+i);
8926                      }
8927                  }
8928              }
8929            else
8930              {
8931                if (number_semitransparent < 259)
8932                  {
8933                    if (number_semitransparent == 0)
8934                      {
8935                        GetPixelInfoPixel(image,q,semitransparent);
8936                        number_semitransparent = 1;
8937                      }
8938
8939                    for (i=0; i< (ssize_t) number_semitransparent; i++)
8940                      {
8941                        if (Magick_png_color_equal(image,q,semitransparent+i)
8942                            && GetPixelAlpha(image,q) ==
8943                            semitransparent[i].alpha)
8944                          break;
8945                      }
8946
8947                    if (i ==  (ssize_t) number_semitransparent &&
8948                        number_semitransparent < 259)
8949                      {
8950                        number_semitransparent++;
8951                        GetPixelInfoPixel(image, q, semitransparent+i);
8952                      }
8953                  }
8954              }
8955            q+=GetPixelChannels(image);
8956         }
8957      }
8958
8959      if (mng_info->write_png8 == MagickFalse &&
8960          ping_exclude_bKGD == MagickFalse)
8961        {
8962          /* Add the background color to the palette, if it
8963           * isn't already there.
8964           */
8965           if (logging != MagickFalse)
8966             {
8967               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8968                   "      Check colormap for background (%d,%d,%d)",
8969                   (int) image->background_color.red,
8970                   (int) image->background_color.green,
8971                   (int) image->background_color.blue);
8972             }
8973           for (i=0; i<number_opaque; i++)
8974           {
8975              if (opaque[i].red == image->background_color.red &&
8976                  opaque[i].green == image->background_color.green &&
8977                  opaque[i].blue == image->background_color.blue)
8978                break;
8979           }
8980           if (number_opaque < 259 && i == number_opaque)
8981             {
8982                opaque[i] = image->background_color;
8983                ping_background.index = i;
8984                number_opaque++;
8985                if (logging != MagickFalse)
8986                  {
8987                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8988                        "      background_color index is %d",(int) i);
8989                  }
8990
8991             }
8992           else if (logging != MagickFalse)
8993               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8994                   "      No room in the colormap to add background color");
8995        }
8996
8997      image_colors=number_opaque+number_transparent+number_semitransparent;
8998
8999      if (logging != MagickFalse)
9000        {
9001          if (image_colors > 256)
9002             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9003                   "      image has more than 256 colors");
9004
9005          else
9006             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9007                   "      image has %d colors",image_colors);
9008        }
9009
9010      if (ping_preserve_colormap != MagickFalse)
9011        break;
9012
9013      if (mng_info->write_png_colortype != 7) /* We won't need this info */
9014        {
9015          ping_have_color=MagickFalse;
9016          ping_have_non_bw=MagickFalse;
9017
9018          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
9019          {
9020            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9021               "incompatible colorspace");
9022            ping_have_color=MagickTrue;
9023            ping_have_non_bw=MagickTrue;
9024          }
9025
9026          if(image_colors > 256)
9027            {
9028              for (y=0; y < (ssize_t) image->rows; y++)
9029              {
9030                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9031
9032                if (q == (Quantum *) NULL)
9033                  break;
9034
9035                s=q;
9036                for (x=0; x < (ssize_t) image->columns; x++)
9037                {
9038                  if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
9039                      GetPixelRed(image,s) != GetPixelBlue(image,s))
9040                    {
9041                       ping_have_color=MagickTrue;
9042                       ping_have_non_bw=MagickTrue;
9043                       break;
9044                    }
9045                  s+=GetPixelChannels(image);
9046                }
9047
9048                if (ping_have_color != MagickFalse)
9049                  break;
9050
9051                /* Worst case is black-and-white; we are looking at every
9052                 * pixel twice.
9053                 */
9054
9055                if (ping_have_non_bw == MagickFalse)
9056                  {
9057                    s=q;
9058                    for (x=0; x < (ssize_t) image->columns; x++)
9059                    {
9060                      if (GetPixelRed(image,s) != 0 &&
9061                          GetPixelRed(image,s) != QuantumRange)
9062                        {
9063                          ping_have_non_bw=MagickTrue;
9064                          break;
9065                        }
9066                      s+=GetPixelChannels(image);
9067                    }
9068                }
9069              }
9070            }
9071        }
9072
9073      if (image_colors < 257)
9074        {
9075          PixelInfo
9076            colormap[260];
9077
9078          /*
9079           * Initialize image colormap.
9080           */
9081
9082          if (logging != MagickFalse)
9083             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9084                   "      Sort the new colormap");
9085
9086         /* Sort palette, transparent first */;
9087
9088          n = 0;
9089
9090          for (i=0; i<number_transparent; i++)
9091             colormap[n++] = transparent[i];
9092
9093          for (i=0; i<number_semitransparent; i++)
9094             colormap[n++] = semitransparent[i];
9095
9096          for (i=0; i<number_opaque; i++)
9097             colormap[n++] = opaque[i];
9098
9099          ping_background.index +=
9100            (number_transparent + number_semitransparent);
9101
9102          /* image_colors < 257; search the colormap instead of the pixels
9103           * to get ping_have_color and ping_have_non_bw
9104           */
9105          for (i=0; i<n; i++)
9106          {
9107            if (ping_have_color == MagickFalse)
9108              {
9109                 if (colormap[i].red != colormap[i].green ||
9110                     colormap[i].red != colormap[i].blue)
9111                   {
9112                      ping_have_color=MagickTrue;
9113                      ping_have_non_bw=MagickTrue;
9114                      break;
9115                   }
9116               }
9117
9118            if (ping_have_non_bw == MagickFalse)
9119              {
9120                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
9121                    ping_have_non_bw=MagickTrue;
9122              }
9123           }
9124
9125         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
9126             (number_transparent == 0 && number_semitransparent == 0)) &&
9127             (((mng_info->write_png_colortype-1) ==
9128             PNG_COLOR_TYPE_PALETTE) ||
9129             (mng_info->write_png_colortype == 0)))
9130           {
9131             if (logging != MagickFalse)
9132               {
9133                 if (n !=  (ssize_t) image_colors)
9134                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9135                    "   image_colors (%d) and n (%d)  don't match",
9136                    image_colors, n);
9137
9138                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9139                    "      AcquireImageColormap");
9140               }
9141
9142             image->colors = image_colors;
9143
9144             if (AcquireImageColormap(image,image_colors,exception) ==
9145                 MagickFalse)
9146                ThrowWriterException(ResourceLimitError,
9147                    "MemoryAllocationFailed");
9148
9149             for (i=0; i< (ssize_t) image_colors; i++)
9150                image->colormap[i] = colormap[i];
9151
9152             if (logging != MagickFalse)
9153               {
9154                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9155                       "      image->colors=%d (%d)",
9156                       (int) image->colors, image_colors);
9157
9158                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9159                       "      Update the pixel indexes");
9160               }
9161
9162             /* Sync the pixel indices with the new colormap */
9163
9164             for (y=0; y < (ssize_t) image->rows; y++)
9165             {
9166               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9167
9168               if (q == (Quantum *) NULL)
9169                 break;
9170
9171               for (x=0; x < (ssize_t) image->columns; x++)
9172               {
9173                 for (i=0; i< (ssize_t) image_colors; i++)
9174                 {
9175                   if ((image->alpha_trait == UndefinedPixelTrait ||
9176                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
9177                       image->colormap[i].red == GetPixelRed(image,q) &&
9178                       image->colormap[i].green == GetPixelGreen(image,q) &&
9179                       image->colormap[i].blue == GetPixelBlue(image,q))
9180                   {
9181                     SetPixelIndex(image,i,q);
9182                     break;
9183                   }
9184                 }
9185                 q+=GetPixelChannels(image);
9186               }
9187
9188               if (SyncAuthenticPixels(image,exception) == MagickFalse)
9189                  break;
9190             }
9191           }
9192        }
9193
9194      if (logging != MagickFalse)
9195        {
9196          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9197             "      image->colors=%d", (int) image->colors);
9198
9199          if (image->colormap != NULL)
9200            {
9201              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9202                  "       i     (red,green,blue,alpha)");
9203
9204              for (i=0; i < (ssize_t) image->colors; i++)
9205              {
9206                if (i < 300 || i >= (ssize_t) image->colors - 10)
9207                  {
9208                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9209                        "       %d     (%d,%d,%d,%d)",
9210                         (int) i,
9211                         (int) image->colormap[i].red,
9212                         (int) image->colormap[i].green,
9213                         (int) image->colormap[i].blue,
9214                         (int) image->colormap[i].alpha);
9215                  }
9216              }
9217            }
9218
9219            if (number_transparent < 257)
9220              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9221                    "      number_transparent     = %d",
9222                    number_transparent);
9223            else
9224
9225              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9226                    "      number_transparent     > 256");
9227
9228            if (number_opaque < 257)
9229              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9230                    "      number_opaque          = %d",
9231                    number_opaque);
9232
9233            else
9234              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9235                    "      number_opaque          > 256");
9236
9237            if (number_semitransparent < 257)
9238              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9239                    "      number_semitransparent = %d",
9240                    number_semitransparent);
9241
9242            else
9243              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9244                    "      number_semitransparent > 256");
9245
9246            if (ping_have_non_bw == MagickFalse)
9247               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9248                     "      All pixels and the background are black or white");
9249
9250            else if (ping_have_color == MagickFalse)
9251               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9252                     "      All pixels and the background are gray");
9253
9254            else
9255               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9256                     "      At least one pixel or the background is non-gray");
9257
9258            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9259                "    Exit BUILD_PALETTE:");
9260        }
9261
9262    if (mng_info->write_png8 == MagickFalse)
9263       break;
9264
9265    /* Make any reductions necessary for the PNG8 format */
9266     if (image_colors <= 256 &&
9267         image_colors != 0 && image->colormap != NULL &&
9268         number_semitransparent == 0 &&
9269         number_transparent <= 1)
9270       break;
9271
9272     /* PNG8 can't have semitransparent colors so we threshold the
9273      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9274      * transparent color so if more than one is transparent we merge
9275      * them into image->background_color.
9276      */
9277     if (number_semitransparent != 0 || number_transparent > 1)
9278       {
9279         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9280             "    Thresholding the alpha channel to binary");
9281
9282         for (y=0; y < (ssize_t) image->rows; y++)
9283         {
9284           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9285
9286           if (r == (Quantum *) NULL)
9287             break;
9288
9289           for (x=0; x < (ssize_t) image->columns; x++)
9290           {
9291               if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9292                 {
9293                   SetPixelViaPixelInfo(image,&image->background_color,r);
9294                   SetPixelAlpha(image,TransparentAlpha,r);
9295                 }
9296               else
9297                   SetPixelAlpha(image,OpaqueAlpha,r);
9298               r+=GetPixelChannels(image);
9299           }
9300
9301           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9302              break;
9303
9304           if (image_colors != 0 && image_colors <= 256 &&
9305              image->colormap != NULL)
9306             for (i=0; i<image_colors; i++)
9307                 image->colormap[i].alpha =
9308                     (image->colormap[i].alpha > TransparentAlpha/2 ?
9309                     TransparentAlpha : OpaqueAlpha);
9310         }
9311       continue;
9312     }
9313
9314     /* PNG8 can't have more than 256 colors so we quantize the pixels and
9315      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
9316      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9317      * colors or less.
9318      */
9319     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9320       {
9321         if (logging != MagickFalse)
9322            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9323                "    Quantizing the background color to 4-4-4");
9324
9325         tried_444 = MagickTrue;
9326
9327         LBR04PacketRGB(image->background_color);
9328
9329         if (logging != MagickFalse)
9330           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9331               "    Quantizing the pixel colors to 4-4-4");
9332
9333         if (image->colormap == NULL)
9334         {
9335           for (y=0; y < (ssize_t) image->rows; y++)
9336           {
9337             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9338
9339             if (r == (Quantum *) NULL)
9340               break;
9341
9342             for (x=0; x < (ssize_t) image->columns; x++)
9343             {
9344               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9345                   LBR04PixelRGB(r);
9346               r+=GetPixelChannels(image);
9347             }
9348
9349             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9350                break;
9351           }
9352         }
9353
9354         else /* Should not reach this; colormap already exists and
9355                 must be <= 256 */
9356         {
9357           if (logging != MagickFalse)
9358               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9359               "    Quantizing the colormap to 4-4-4");
9360
9361           for (i=0; i<image_colors; i++)
9362           {
9363             LBR04PacketRGB(image->colormap[i]);
9364           }
9365         }
9366         continue;
9367       }
9368
9369     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9370       {
9371         if (logging != MagickFalse)
9372            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9373                "    Quantizing the background color to 3-3-3");
9374
9375         tried_333 = MagickTrue;
9376
9377         LBR03PacketRGB(image->background_color);
9378
9379         if (logging != MagickFalse)
9380           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9381               "    Quantizing the pixel colors to 3-3-3-1");
9382
9383         if (image->colormap == NULL)
9384         {
9385           for (y=0; y < (ssize_t) image->rows; y++)
9386           {
9387             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9388
9389             if (r == (Quantum *) NULL)
9390               break;
9391
9392             for (x=0; x < (ssize_t) image->columns; x++)
9393             {
9394               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9395                   LBR03RGB(r);
9396               r+=GetPixelChannels(image);
9397             }
9398
9399             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9400                break;
9401           }
9402         }
9403
9404         else /* Should not reach this; colormap already exists and
9405                 must be <= 256 */
9406         {
9407           if (logging != MagickFalse)
9408               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9409               "    Quantizing the colormap to 3-3-3-1");
9410           for (i=0; i<image_colors; i++)
9411           {
9412               LBR03PacketRGB(image->colormap[i]);
9413           }
9414         }
9415         continue;
9416       }
9417
9418     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9419       {
9420         if (logging != MagickFalse)
9421            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9422                "    Quantizing the background color to 3-3-2");
9423
9424         tried_332 = MagickTrue;
9425
9426         /* Red and green were already done so we only quantize the blue
9427          * channel
9428          */
9429
9430         LBR02PacketBlue(image->background_color);
9431
9432         if (logging != MagickFalse)
9433           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9434               "    Quantizing the pixel colors to 3-3-2-1");
9435
9436         if (image->colormap == NULL)
9437         {
9438           for (y=0; y < (ssize_t) image->rows; y++)
9439           {
9440             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9441
9442             if (r == (Quantum *) NULL)
9443               break;
9444
9445             for (x=0; x < (ssize_t) image->columns; x++)
9446             {
9447               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9448                   LBR02PixelBlue(r);
9449               r+=GetPixelChannels(image);
9450             }
9451
9452             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9453                break;
9454           }
9455         }
9456
9457         else /* Should not reach this; colormap already exists and
9458                 must be <= 256 */
9459         {
9460           if (logging != MagickFalse)
9461               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9462               "    Quantizing the colormap to 3-3-2-1");
9463           for (i=0; i<image_colors; i++)
9464           {
9465               LBR02PacketBlue(image->colormap[i]);
9466           }
9467       }
9468       continue;
9469     }
9470
9471     if (image_colors == 0 || image_colors > 256)
9472     {
9473       /* Take care of special case with 256 opaque colors + 1 transparent
9474        * color.  We don't need to quantize to 2-3-2-1; we only need to
9475        * eliminate one color, so we'll merge the two darkest red
9476        * colors (0x49, 0, 0) -> (0x24, 0, 0).
9477        */
9478       if (logging != MagickFalse)
9479         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9480             "    Merging two dark red background colors to 3-3-2-1");
9481
9482       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9483           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9484           ScaleQuantumToChar(image->background_color.blue) == 0x00)
9485       {
9486          image->background_color.red=ScaleCharToQuantum(0x24);
9487       }
9488
9489       if (logging != MagickFalse)
9490         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9491             "    Merging two dark red pixel colors to 3-3-2-1");
9492
9493       if (image->colormap == NULL)
9494       {
9495         for (y=0; y < (ssize_t) image->rows; y++)
9496         {
9497           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9498
9499           if (r == (Quantum *) NULL)
9500             break;
9501
9502           for (x=0; x < (ssize_t) image->columns; x++)
9503           {
9504             if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9505                 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9506                 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9507                 GetPixelAlpha(image,r) == OpaqueAlpha)
9508               {
9509                 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9510               }
9511             r+=GetPixelChannels(image);
9512           }
9513
9514           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9515              break;
9516
9517         }
9518       }
9519
9520       else
9521       {
9522          for (i=0; i<image_colors; i++)
9523          {
9524             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9525                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9526                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9527             {
9528                image->colormap[i].red=ScaleCharToQuantum(0x24);
9529             }
9530          }
9531       }
9532     }
9533   }
9534   }
9535   /* END OF BUILD_PALETTE */
9536
9537   /* If we are excluding the tRNS chunk and there is transparency,
9538    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9539    * PNG.
9540    */
9541   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9542      (number_transparent != 0 || number_semitransparent != 0))
9543     {
9544       unsigned int colortype=mng_info->write_png_colortype;
9545
9546       if (ping_have_color == MagickFalse)
9547         mng_info->write_png_colortype = 5;
9548
9549       else
9550         mng_info->write_png_colortype = 7;
9551
9552       if (colortype != 0 &&
9553          mng_info->write_png_colortype != colortype)
9554         ping_need_colortype_warning=MagickTrue;
9555
9556     }
9557
9558   /* See if cheap transparency is possible.  It is only possible
9559    * when there is a single transparent color, no semitransparent
9560    * color, and no opaque color that has the same RGB components
9561    * as the transparent color.  We only need this information if
9562    * we are writing a PNG with colortype 0 or 2, and we have not
9563    * excluded the tRNS chunk.
9564    */
9565   if (number_transparent == 1 &&
9566       mng_info->write_png_colortype < 4)
9567     {
9568        ping_have_cheap_transparency = MagickTrue;
9569
9570        if (number_semitransparent != 0)
9571          ping_have_cheap_transparency = MagickFalse;
9572
9573        else if (image_colors == 0 || image_colors > 256 ||
9574            image->colormap == NULL)
9575          {
9576            register const Quantum
9577              *q;
9578
9579            for (y=0; y < (ssize_t) image->rows; y++)
9580            {
9581              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9582
9583              if (q == (Quantum *) NULL)
9584                break;
9585
9586              for (x=0; x < (ssize_t) image->columns; x++)
9587              {
9588                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9589                      (unsigned short) GetPixelRed(image,q) ==
9590                                      ping_trans_color.red &&
9591                      (unsigned short) GetPixelGreen(image,q) ==
9592                                      ping_trans_color.green &&
9593                      (unsigned short) GetPixelBlue(image,q) ==
9594                                      ping_trans_color.blue)
9595                    {
9596                      ping_have_cheap_transparency = MagickFalse;
9597                      break;
9598                    }
9599
9600                  q+=GetPixelChannels(image);
9601              }
9602
9603              if (ping_have_cheap_transparency == MagickFalse)
9604                 break;
9605            }
9606          }
9607        else
9608          {
9609             /* Assuming that image->colormap[0] is the one transparent color
9610              * and that all others are opaque.
9611              */
9612             if (image_colors > 1)
9613               for (i=1; i<image_colors; i++)
9614                 if (image->colormap[i].red == image->colormap[0].red &&
9615                     image->colormap[i].green == image->colormap[0].green &&
9616                     image->colormap[i].blue == image->colormap[0].blue)
9617                   {
9618                      ping_have_cheap_transparency = MagickFalse;
9619                      break;
9620                   }
9621          }
9622
9623        if (logging != MagickFalse)
9624          {
9625            if (ping_have_cheap_transparency == MagickFalse)
9626              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9627                  "   Cheap transparency is not possible.");
9628
9629            else
9630              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631                  "   Cheap transparency is possible.");
9632          }
9633      }
9634   else
9635     ping_have_cheap_transparency = MagickFalse;
9636
9637   image_depth=image->depth;
9638
9639   quantum_info = (QuantumInfo *) NULL;
9640   number_colors=0;
9641   image_colors=(int) image->colors;
9642   image_matte=image->alpha_trait !=
9643         UndefinedPixelTrait ? MagickTrue : MagickFalse;
9644
9645   if (mng_info->write_png_colortype < 5)
9646     mng_info->IsPalette=image->storage_class == PseudoClass &&
9647       image_colors <= 256 && image->colormap != NULL;
9648   else
9649     mng_info->IsPalette = MagickFalse;
9650
9651   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9652      (image->colors == 0 || image->colormap == NULL))
9653     {
9654       image_info=DestroyImageInfo(image_info);
9655       image=DestroyImage(image);
9656       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9657           "Cannot write PNG8 or color-type 3; colormap is NULL",
9658           "`%s'",IMimage->filename);
9659       return(MagickFalse);
9660     }
9661
9662   /*
9663     Allocate the PNG structures
9664   */
9665 #ifdef PNG_USER_MEM_SUPPORTED
9666  error_info.image=image;
9667  error_info.exception=exception;
9668   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9669     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9670     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9671
9672 #else
9673   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9674     MagickPNGErrorHandler,MagickPNGWarningHandler);
9675
9676 #endif
9677   if (ping == (png_struct *) NULL)
9678     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9679
9680   ping_info=png_create_info_struct(ping);
9681
9682   if (ping_info == (png_info *) NULL)
9683     {
9684       png_destroy_write_struct(&ping,(png_info **) NULL);
9685       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9686     }
9687
9688   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9689   pixel_info=(MemoryInfo *) NULL;
9690
9691   if (setjmp(png_jmpbuf(ping)))
9692     {
9693       /*
9694         PNG write failed.
9695       */
9696 #ifdef PNG_DEBUG
9697      if (image_info->verbose)
9698         (void) printf("PNG write has failed.\n");
9699 #endif
9700       png_destroy_write_struct(&ping,&ping_info);
9701 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9702       UnlockSemaphoreInfo(ping_semaphore);
9703 #endif
9704
9705       if (pixel_info != (MemoryInfo *) NULL)
9706         pixel_info=RelinquishVirtualMemory(pixel_info);
9707
9708       if (quantum_info != (QuantumInfo *) NULL)
9709         quantum_info=DestroyQuantumInfo(quantum_info);
9710
9711       if (ping_have_blob != MagickFalse)
9712           (void) CloseBlob(image);
9713       image_info=DestroyImageInfo(image_info);
9714       image=DestroyImage(image);
9715       return(MagickFalse);
9716     }
9717
9718   /* {  For navigation to end of SETJMP-protected block.  Within this
9719    *    block, use png_error() instead of Throwing an Exception, to ensure
9720    *    that libpng is able to clean up, and that the semaphore is unlocked.
9721    */
9722
9723 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9724   LockSemaphoreInfo(ping_semaphore);
9725 #endif
9726
9727 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9728   /* Allow benign errors */
9729   png_set_benign_errors(ping, 1);
9730 #endif
9731
9732 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9733   /* Reject images with too many rows or columns */
9734   png_set_user_limits(ping,
9735     (png_uint_32) MagickMin(0x7fffffffL,
9736         GetMagickResourceLimit(WidthResource)),
9737     (png_uint_32) MagickMin(0x7fffffffL,
9738         GetMagickResourceLimit(HeightResource)));
9739 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9740
9741   /*
9742     Prepare PNG for writing.
9743   */
9744
9745 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9746   if (mng_info->write_mng)
9747   {
9748      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9749 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9750      /* Disable new libpng-1.5.10 feature when writing a MNG because
9751       * zero-length PLTE is OK
9752       */
9753      png_set_check_for_invalid_index (ping, 0);
9754 # endif
9755   }
9756
9757 #else
9758 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9759   if (mng_info->write_mng)
9760      png_permit_empty_plte(ping,MagickTrue);
9761
9762 # endif
9763 #endif
9764
9765   x=0;
9766
9767   ping_width=(png_uint_32) image->columns;
9768   ping_height=(png_uint_32) image->rows;
9769
9770   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9771      image_depth=8;
9772
9773   if (mng_info->write_png48 || mng_info->write_png64)
9774      image_depth=16;
9775
9776   if (mng_info->write_png_depth != 0)
9777      image_depth=mng_info->write_png_depth;
9778
9779   /* Adjust requested depth to next higher valid depth if necessary */
9780   if (image_depth > 8)
9781      image_depth=16;
9782
9783   if ((image_depth > 4) && (image_depth < 8))
9784      image_depth=8;
9785
9786   if (image_depth == 3)
9787      image_depth=4;
9788
9789   if (logging != MagickFalse)
9790     {
9791      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9792         "    width=%.20g",(double) ping_width);
9793      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9794         "    height=%.20g",(double) ping_height);
9795      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9796         "    image_matte=%.20g",(double) image->alpha_trait);
9797      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9798         "    image->depth=%.20g",(double) image->depth);
9799      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9800         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
9801     }
9802
9803   save_image_depth=image_depth;
9804   ping_bit_depth=(png_byte) save_image_depth;
9805
9806
9807 #if defined(PNG_pHYs_SUPPORTED)
9808   if (ping_exclude_pHYs == MagickFalse)
9809   {
9810   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9811       (!mng_info->write_mng || !mng_info->equal_physs))
9812     {
9813       if (logging != MagickFalse)
9814         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9815             "    Setting up pHYs chunk");
9816
9817       if (image->units == PixelsPerInchResolution)
9818         {
9819           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9820           ping_pHYs_x_resolution=
9821              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9822           ping_pHYs_y_resolution=
9823              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9824         }
9825
9826       else if (image->units == PixelsPerCentimeterResolution)
9827         {
9828           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9829           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9830           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9831         }
9832
9833       else
9834         {
9835           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9836           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9837           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9838         }
9839
9840       if (logging != MagickFalse)
9841         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9842           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9843           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9844           (int) ping_pHYs_unit_type);
9845        ping_have_pHYs = MagickTrue;
9846     }
9847   }
9848 #endif
9849
9850   if (ping_exclude_bKGD == MagickFalse)
9851   {
9852   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9853     {
9854        unsigned int
9855          mask;
9856
9857        mask=0xffff;
9858        if (ping_bit_depth == 8)
9859           mask=0x00ff;
9860
9861        if (ping_bit_depth == 4)
9862           mask=0x000f;
9863
9864        if (ping_bit_depth == 2)
9865           mask=0x0003;
9866
9867        if (ping_bit_depth == 1)
9868           mask=0x0001;
9869
9870        ping_background.red=(png_uint_16)
9871          (ScaleQuantumToShort(image->background_color.red) & mask);
9872
9873        ping_background.green=(png_uint_16)
9874          (ScaleQuantumToShort(image->background_color.green) & mask);
9875
9876        ping_background.blue=(png_uint_16)
9877          (ScaleQuantumToShort(image->background_color.blue) & mask);
9878
9879        ping_background.gray=(png_uint_16) ping_background.green;
9880     }
9881
9882   if (logging != MagickFalse)
9883     {
9884       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9885           "    Setting up bKGD chunk (1)");
9886       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9887           "      background_color index is %d",
9888           (int) ping_background.index);
9889
9890       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9891           "    ping_bit_depth=%d",ping_bit_depth);
9892     }
9893
9894   ping_have_bKGD = MagickTrue;
9895   }
9896
9897   /*
9898     Select the color type.
9899   */
9900   matte=image_matte;
9901   old_bit_depth=0;
9902
9903   if (mng_info->IsPalette && mng_info->write_png8)
9904     {
9905       /* To do: make this a function cause it's used twice, except
9906          for reducing the sample depth from 8. */
9907
9908       number_colors=image_colors;
9909
9910       ping_have_tRNS=MagickFalse;
9911
9912       /*
9913         Set image palette.
9914       */
9915       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9916
9917       if (logging != MagickFalse)
9918         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9919             "  Setting up PLTE chunk with %d colors (%d)",
9920             number_colors, image_colors);
9921
9922       for (i=0; i < (ssize_t) number_colors; i++)
9923       {
9924         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9925         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9926         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9927         if (logging != MagickFalse)
9928           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9929 #if MAGICKCORE_QUANTUM_DEPTH == 8
9930             "    %3ld (%3d,%3d,%3d)",
9931 #else
9932             "    %5ld (%5d,%5d,%5d)",
9933 #endif
9934             (long) i,palette[i].red,palette[i].green,palette[i].blue);
9935
9936       }
9937
9938       ping_have_PLTE=MagickTrue;
9939       image_depth=ping_bit_depth;
9940       ping_num_trans=0;
9941
9942       if (matte != MagickFalse)
9943       {
9944           /*
9945             Identify which colormap entry is transparent.
9946           */
9947           assert(number_colors <= 256);
9948           assert(image->colormap != NULL);
9949
9950           for (i=0; i < (ssize_t) number_transparent; i++)
9951              ping_trans_alpha[i]=0;
9952
9953
9954           ping_num_trans=(unsigned short) (number_transparent +
9955              number_semitransparent);
9956
9957           if (ping_num_trans == 0)
9958              ping_have_tRNS=MagickFalse;
9959
9960           else
9961              ping_have_tRNS=MagickTrue;
9962       }
9963
9964       if (ping_exclude_bKGD == MagickFalse)
9965       {
9966        /*
9967         * Identify which colormap entry is the background color.
9968         */
9969
9970         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9971           if (IsPNGColorEqual(ping_background,image->colormap[i]))
9972             break;
9973
9974         ping_background.index=(png_byte) i;
9975
9976         if (logging != MagickFalse)
9977           {
9978             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9979                  "      background_color index is %d",
9980                  (int) ping_background.index);
9981           }
9982       }
9983     } /* end of write_png8 */
9984
9985   else if (mng_info->write_png_colortype == 1)
9986     {
9987       image_matte=MagickFalse;
9988       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9989     }
9990
9991   else if (mng_info->write_png24 || mng_info->write_png48 ||
9992       mng_info->write_png_colortype == 3)
9993     {
9994       image_matte=MagickFalse;
9995       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9996     }
9997
9998   else if (mng_info->write_png32 || mng_info->write_png64 ||
9999       mng_info->write_png_colortype == 7)
10000     {
10001       image_matte=MagickTrue;
10002       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10003     }
10004
10005   else /* mng_info->write_pngNN not specified */
10006     {
10007       image_depth=ping_bit_depth;
10008
10009       if (mng_info->write_png_colortype != 0)
10010         {
10011           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
10012
10013           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10014               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10015             image_matte=MagickTrue;
10016
10017           else
10018             image_matte=MagickFalse;
10019
10020           if (logging != MagickFalse)
10021              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10022              "   PNG colortype %d was specified:",(int) ping_color_type);
10023         }
10024
10025       else /* write_png_colortype not specified */
10026         {
10027           if (logging != MagickFalse)
10028              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10029              "  Selecting PNG colortype:");
10030
10031           ping_color_type=(png_byte) ((matte != MagickFalse)?
10032             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
10033
10034           if (image_info->type == TrueColorType)
10035             {
10036               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10037               image_matte=MagickFalse;
10038             }
10039
10040           if (image_info->type == TrueColorAlphaType)
10041             {
10042               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10043               image_matte=MagickTrue;
10044             }
10045
10046           if (image_info->type == PaletteType ||
10047               image_info->type == PaletteAlphaType)
10048             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10049
10050           if (mng_info->write_png_colortype == 0 &&
10051              image_info->type == UndefinedType)
10052             {
10053               if (ping_have_color == MagickFalse)
10054                 {
10055                   if (image_matte == MagickFalse)
10056                     {
10057                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10058                       image_matte=MagickFalse;
10059                     }
10060
10061                   else
10062                     {
10063                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
10064                       image_matte=MagickTrue;
10065                     }
10066                 }
10067               else
10068                 {
10069                   if (image_matte == MagickFalse)
10070                     {
10071                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10072                       image_matte=MagickFalse;
10073                     }
10074
10075                   else
10076                     {
10077                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
10078                       image_matte=MagickTrue;
10079                     }
10080                  }
10081             }
10082
10083         }
10084
10085       if (logging != MagickFalse)
10086          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10087          "    Selected PNG colortype=%d",ping_color_type);
10088
10089       if (ping_bit_depth < 8)
10090         {
10091           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10092               ping_color_type == PNG_COLOR_TYPE_RGB ||
10093               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10094             ping_bit_depth=8;
10095         }
10096
10097       old_bit_depth=ping_bit_depth;
10098
10099       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10100         {
10101           if (image->alpha_trait == UndefinedPixelTrait &&
10102                ping_have_non_bw == MagickFalse)
10103              ping_bit_depth=1;
10104         }
10105
10106       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
10107         {
10108            size_t one = 1;
10109            ping_bit_depth=1;
10110
10111            if (image->colors == 0)
10112            {
10113               /* DO SOMETHING */
10114                 png_error(ping,"image has 0 colors");
10115            }
10116
10117            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
10118              ping_bit_depth <<= 1;
10119         }
10120
10121       if (logging != MagickFalse)
10122          {
10123            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10124             "    Number of colors: %.20g",(double) image_colors);
10125
10126            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10127             "    Tentative PNG bit depth: %d",ping_bit_depth);
10128          }
10129
10130       if (ping_bit_depth < (int) mng_info->write_png_depth)
10131          ping_bit_depth = mng_info->write_png_depth;
10132     }
10133
10134   image_depth=ping_bit_depth;
10135
10136   if (logging != MagickFalse)
10137     {
10138       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10139         "    Tentative PNG color type: %s (%.20g)",
10140         PngColorTypeToString(ping_color_type),
10141         (double) ping_color_type);
10142
10143       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10144         "    image_info->type: %.20g",(double) image_info->type);
10145
10146       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10147         "    image_depth: %.20g",(double) image_depth);
10148
10149       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10150
10151         "    image->depth: %.20g",(double) image->depth);
10152
10153       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10154         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
10155     }
10156
10157   if (matte != MagickFalse)
10158     {
10159       if (mng_info->IsPalette)
10160         {
10161           if (mng_info->write_png_colortype == 0)
10162             {
10163               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10164
10165               if (ping_have_color != MagickFalse)
10166                  ping_color_type=PNG_COLOR_TYPE_RGBA;
10167             }
10168
10169           /*
10170            * Determine if there is any transparent color.
10171           */
10172           if (number_transparent + number_semitransparent == 0)
10173             {
10174               /*
10175                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
10176               */
10177
10178               image_matte=MagickFalse;
10179
10180               if (mng_info->write_png_colortype == 0)
10181                 ping_color_type&=0x03;
10182             }
10183
10184           else
10185             {
10186               unsigned int
10187                 mask;
10188
10189               mask=0xffff;
10190
10191               if (ping_bit_depth == 8)
10192                  mask=0x00ff;
10193
10194               if (ping_bit_depth == 4)
10195                  mask=0x000f;
10196
10197               if (ping_bit_depth == 2)
10198                  mask=0x0003;
10199
10200               if (ping_bit_depth == 1)
10201                  mask=0x0001;
10202
10203               ping_trans_color.red=(png_uint_16)
10204                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10205
10206               ping_trans_color.green=(png_uint_16)
10207                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10208
10209               ping_trans_color.blue=(png_uint_16)
10210                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10211
10212               ping_trans_color.gray=(png_uint_16)
10213                 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
10214                    image->colormap)) & mask);
10215
10216               ping_trans_color.index=(png_byte) 0;
10217
10218               ping_have_tRNS=MagickTrue;
10219             }
10220
10221           if (ping_have_tRNS != MagickFalse)
10222             {
10223               /*
10224                * Determine if there is one and only one transparent color
10225                * and if so if it is fully transparent.
10226                */
10227               if (ping_have_cheap_transparency == MagickFalse)
10228                 ping_have_tRNS=MagickFalse;
10229             }
10230
10231           if (ping_have_tRNS != MagickFalse)
10232             {
10233               if (mng_info->write_png_colortype == 0)
10234                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
10235
10236               if (image_depth == 8)
10237                 {
10238                   ping_trans_color.red&=0xff;
10239                   ping_trans_color.green&=0xff;
10240                   ping_trans_color.blue&=0xff;
10241                   ping_trans_color.gray&=0xff;
10242                 }
10243             }
10244         }
10245       else
10246         {
10247           if (image_depth == 8)
10248             {
10249               ping_trans_color.red&=0xff;
10250               ping_trans_color.green&=0xff;
10251               ping_trans_color.blue&=0xff;
10252               ping_trans_color.gray&=0xff;
10253             }
10254         }
10255     }
10256
10257     matte=image_matte;
10258
10259     if (ping_have_tRNS != MagickFalse)
10260       image_matte=MagickFalse;
10261
10262     if ((mng_info->IsPalette) &&
10263         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10264         ping_have_color == MagickFalse &&
10265         (image_matte == MagickFalse || image_depth >= 8))
10266       {
10267         size_t one=1;
10268
10269         if (image_matte != MagickFalse)
10270           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10271
10272         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10273           {
10274             ping_color_type=PNG_COLOR_TYPE_GRAY;
10275
10276             if (save_image_depth == 16 && image_depth == 8)
10277               {
10278                 if (logging != MagickFalse)
10279                   {
10280                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10281                         "  Scaling ping_trans_color (0)");
10282                   }
10283                     ping_trans_color.gray*=0x0101;
10284               }
10285           }
10286
10287         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10288           image_depth=MAGICKCORE_QUANTUM_DEPTH;
10289
10290         if ((image_colors == 0) ||
10291              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10292           image_colors=(int) (one << image_depth);
10293
10294         if (image_depth > 8)
10295           ping_bit_depth=16;
10296
10297         else
10298           {
10299             ping_bit_depth=8;
10300             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10301               {
10302                 if(!mng_info->write_png_depth)
10303                   {
10304                     ping_bit_depth=1;
10305
10306                     while ((int) (one << ping_bit_depth)
10307                         < (ssize_t) image_colors)
10308                       ping_bit_depth <<= 1;
10309                   }
10310               }
10311
10312             else if (ping_color_type ==
10313                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10314                 mng_info->IsPalette)
10315               {
10316               /* Check if grayscale is reducible */
10317
10318                 int
10319                   depth_4_ok=MagickTrue,
10320                   depth_2_ok=MagickTrue,
10321                   depth_1_ok=MagickTrue;
10322
10323                 for (i=0; i < (ssize_t) image_colors; i++)
10324                 {
10325                    unsigned char
10326                      intensity;
10327
10328                    intensity=ScaleQuantumToChar(image->colormap[i].red);
10329
10330                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10331                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10332                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10333                      depth_2_ok=depth_1_ok=MagickFalse;
10334                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10335                      depth_1_ok=MagickFalse;
10336                 }
10337
10338                 if (depth_1_ok && mng_info->write_png_depth <= 1)
10339                   ping_bit_depth=1;
10340
10341                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10342                   ping_bit_depth=2;
10343
10344                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10345                   ping_bit_depth=4;
10346               }
10347           }
10348
10349           image_depth=ping_bit_depth;
10350       }
10351
10352     else
10353
10354       if (mng_info->IsPalette)
10355       {
10356         number_colors=image_colors;
10357
10358         if (image_depth <= 8)
10359           {
10360             /*
10361               Set image palette.
10362             */
10363             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10364
10365             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10366               {
10367                 for (i=0; i < (ssize_t) number_colors; i++)
10368                 {
10369                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10370                   palette[i].green=
10371                     ScaleQuantumToChar(image->colormap[i].green);
10372                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10373                 }
10374
10375                 if (logging != MagickFalse)
10376                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10377                     "  Setting up PLTE chunk with %d colors",
10378                     number_colors);
10379
10380                 ping_have_PLTE=MagickTrue;
10381               }
10382
10383             /* color_type is PNG_COLOR_TYPE_PALETTE */
10384             if (mng_info->write_png_depth == 0)
10385               {
10386                 size_t
10387                   one;
10388
10389                 ping_bit_depth=1;
10390                 one=1;
10391
10392                 while ((one << ping_bit_depth) < (size_t) number_colors)
10393                   ping_bit_depth <<= 1;
10394               }
10395
10396             ping_num_trans=0;
10397
10398             if (matte != MagickFalse)
10399               {
10400                 /*
10401                  * Set up trans_colors array.
10402                  */
10403                 assert(number_colors <= 256);
10404
10405                 ping_num_trans=(unsigned short) (number_transparent +
10406                   number_semitransparent);
10407
10408                 if (ping_num_trans == 0)
10409                   ping_have_tRNS=MagickFalse;
10410
10411                 else
10412                   {
10413                     if (logging != MagickFalse)
10414                       {
10415                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10416                           "  Scaling ping_trans_color (1)");
10417                       }
10418                     ping_have_tRNS=MagickTrue;
10419
10420                     for (i=0; i < ping_num_trans; i++)
10421                     {
10422                        ping_trans_alpha[i]= (png_byte)
10423                          ScaleQuantumToChar(image->colormap[i].alpha);
10424                     }
10425                   }
10426               }
10427           }
10428       }
10429
10430     else
10431       {
10432
10433         if (image_depth < 8)
10434           image_depth=8;
10435
10436         if ((save_image_depth == 16) && (image_depth == 8))
10437           {
10438             if (logging != MagickFalse)
10439               {
10440                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10441                   "    Scaling ping_trans_color from (%d,%d,%d)",
10442                   (int) ping_trans_color.red,
10443                   (int) ping_trans_color.green,
10444                   (int) ping_trans_color.blue);
10445               }
10446
10447             ping_trans_color.red*=0x0101;
10448             ping_trans_color.green*=0x0101;
10449             ping_trans_color.blue*=0x0101;
10450             ping_trans_color.gray*=0x0101;
10451
10452             if (logging != MagickFalse)
10453               {
10454                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10455                   "    to (%d,%d,%d)",
10456                   (int) ping_trans_color.red,
10457                   (int) ping_trans_color.green,
10458                   (int) ping_trans_color.blue);
10459               }
10460           }
10461       }
10462
10463     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
10464          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
10465
10466     /*
10467       Adjust background and transparency samples in sub-8-bit grayscale files.
10468     */
10469     if (ping_bit_depth < 8 && ping_color_type ==
10470         PNG_COLOR_TYPE_GRAY)
10471       {
10472          png_uint_16
10473            maxval;
10474
10475          size_t
10476            one=1;
10477
10478          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10479
10480          if (ping_exclude_bKGD == MagickFalse)
10481          {
10482
10483          ping_background.gray=(png_uint_16) ((maxval/65535.)*
10484            (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10485            &image->background_color))) +.5)));
10486
10487          if (logging != MagickFalse)
10488            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10489              "  Setting up bKGD chunk (2)");
10490          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10491              "      background_color index is %d",
10492              (int) ping_background.index);
10493
10494          ping_have_bKGD = MagickTrue;
10495          }
10496
10497          if (logging != MagickFalse)
10498            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10499              "  Scaling ping_trans_color.gray from %d",
10500              (int)ping_trans_color.gray);
10501
10502          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10503            ping_trans_color.gray)+.5);
10504
10505          if (logging != MagickFalse)
10506            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10507              "      to %d", (int)ping_trans_color.gray);
10508       }
10509
10510   if (ping_exclude_bKGD == MagickFalse)
10511   {
10512     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10513       {
10514         /*
10515            Identify which colormap entry is the background color.
10516         */
10517
10518         number_colors=image_colors;
10519
10520         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10521           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10522             break;
10523
10524         ping_background.index=(png_byte) i;
10525
10526         if (logging != MagickFalse)
10527           {
10528             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10529               "  Setting up bKGD chunk with index=%d",(int) i);
10530           }
10531
10532         if (i < (ssize_t) number_colors)
10533           {
10534             ping_have_bKGD = MagickTrue;
10535
10536             if (logging != MagickFalse)
10537               {
10538                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10539                   "     background   =(%d,%d,%d)",
10540                         (int) ping_background.red,
10541                         (int) ping_background.green,
10542                         (int) ping_background.blue);
10543               }
10544           }
10545
10546         else  /* Can't happen */
10547           {
10548             if (logging != MagickFalse)
10549               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10550                   "      No room in PLTE to add bKGD color");
10551             ping_have_bKGD = MagickFalse;
10552           }
10553       }
10554   }
10555
10556   if (logging != MagickFalse)
10557     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10558       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10559       ping_color_type);
10560   /*
10561     Initialize compression level and filtering.
10562   */
10563   if (logging != MagickFalse)
10564     {
10565       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10566         "  Setting up deflate compression");
10567
10568       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10569         "    Compression buffer size: 32768");
10570     }
10571
10572   png_set_compression_buffer_size(ping,32768L);
10573
10574   if (logging != MagickFalse)
10575     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10576       "    Compression mem level: 9");
10577
10578   png_set_compression_mem_level(ping, 9);
10579
10580   /* Untangle the "-quality" setting:
10581
10582      Undefined is 0; the default is used.
10583      Default is 75
10584
10585      10's digit:
10586
10587         0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10588            zlib default compression level
10589
10590         1-9: the zlib compression level
10591
10592      1's digit:
10593
10594         0-4: the PNG filter method
10595
10596         5:   libpng adaptive filtering if compression level > 5
10597              libpng filter type "none" if compression level <= 5
10598                 or if image is grayscale or palette
10599
10600         6:   libpng adaptive filtering
10601
10602         7:   "LOCO" filtering (intrapixel differing) if writing
10603              a MNG, otherwise "none".  Did not work in IM-6.7.0-9
10604              and earlier because of a missing "else".
10605
10606         8:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10607              filtering. Unused prior to IM-6.7.0-10, was same as 6
10608
10609         9:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10610              Unused prior to IM-6.7.0-10, was same as 6
10611
10612     Note that using the -quality option, not all combinations of
10613     PNG filter type, zlib compression level, and zlib compression
10614     strategy are possible.  This will be addressed soon in a
10615     release that accomodates "-define png:compression-strategy", etc.
10616
10617    */
10618
10619   quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10620      image_info->quality;
10621
10622   if (quality <= 9)
10623     {
10624       if (mng_info->write_png_compression_strategy == 0)
10625         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10626     }
10627
10628   else if (mng_info->write_png_compression_level == 0)
10629     {
10630       int
10631         level;
10632
10633       level=(int) MagickMin((ssize_t) quality/10,9);
10634
10635       mng_info->write_png_compression_level = level+1;
10636     }
10637
10638   if (mng_info->write_png_compression_strategy == 0)
10639     {
10640         if ((quality %10) == 8 || (quality %10) == 9)
10641 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10642           mng_info->write_png_compression_strategy=Z_RLE+1;
10643 #else
10644           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10645 #endif
10646     }
10647
10648   if (mng_info->write_png_compression_filter == 0)
10649         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10650
10651   if (logging != MagickFalse)
10652     {
10653         if (mng_info->write_png_compression_level)
10654           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10655             "    Compression level:    %d",
10656             (int) mng_info->write_png_compression_level-1);
10657
10658         if (mng_info->write_png_compression_strategy)
10659           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10660             "    Compression strategy: %d",
10661             (int) mng_info->write_png_compression_strategy-1);
10662
10663         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10664           "  Setting up filtering");
10665
10666         if (mng_info->write_png_compression_filter == 6)
10667           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10668             "    Base filter method: ADAPTIVE");
10669         else if (mng_info->write_png_compression_filter == 0 ||
10670                  mng_info->write_png_compression_filter == 1)
10671           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10672             "    Base filter method: NONE");
10673         else
10674           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10675             "    Base filter method: %d",
10676             (int) mng_info->write_png_compression_filter-1);
10677     }
10678
10679   if (mng_info->write_png_compression_level != 0)
10680     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10681
10682   if (mng_info->write_png_compression_filter == 6)
10683     {
10684       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10685          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10686          (quality < 50))
10687         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10688       else
10689         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10690      }
10691   else if (mng_info->write_png_compression_filter == 7 ||
10692       mng_info->write_png_compression_filter == 10)
10693     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10694
10695   else if (mng_info->write_png_compression_filter == 8)
10696     {
10697 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10698       if (mng_info->write_mng)
10699       {
10700          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10701              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10702         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10703       }
10704 #endif
10705       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10706     }
10707
10708   else if (mng_info->write_png_compression_filter == 9)
10709     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10710
10711   else if (mng_info->write_png_compression_filter != 0)
10712     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10713        mng_info->write_png_compression_filter-1);
10714
10715   if (mng_info->write_png_compression_strategy != 0)
10716     png_set_compression_strategy(ping,
10717        mng_info->write_png_compression_strategy-1);
10718
10719   ping_interlace_method=image_info->interlace != NoInterlace;
10720
10721   if (mng_info->write_mng)
10722     png_set_sig_bytes(ping,8);
10723
10724   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10725
10726   if (mng_info->write_png_colortype != 0)
10727     {
10728      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10729        if (ping_have_color != MagickFalse)
10730          {
10731            ping_color_type = PNG_COLOR_TYPE_RGB;
10732
10733            if (ping_bit_depth < 8)
10734              ping_bit_depth=8;
10735          }
10736
10737      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10738        if (ping_have_color != MagickFalse)
10739          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10740     }
10741
10742   if (ping_need_colortype_warning != MagickFalse ||
10743      ((mng_info->write_png_depth &&
10744      (int) mng_info->write_png_depth != ping_bit_depth) ||
10745      (mng_info->write_png_colortype &&
10746      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10747       mng_info->write_png_colortype != 7 &&
10748       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10749     {
10750       if (logging != MagickFalse)
10751         {
10752           if (ping_need_colortype_warning != MagickFalse)
10753             {
10754               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10755                  "  Image has transparency but tRNS chunk was excluded");
10756             }
10757
10758           if (mng_info->write_png_depth)
10759             {
10760               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10761                   "  Defined png:bit-depth=%u, Computed depth=%u",
10762                   mng_info->write_png_depth,
10763                   ping_bit_depth);
10764             }
10765
10766           if (mng_info->write_png_colortype)
10767             {
10768               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10769                   "  Defined png:color-type=%u, Computed color type=%u",
10770                   mng_info->write_png_colortype-1,
10771                   ping_color_type);
10772             }
10773         }
10774
10775       png_warning(ping,
10776         "Cannot write image with defined png:bit-depth or png:color-type.");
10777     }
10778
10779   if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10780     {
10781       /* Add an opaque matte channel */
10782       image->alpha_trait = BlendPixelTrait;
10783       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10784
10785       if (logging != MagickFalse)
10786         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10787           "  Added an opaque matte channel");
10788     }
10789
10790   if (number_transparent != 0 || number_semitransparent != 0)
10791     {
10792       if (ping_color_type < 4)
10793         {
10794            ping_have_tRNS=MagickTrue;
10795            if (logging != MagickFalse)
10796              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10797                "  Setting ping_have_tRNS=MagickTrue.");
10798         }
10799     }
10800
10801   if (logging != MagickFalse)
10802     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10803       "  Writing PNG header chunks");
10804
10805   png_set_IHDR(ping,ping_info,ping_width,ping_height,
10806                ping_bit_depth,ping_color_type,
10807                ping_interlace_method,ping_compression_method,
10808                ping_filter_method);
10809
10810   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10811     {
10812       png_set_PLTE(ping,ping_info,palette,number_colors);
10813
10814       if (logging != MagickFalse)
10815         {
10816           for (i=0; i< (ssize_t) number_colors; i++)
10817           {
10818             if (i < ping_num_trans)
10819               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10820                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10821                       (int) i,
10822                       (int) palette[i].red,
10823                       (int) palette[i].green,
10824                       (int) palette[i].blue,
10825                       (int) i,
10826                       (int) ping_trans_alpha[i]);
10827              else
10828               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10829                 "     PLTE[%d] = (%d,%d,%d)",
10830                       (int) i,
10831                       (int) palette[i].red,
10832                       (int) palette[i].green,
10833                       (int) palette[i].blue);
10834            }
10835          }
10836     }
10837
10838   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10839   if (ping_exclude_sRGB != MagickFalse ||
10840      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10841   {
10842     if ((ping_exclude_tEXt == MagickFalse ||
10843        ping_exclude_zTXt == MagickFalse) &&
10844        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10845     {
10846       ResetImageProfileIterator(image);
10847       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10848       {
10849         profile=GetImageProfile(image,name);
10850
10851         if (profile != (StringInfo *) NULL)
10852           {
10853 #ifdef PNG_WRITE_iCCP_SUPPORTED
10854             if ((LocaleCompare(name,"ICC") == 0) ||
10855                 (LocaleCompare(name,"ICM") == 0))
10856               {
10857                 ping_have_iCCP = MagickTrue;
10858                 if (ping_exclude_iCCP == MagickFalse)
10859                   {
10860                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10861                         "  Setting up iCCP chunk");
10862
10863                     png_set_iCCP(ping,ping_info,(png_charp) name,0,
10864 #if (PNG_LIBPNG_VER < 10500)
10865                     (png_charp) GetStringInfoDatum(profile),
10866 #else
10867                     (const png_byte *) GetStringInfoDatum(profile),
10868 #endif
10869                     (png_uint_32) GetStringInfoLength(profile));
10870                   }
10871                 else
10872                   {
10873                     /* Do not write hex-encoded ICC chunk */
10874                        name=GetNextImageProfile(image);
10875                        continue;
10876                   }
10877               }
10878 #endif /* WRITE_iCCP */
10879
10880             if (LocaleCompare(name,"exif") == 0)
10881               {
10882                    /* Do not write hex-encoded ICC chunk; we will
10883                       write it later as an eXIf chunk */
10884                    name=GetNextImageProfile(image);
10885                    continue;
10886               }
10887
10888               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10889                  "  Setting up zTXt chunk with uuencoded %s profile",
10890                  name);
10891               Magick_png_write_raw_profile(image_info,ping,ping_info,
10892                 (unsigned char *) name,(unsigned char *) name,
10893                 GetStringInfoDatum(profile),
10894                 (png_uint_32) GetStringInfoLength(profile));
10895           }
10896         name=GetNextImageProfile(image);
10897       }
10898     }
10899   }
10900
10901 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10902   if ((mng_info->have_write_global_srgb == 0) &&
10903       ping_have_iCCP != MagickTrue &&
10904       (ping_have_sRGB != MagickFalse ||
10905       png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10906     {
10907       if (ping_exclude_sRGB == MagickFalse)
10908         {
10909           /*
10910             Note image rendering intent.
10911           */
10912           if (logging != MagickFalse)
10913             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10914                 "  Setting up sRGB chunk");
10915
10916           (void) png_set_sRGB(ping,ping_info,(
10917             Magick_RenderingIntent_to_PNG_RenderingIntent(
10918               image->rendering_intent)));
10919
10920           ping_have_sRGB = MagickTrue;
10921         }
10922     }
10923
10924   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10925 #endif
10926     {
10927       if (ping_exclude_gAMA == MagickFalse &&
10928           ping_have_iCCP == MagickFalse &&
10929           ping_have_sRGB == MagickFalse &&
10930           (ping_exclude_sRGB == MagickFalse ||
10931           (image->gamma < .45 || image->gamma > .46)))
10932       {
10933       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10934         {
10935           /*
10936             Note image gamma.
10937             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10938           */
10939           if (logging != MagickFalse)
10940             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10941               "  Setting up gAMA chunk");
10942
10943           png_set_gAMA(ping,ping_info,image->gamma);
10944         }
10945       }
10946
10947       if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10948         {
10949           if ((mng_info->have_write_global_chrm == 0) &&
10950               (image->chromaticity.red_primary.x != 0.0))
10951             {
10952               /*
10953                 Note image chromaticity.
10954                 Note: if cHRM+gAMA == sRGB write sRGB instead.
10955               */
10956                PrimaryInfo
10957                  bp,
10958                  gp,
10959                  rp,
10960                  wp;
10961
10962                wp=image->chromaticity.white_point;
10963                rp=image->chromaticity.red_primary;
10964                gp=image->chromaticity.green_primary;
10965                bp=image->chromaticity.blue_primary;
10966
10967                if (logging != MagickFalse)
10968                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10969                    "  Setting up cHRM chunk");
10970
10971                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10972                    bp.x,bp.y);
10973            }
10974         }
10975     }
10976
10977   if (ping_exclude_bKGD == MagickFalse)
10978     {
10979       if (ping_have_bKGD != MagickFalse)
10980         {
10981           png_set_bKGD(ping,ping_info,&ping_background);
10982           if (logging != MagickFalse)
10983             {
10984               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10985                    "    Setting up bKGD chunk");
10986               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10987                    "      background color = (%d,%d,%d)",
10988                         (int) ping_background.red,
10989                         (int) ping_background.green,
10990                         (int) ping_background.blue);
10991               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10992                    "      index = %d, gray=%d",
10993                         (int) ping_background.index,
10994                         (int) ping_background.gray);
10995             }
10996          }
10997     }
10998
10999   if (ping_exclude_pHYs == MagickFalse)
11000     {
11001       if (ping_have_pHYs != MagickFalse)
11002         {
11003           png_set_pHYs(ping,ping_info,
11004              ping_pHYs_x_resolution,
11005              ping_pHYs_y_resolution,
11006              ping_pHYs_unit_type);
11007
11008           if (logging != MagickFalse)
11009             {
11010               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11011                    "    Setting up pHYs chunk");
11012               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11013                    "      x_resolution=%lu",
11014                    (unsigned long) ping_pHYs_x_resolution);
11015               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11016                    "      y_resolution=%lu",
11017                    (unsigned long) ping_pHYs_y_resolution);
11018               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11019                    "      unit_type=%lu",
11020                    (unsigned long) ping_pHYs_unit_type);
11021             }
11022         }
11023     }
11024
11025 #if defined(PNG_tIME_SUPPORTED)
11026   if (ping_exclude_tIME == MagickFalse)
11027     {
11028       const char
11029         *timestamp;
11030
11031       if (image->taint == MagickFalse)
11032         {
11033           timestamp=GetImageOption(image_info,"png:tIME");
11034
11035           if (timestamp == (const char *) NULL)
11036             timestamp=GetImageProperty(image,"png:tIME",exception);
11037         }
11038
11039       else
11040         {
11041           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11042              "  Reset tIME in tainted image");
11043
11044           timestamp=GetImageProperty(image,"date:modify",exception);
11045         }
11046
11047       if (timestamp != (const char *) NULL)
11048           write_tIME_chunk(image,ping,ping_info,timestamp,exception);
11049     }
11050 #endif
11051
11052   if (mng_info->need_blob != MagickFalse)
11053   {
11054     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
11055        MagickFalse)
11056        png_error(ping,"WriteBlob Failed");
11057
11058      ping_have_blob=MagickTrue;
11059   }
11060
11061   png_write_info_before_PLTE(ping, ping_info);
11062
11063   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
11064     {
11065       if (logging != MagickFalse)
11066         {
11067           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11068               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
11069         }
11070
11071       if (ping_color_type == 3)
11072          (void) png_set_tRNS(ping, ping_info,
11073                 ping_trans_alpha,
11074                 ping_num_trans,
11075                 NULL);
11076
11077       else
11078         {
11079            (void) png_set_tRNS(ping, ping_info,
11080                   NULL,
11081                   0,
11082                   &ping_trans_color);
11083
11084            if (logging != MagickFalse)
11085              {
11086                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11087                  "     tRNS color   =(%d,%d,%d)",
11088                        (int) ping_trans_color.red,
11089                        (int) ping_trans_color.green,
11090                        (int) ping_trans_color.blue);
11091              }
11092          }
11093     }
11094
11095   png_write_info(ping,ping_info);
11096
11097   /* write orNT if image->orientation is defined */
11098   if (image->orientation != UndefinedOrientation)
11099     {
11100       unsigned char
11101         chunk[6];
11102       (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
11103       PNGType(chunk,mng_orNT);
11104       LogPNGChunk(logging,mng_orNT,1L);
11105       /* PNG uses Exif orientation values */
11106       chunk[4]=Magick_Orientation_to_Exif_Orientation(image->orientation);
11107       (void) WriteBlob(image,5,chunk);
11108       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11109     }
11110
11111   ping_wrote_caNv = MagickFalse;
11112
11113   /* write caNv chunk */
11114   if (ping_exclude_caNv == MagickFalse)
11115     {
11116       if ((image->page.width != 0 && image->page.width != image->columns) ||
11117           (image->page.height != 0 && image->page.height != image->rows) ||
11118           image->page.x != 0 || image->page.y != 0)
11119         {
11120           unsigned char
11121             chunk[20];
11122
11123           (void) WriteBlobMSBULong(image,16L);  /* data length=8 */
11124           PNGType(chunk,mng_caNv);
11125           LogPNGChunk(logging,mng_caNv,16L);
11126           PNGLong(chunk+4,(png_uint_32) image->page.width);
11127           PNGLong(chunk+8,(png_uint_32) image->page.height);
11128           PNGsLong(chunk+12,(png_int_32) image->page.x);
11129           PNGsLong(chunk+16,(png_int_32) image->page.y);
11130           (void) WriteBlob(image,20,chunk);
11131           (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11132           ping_wrote_caNv = MagickTrue;
11133         }
11134     }
11135
11136 #if defined(PNG_oFFs_SUPPORTED)
11137   if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
11138     {
11139       if (image->page.x || image->page.y)
11140         {
11141            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
11142               (png_int_32) image->page.y, 0);
11143
11144            if (logging != MagickFalse)
11145              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11146                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
11147                  (int) image->page.x, (int) image->page.y);
11148         }
11149     }
11150 #endif
11151
11152 #if (PNG_LIBPNG_VER == 10206)
11153     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
11154 #define PNG_HAVE_IDAT               0x04
11155     ping->mode |= PNG_HAVE_IDAT;
11156 #undef PNG_HAVE_IDAT
11157 #endif
11158
11159   png_set_packing(ping);
11160   /*
11161     Allocate memory.
11162   */
11163   rowbytes=image->columns;
11164   if (image_depth > 8)
11165     rowbytes*=2;
11166   switch (ping_color_type)
11167     {
11168       case PNG_COLOR_TYPE_RGB:
11169         rowbytes*=3;
11170         break;
11171
11172       case PNG_COLOR_TYPE_GRAY_ALPHA:
11173         rowbytes*=2;
11174         break;
11175
11176       case PNG_COLOR_TYPE_RGBA:
11177         rowbytes*=4;
11178         break;
11179
11180       default:
11181         break;
11182     }
11183
11184   if (logging != MagickFalse)
11185     {
11186       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11187         "  Writing PNG image data");
11188
11189       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11190         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
11191     }
11192   pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
11193   if (pixel_info == (MemoryInfo *) NULL)
11194     png_error(ping,"Allocation of memory for pixels failed");
11195   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
11196
11197   /*
11198     Initialize image scanlines.
11199   */
11200   quantum_info=AcquireQuantumInfo(image_info,image);
11201   if (quantum_info == (QuantumInfo *) NULL)
11202     png_error(ping,"Memory allocation for quantum_info failed");
11203   quantum_info->format=UndefinedQuantumFormat;
11204   SetQuantumDepth(image,quantum_info,image_depth);
11205   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
11206   num_passes=png_set_interlace_handling(ping);
11207
11208   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11209        !mng_info->write_png48 && !mng_info->write_png64 &&
11210        !mng_info->write_png32) &&
11211        (mng_info->IsPalette ||
11212        (image_info->type == BilevelType)) &&
11213        image_matte == MagickFalse &&
11214        ping_have_non_bw == MagickFalse)
11215     {
11216       /* Palette, Bilevel, or Opaque Monochrome */
11217       register const Quantum
11218         *p;
11219
11220       SetQuantumDepth(image,quantum_info,8);
11221       for (pass=0; pass < num_passes; pass++)
11222       {
11223         /*
11224           Convert PseudoClass image to a PNG monochrome image.
11225         */
11226         for (y=0; y < (ssize_t) image->rows; y++)
11227         {
11228           if (logging != MagickFalse && y == 0)
11229              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11230                  "    Writing row of pixels (0)");
11231
11232           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11233
11234           if (p == (const Quantum *) NULL)
11235             break;
11236
11237           if (mng_info->IsPalette)
11238             {
11239               (void) ExportQuantumPixels(image,(CacheView *) NULL,
11240                 quantum_info,GrayQuantum,ping_pixels,exception);
11241               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
11242                   mng_info->write_png_depth &&
11243                   mng_info->write_png_depth != old_bit_depth)
11244                 {
11245                   /* Undo pixel scaling */
11246                   for (i=0; i < (ssize_t) image->columns; i++)
11247                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
11248                      >> (8-old_bit_depth));
11249                 }
11250             }
11251
11252           else
11253             {
11254               (void) ExportQuantumPixels(image,(CacheView *) NULL,
11255                 quantum_info,RedQuantum,ping_pixels,exception);
11256             }
11257
11258           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11259             for (i=0; i < (ssize_t) image->columns; i++)
11260                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11261                       255 : 0);
11262
11263           if (logging != MagickFalse && y == 0)
11264             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11265                 "    Writing row of pixels (1)");
11266
11267           png_write_row(ping,ping_pixels);
11268
11269           status=SetImageProgress(image,SaveImageTag,
11270               (MagickOffsetType) (pass * image->rows + y),
11271               num_passes * image->rows);
11272
11273           if (status == MagickFalse)
11274             break;
11275         }
11276       }
11277     }
11278
11279   else   /* Not Palette, Bilevel, or Opaque Monochrome */
11280     {
11281       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11282           !mng_info->write_png48 && !mng_info->write_png64 &&
11283           !mng_info->write_png32) && (image_matte != MagickFalse ||
11284           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11285           (mng_info->IsPalette) && ping_have_color == MagickFalse)
11286         {
11287           register const Quantum
11288             *p;
11289
11290           for (pass=0; pass < num_passes; pass++)
11291           {
11292
11293           for (y=0; y < (ssize_t) image->rows; y++)
11294           {
11295             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11296
11297             if (p == (const Quantum *) NULL)
11298               break;
11299
11300             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11301               {
11302                 if (mng_info->IsPalette)
11303                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11304                     quantum_info,GrayQuantum,ping_pixels,exception);
11305
11306                 else
11307                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11308                     quantum_info,RedQuantum,ping_pixels,exception);
11309
11310                 if (logging != MagickFalse && y == 0)
11311                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11312                        "    Writing GRAY PNG pixels (2)");
11313               }
11314
11315             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11316               {
11317                 if (logging != MagickFalse && y == 0)
11318                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11319                          "    Writing GRAY_ALPHA PNG pixels (2)");
11320
11321                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11322                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11323               }
11324
11325             if (logging != MagickFalse && y == 0)
11326               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11327                   "    Writing row of pixels (2)");
11328
11329             png_write_row(ping,ping_pixels);
11330
11331             status=SetImageProgress(image,SaveImageTag,
11332               (MagickOffsetType) (pass * image->rows + y),
11333               num_passes * image->rows);
11334
11335             if (status == MagickFalse)
11336               break;
11337             }
11338           }
11339         }
11340
11341       else
11342         {
11343           register const Quantum
11344             *p;
11345
11346           for (pass=0; pass < num_passes; pass++)
11347           {
11348             if ((image_depth > 8) ||
11349                 mng_info->write_png24 ||
11350                 mng_info->write_png32 ||
11351                 mng_info->write_png48 ||
11352                 mng_info->write_png64 ||
11353                 (!mng_info->write_png8 && !mng_info->IsPalette))
11354             {
11355               for (y=0; y < (ssize_t) image->rows; y++)
11356               {
11357                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11358
11359                 if (p == (const Quantum *) NULL)
11360                   break;
11361
11362                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11363                   {
11364                     if (image->storage_class == DirectClass)
11365                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11366                         quantum_info,RedQuantum,ping_pixels,exception);
11367
11368                     else
11369                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11370                         quantum_info,GrayQuantum,ping_pixels,exception);
11371                   }
11372
11373                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11374                   {
11375                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11376                       quantum_info,GrayAlphaQuantum,ping_pixels,
11377                       exception);
11378
11379                     if (logging != MagickFalse && y == 0)
11380                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11381                            "    Writing GRAY_ALPHA PNG pixels (3)");
11382                   }
11383
11384                 else if (image_matte != MagickFalse)
11385                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11386                     quantum_info,RGBAQuantum,ping_pixels,exception);
11387
11388                 else
11389                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11390                     quantum_info,RGBQuantum,ping_pixels,exception);
11391
11392                 if (logging != MagickFalse && y == 0)
11393                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11394                       "    Writing row of pixels (3)");
11395
11396                 png_write_row(ping,ping_pixels);
11397
11398                 status=SetImageProgress(image,SaveImageTag,
11399                   (MagickOffsetType) (pass * image->rows + y),
11400                   num_passes * image->rows);
11401
11402                 if (status == MagickFalse)
11403                   break;
11404               }
11405             }
11406
11407           else
11408             /* not ((image_depth > 8) ||
11409                 mng_info->write_png24 || mng_info->write_png32 ||
11410                 mng_info->write_png48 || mng_info->write_png64 ||
11411                 (!mng_info->write_png8 && !mng_info->IsPalette))
11412              */
11413             {
11414               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11415                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11416                 {
11417                   if (logging != MagickFalse)
11418                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11419                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11420
11421                   SetQuantumDepth(image,quantum_info,8);
11422                   image_depth=8;
11423                 }
11424
11425               for (y=0; y < (ssize_t) image->rows; y++)
11426               {
11427                 if (logging != MagickFalse && y == 0)
11428                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11429                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
11430                     pass);
11431
11432                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11433
11434                 if (p == (const Quantum *) NULL)
11435                   break;
11436
11437                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11438                   {
11439                     SetQuantumDepth(image,quantum_info,image->depth);
11440
11441                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11442                        quantum_info,GrayQuantum,ping_pixels,exception);
11443                   }
11444
11445                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11446                   {
11447                     if (logging != MagickFalse && y == 0)
11448                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11449                            "  Writing GRAY_ALPHA PNG pixels (4)");
11450
11451                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11452                          quantum_info,GrayAlphaQuantum,ping_pixels,
11453                          exception);
11454                   }
11455
11456                 else
11457                   {
11458                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11459                       quantum_info,IndexQuantum,ping_pixels,exception);
11460
11461                     if (logging != MagickFalse && y <= 2)
11462                     {
11463                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11464                           "  Writing row of non-gray pixels (4)");
11465
11466                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11467                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
11468                           (int)ping_pixels[0],(int)ping_pixels[1]);
11469                     }
11470                   }
11471                 png_write_row(ping,ping_pixels);
11472
11473                 status=SetImageProgress(image,SaveImageTag,
11474                   (MagickOffsetType) (pass * image->rows + y),
11475                   num_passes * image->rows);
11476
11477                 if (status == MagickFalse)
11478                   break;
11479               }
11480             }
11481           }
11482         }
11483     }
11484
11485   if (quantum_info != (QuantumInfo *) NULL)
11486     quantum_info=DestroyQuantumInfo(quantum_info);
11487
11488   if (logging != MagickFalse)
11489     {
11490       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11491         "  Wrote PNG image data");
11492
11493       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11494         "    Width: %.20g",(double) ping_width);
11495
11496       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11497         "    Height: %.20g",(double) ping_height);
11498
11499       if (mng_info->write_png_depth)
11500         {
11501           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11502             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
11503         }
11504
11505       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11506         "    PNG bit-depth written: %d",ping_bit_depth);
11507
11508       if (mng_info->write_png_colortype)
11509         {
11510           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11511             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
11512         }
11513
11514       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11515         "    PNG color-type written: %d",ping_color_type);
11516
11517       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11518         "    PNG Interlace method: %d",ping_interlace_method);
11519     }
11520   /*
11521     Generate text chunks after IDAT.
11522   */
11523   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11524   {
11525     ResetImagePropertyIterator(image);
11526     property=GetNextImageProperty(image);
11527     while (property != (const char *) NULL)
11528     {
11529       png_textp
11530         text;
11531
11532       value=GetImageProperty(image,property,exception);
11533
11534       /* Don't write any "png:" or "jpeg:" properties; those are just for
11535        * "identify" or for passing through to another JPEG
11536        */
11537       if ((LocaleNCompare(property,"png:",4) != 0 &&
11538            LocaleNCompare(property,"jpeg:",5) != 0) &&
11539
11540
11541           /* Suppress density and units if we wrote a pHYs chunk */
11542           (ping_exclude_pHYs != MagickFalse      ||
11543           LocaleCompare(property,"density") != 0 ||
11544           LocaleCompare(property,"units") != 0) &&
11545
11546           /* Suppress the IM-generated Date:create and Date:modify */
11547           (ping_exclude_date == MagickFalse      ||
11548           LocaleNCompare(property, "Date:",5) != 0))
11549         {
11550         if (value != (const char *) NULL)
11551           {
11552
11553 #if PNG_LIBPNG_VER >= 10400
11554             text=(png_textp) png_malloc(ping,
11555                  (png_alloc_size_t) sizeof(png_text));
11556 #else
11557             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11558 #endif
11559             text[0].key=(char *) property;
11560             text[0].text=(char *) value;
11561             text[0].text_length=strlen(value);
11562
11563             if (ping_exclude_tEXt != MagickFalse)
11564                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11565
11566             else if (ping_exclude_zTXt != MagickFalse)
11567                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11568
11569             else
11570             {
11571                text[0].compression=image_info->compression == NoCompression ||
11572                  (image_info->compression == UndefinedCompression &&
11573                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11574                  PNG_TEXT_COMPRESSION_zTXt ;
11575             }
11576
11577             if (logging != MagickFalse)
11578               {
11579                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11580                   "  Setting up text chunk");
11581
11582                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11583                   "    keyword: '%s'",text[0].key);
11584               }
11585
11586             png_set_text(ping,ping_info,text,1);
11587             png_free(ping,text);
11588           }
11589         }
11590       property=GetNextImageProperty(image);
11591     }
11592   }
11593
11594   /* write eXIf profile */
11595   if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
11596     {
11597       char
11598         *name;
11599
11600       ResetImageProfileIterator(image);
11601
11602       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
11603       {
11604         if (LocaleCompare(name,"exif") == 0)
11605           {
11606             const StringInfo
11607               *profile;
11608
11609             profile=GetImageProfile(image,name);
11610
11611             if (profile != (StringInfo *) NULL)
11612               {
11613                 png_uint_32
11614                   length;
11615
11616                 unsigned char
11617                   chunk[4],
11618                   *data;
11619
11620                StringInfo
11621                  *ping_profile;
11622
11623                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11624                   "  Have eXIf profile");
11625
11626                ping_profile=CloneStringInfo(profile);
11627                data=GetStringInfoDatum(ping_profile),
11628                length=(png_uint_32) GetStringInfoLength(ping_profile);
11629
11630                PNGType(chunk,mng_eXIf);
11631                if (length < 7)
11632                  {
11633                    ping_profile=DestroyStringInfo(ping_profile);
11634                    break;  /* otherwise crashes */
11635                  }
11636
11637                /* skip the "Exif\0\0" JFIF Exif Header ID */
11638                length -= 6;
11639
11640                LogPNGChunk(logging,chunk,length);
11641                (void) WriteBlobMSBULong(image,length);
11642                (void) WriteBlob(image,4,chunk);
11643                (void) WriteBlob(image,length,data+6);
11644                (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),
11645                  data+6, (uInt) length));
11646                ping_profile=DestroyStringInfo(ping_profile);
11647                break;
11648              }
11649          }
11650        name=GetNextImageProfile(image);
11651      }
11652   }
11653
11654   if (logging != MagickFalse)
11655     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11656       "  Writing PNG end info");
11657
11658   png_write_end(ping,ping_info);
11659
11660   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11661     {
11662       if (mng_info->page.x || mng_info->page.y ||
11663           (ping_width != mng_info->page.width) ||
11664           (ping_height != mng_info->page.height))
11665         {
11666           unsigned char
11667             chunk[32];
11668
11669           /*
11670             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11671           */
11672           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11673           PNGType(chunk,mng_FRAM);
11674           LogPNGChunk(logging,mng_FRAM,27L);
11675           chunk[4]=4;
11676           chunk[5]=0;  /* frame name separator (no name) */
11677           chunk[6]=1;  /* flag for changing delay, for next frame only */
11678           chunk[7]=0;  /* flag for changing frame timeout */
11679           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11680           chunk[9]=0;  /* flag for changing frame sync_id */
11681           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11682           chunk[14]=0; /* clipping boundaries delta type */
11683           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11684           PNGLong(chunk+19,
11685              (png_uint_32) (mng_info->page.x + ping_width));
11686           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11687           PNGLong(chunk+27,
11688              (png_uint_32) (mng_info->page.y + ping_height));
11689           (void) WriteBlob(image,31,chunk);
11690           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11691           mng_info->old_framing_mode=4;
11692           mng_info->framing_mode=1;
11693         }
11694
11695       else
11696         mng_info->framing_mode=3;
11697     }
11698   if (mng_info->write_mng && !mng_info->need_fram &&
11699       ((int) image->dispose == 3))
11700      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11701
11702   /*
11703     Free PNG resources.
11704   */
11705
11706   png_destroy_write_struct(&ping,&ping_info);
11707
11708   pixel_info=RelinquishVirtualMemory(pixel_info);
11709
11710   if (ping_have_blob != MagickFalse)
11711      (void) CloseBlob(image);
11712
11713   image_info=DestroyImageInfo(image_info);
11714   image=DestroyImage(image);
11715
11716   /* Store bit depth actually written */
11717   s[0]=(char) ping_bit_depth;
11718   s[1]='\0';
11719
11720   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11721
11722   if (logging != MagickFalse)
11723     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11724       "  exit WriteOnePNGImage()");
11725
11726 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11727   UnlockSemaphoreInfo(ping_semaphore);
11728 #endif
11729
11730    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11731     *    Throwing an Exception when an error occurs.
11732     */
11733
11734   return(MagickTrue);
11735 /*  End write one PNG image */
11736
11737 }
11738
11739 /*
11740 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11741 %                                                                             %
11742 %                                                                             %
11743 %                                                                             %
11744 %   W r i t e P N G I m a g e                                                 %
11745 %                                                                             %
11746 %                                                                             %
11747 %                                                                             %
11748 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11749 %
11750 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11751 %  Multiple-image Network Graphics (MNG) image file.
11752 %
11753 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11754 %
11755 %  The format of the WritePNGImage method is:
11756 %
11757 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11758 %        Image *image,ExceptionInfo *exception)
11759 %
11760 %  A description of each parameter follows:
11761 %
11762 %    o image_info: the image info.
11763 %
11764 %    o image:  The image.
11765 %
11766 %    o exception: return any errors or warnings in this structure.
11767 %
11768 %  Returns MagickTrue on success, MagickFalse on failure.
11769 %
11770 %  Communicating with the PNG encoder:
11771 %
11772 %  While the datastream written is always in PNG format and normally would
11773 %  be given the "png" file extension, this method also writes the following
11774 %  pseudo-formats which are subsets of png:
11775 %
11776 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11777 %               a depth greater than 8, the depth is reduced. If transparency
11778 %               is present, the tRNS chunk must only have values 0 and 255
11779 %               (i.e., transparency is binary: fully opaque or fully
11780 %               transparent).  If other values are present they will be
11781 %               50%-thresholded to binary transparency.  If more than 256
11782 %               colors are present, they will be quantized to the 4-4-4-1,
11783 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11784 %               of any resulting fully-transparent pixels is changed to
11785 %               the image's background color.
11786 %
11787 %               If you want better quantization or dithering of the colors
11788 %               or alpha than that, you need to do it before calling the
11789 %               PNG encoder. The pixels contain 8-bit indices even if
11790 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11791 %               images will be written as indexed PNG files even though the
11792 %               PNG grayscale type might be slightly more efficient.  Please
11793 %               note that writing to the PNG8 format may result in loss
11794 %               of color and alpha data.
11795 %
11796 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11797 %               chunk can be present to convey binary transparency by naming
11798 %               one of the colors as transparent.  The only loss incurred
11799 %               is reduction of sample depth to 8.  If the image has more
11800 %               than one transparent color, has semitransparent pixels, or
11801 %               has an opaque pixel with the same RGB components as the
11802 %               transparent color, an image is not written.
11803 %
11804 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
11805 %               transparency is permitted, i.e., the alpha sample for
11806 %               each pixel can have any value from 0 to 255. The alpha
11807 %               channel is present even if the image is fully opaque.
11808 %               The only loss in data is the reduction of the sample depth
11809 %               to 8.
11810 %
11811 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
11812 %               chunk can be present to convey binary transparency by naming
11813 %               one of the colors as transparent.  If the image has more
11814 %               than one transparent color, has semitransparent pixels, or
11815 %               has an opaque pixel with the same RGB components as the
11816 %               transparent color, an image is not written.
11817 %
11818 %    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
11819 %               transparency is permitted, i.e., the alpha sample for
11820 %               each pixel can have any value from 0 to 65535. The alpha
11821 %               channel is present even if the image is fully opaque.
11822 %
11823 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
11824 %               image, if the input was a PNG, is written.  If these values
11825 %               cannot be found, or if the pixels have been changed in a way
11826 %               that makes this impossible, then "PNG00" falls back to the
11827 %               regular "PNG" format.
11828 %
11829 %    o -define: For more precise control of the PNG output, you can use the
11830 %               Image options "png:bit-depth" and "png:color-type".  These
11831 %               can be set from the commandline with "-define" and also
11832 %               from the application programming interfaces.  The options
11833 %               are case-independent and are converted to lowercase before
11834 %               being passed to this encoder.
11835 %
11836 %               png:color-type can be 0, 2, 3, 4, or 6.
11837 %
11838 %               When png:color-type is 0 (Grayscale), png:bit-depth can
11839 %               be 1, 2, 4, 8, or 16.
11840 %
11841 %               When png:color-type is 2 (RGB), png:bit-depth can
11842 %               be 8 or 16.
11843 %
11844 %               When png:color-type is 3 (Indexed), png:bit-depth can
11845 %               be 1, 2, 4, or 8.  This refers to the number of bits
11846 %               used to store the index.  The color samples always have
11847 %               bit-depth 8 in indexed PNG files.
11848 %
11849 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11850 %               png:bit-depth can be 8 or 16.
11851 %
11852 %               If the image cannot be written without loss with the
11853 %               requested bit-depth and color-type, a PNG file will not
11854 %               be written, a warning will be issued, and the encoder will
11855 %               return MagickFalse.
11856 %
11857 %  Since image encoders should not be responsible for the "heavy lifting",
11858 %  the user should make sure that ImageMagick has already reduced the
11859 %  image depth and number of colors and limit transparency to binary
11860 %  transparency prior to attempting to write the image with depth, color,
11861 %  or transparency limitations.
11862 %
11863 %  Note that another definition, "png:bit-depth-written" exists, but it
11864 %  is not intended for external use.  It is only used internally by the
11865 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11866 %
11867 %  As of version 6.6.6 the following optimizations are always done:
11868 %
11869 %   o  32-bit depth is reduced to 16.
11870 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
11871 %      high byte and low byte are identical.
11872 %   o  Palette is sorted to remove unused entries and to put a
11873 %      transparent color first, if BUILD_PNG_PALETTE is defined.
11874 %   o  Opaque matte channel is removed when writing an indexed PNG.
11875 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
11876 %      this can be done without loss and a larger bit depth N was not
11877 %      requested via the "-define png:bit-depth=N" option.
11878 %   o  If matte channel is present but only one transparent color is
11879 %      present, RGB+tRNS is written instead of RGBA
11880 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
11881 %      was requested when converting an opaque image).
11882 %
11883 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11884 */
11885 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11886   Image *image,ExceptionInfo *exception)
11887 {
11888   MagickBooleanType
11889     excluding,
11890     logging,
11891     status;
11892
11893   MngInfo
11894     *mng_info;
11895
11896   const char
11897     *value;
11898
11899   int
11900     source;
11901
11902   /*
11903     Open image file.
11904   */
11905   assert(image_info != (const ImageInfo *) NULL);
11906   assert(image_info->signature == MagickCoreSignature);
11907   assert(image != (Image *) NULL);
11908   assert(image->signature == MagickCoreSignature);
11909   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11910   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11911   /*
11912     Allocate a MngInfo structure.
11913   */
11914   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11915
11916   if (mng_info == (MngInfo *) NULL)
11917     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11918
11919   /*
11920     Initialize members of the MngInfo structure.
11921   */
11922   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11923   mng_info->image=image;
11924   mng_info->equal_backgrounds=MagickTrue;
11925
11926   /* See if user has requested a specific PNG subformat */
11927
11928   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11929   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11930   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11931   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11932   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11933
11934   value=GetImageOption(image_info,"png:format");
11935
11936   if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
11937     {
11938       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11939          "  Format=%s",value);
11940
11941       mng_info->write_png8 = MagickFalse;
11942       mng_info->write_png24 = MagickFalse;
11943       mng_info->write_png32 = MagickFalse;
11944       mng_info->write_png48 = MagickFalse;
11945       mng_info->write_png64 = MagickFalse;
11946
11947       if (LocaleCompare(value,"png8") == 0)
11948         mng_info->write_png8 = MagickTrue;
11949
11950       else if (LocaleCompare(value,"png24") == 0)
11951         mng_info->write_png24 = MagickTrue;
11952
11953       else if (LocaleCompare(value,"png32") == 0)
11954         mng_info->write_png32 = MagickTrue;
11955
11956       else if (LocaleCompare(value,"png48") == 0)
11957         mng_info->write_png48 = MagickTrue;
11958
11959       else if (LocaleCompare(value,"png64") == 0)
11960         mng_info->write_png64 = MagickTrue;
11961
11962       else if ((LocaleCompare(value,"png00") == 0) ||
11963          LocaleCompare(image_info->magick,"PNG00") == 0)
11964         {
11965           /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11966           value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11967
11968           if (value != (char *) NULL)
11969             {
11970               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11971                  "  png00 inherited bit depth=%s",value);
11972
11973               if (LocaleCompare(value,"1") == 0)
11974                 mng_info->write_png_depth = 1;
11975
11976               else if (LocaleCompare(value,"2") == 0)
11977                 mng_info->write_png_depth = 2;
11978
11979               else if (LocaleCompare(value,"4") == 0)
11980                 mng_info->write_png_depth = 4;
11981
11982               else if (LocaleCompare(value,"8") == 0)
11983                 mng_info->write_png_depth = 8;
11984
11985               else if (LocaleCompare(value,"16") == 0)
11986                 mng_info->write_png_depth = 16;
11987             }
11988
11989           value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11990
11991           if (value != (char *) NULL)
11992             {
11993               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11994                  "  png00 inherited color type=%s",value);
11995
11996               if (LocaleCompare(value,"0") == 0)
11997                 mng_info->write_png_colortype = 1;
11998
11999               else if (LocaleCompare(value,"2") == 0)
12000                 mng_info->write_png_colortype = 3;
12001
12002               else if (LocaleCompare(value,"3") == 0)
12003                 mng_info->write_png_colortype = 4;
12004
12005               else if (LocaleCompare(value,"4") == 0)
12006                 mng_info->write_png_colortype = 5;
12007
12008               else if (LocaleCompare(value,"6") == 0)
12009                 mng_info->write_png_colortype = 7;
12010             }
12011         }
12012     }
12013
12014   if (mng_info->write_png8)
12015     {
12016       mng_info->write_png_colortype = /* 3 */ 4;
12017       mng_info->write_png_depth = 8;
12018       image->depth = 8;
12019     }
12020
12021   if (mng_info->write_png24)
12022     {
12023       mng_info->write_png_colortype = /* 2 */ 3;
12024       mng_info->write_png_depth = 8;
12025       image->depth = 8;
12026
12027       if (image->alpha_trait != UndefinedPixelTrait)
12028         (void) SetImageType(image,TrueColorAlphaType,exception);
12029
12030       else
12031         (void) SetImageType(image,TrueColorType,exception);
12032
12033       (void) SyncImage(image,exception);
12034     }
12035
12036   if (mng_info->write_png32)
12037     {
12038       mng_info->write_png_colortype = /* 6 */  7;
12039       mng_info->write_png_depth = 8;
12040       image->depth = 8;
12041       image->alpha_trait = BlendPixelTrait;
12042
12043       (void) SetImageType(image,TrueColorAlphaType,exception);
12044       (void) SyncImage(image,exception);
12045     }
12046
12047   if (mng_info->write_png48)
12048     {
12049       mng_info->write_png_colortype = /* 2 */ 3;
12050       mng_info->write_png_depth = 16;
12051       image->depth = 16;
12052
12053       if (image->alpha_trait != UndefinedPixelTrait)
12054         (void) SetImageType(image,TrueColorAlphaType,exception);
12055
12056       else
12057         (void) SetImageType(image,TrueColorType,exception);
12058
12059       (void) SyncImage(image,exception);
12060     }
12061
12062   if (mng_info->write_png64)
12063     {
12064       mng_info->write_png_colortype = /* 6 */  7;
12065       mng_info->write_png_depth = 16;
12066       image->depth = 16;
12067       image->alpha_trait = BlendPixelTrait;
12068
12069       (void) SetImageType(image,TrueColorAlphaType,exception);
12070       (void) SyncImage(image,exception);
12071     }
12072
12073   value=GetImageOption(image_info,"png:bit-depth");
12074
12075   if (value != (char *) NULL)
12076     {
12077       if (LocaleCompare(value,"1") == 0)
12078         mng_info->write_png_depth = 1;
12079
12080       else if (LocaleCompare(value,"2") == 0)
12081         mng_info->write_png_depth = 2;
12082
12083       else if (LocaleCompare(value,"4") == 0)
12084         mng_info->write_png_depth = 4;
12085
12086       else if (LocaleCompare(value,"8") == 0)
12087         mng_info->write_png_depth = 8;
12088
12089       else if (LocaleCompare(value,"16") == 0)
12090         mng_info->write_png_depth = 16;
12091
12092       else
12093         (void) ThrowMagickException(exception,
12094              GetMagickModule(),CoderWarning,
12095              "ignoring invalid defined png:bit-depth",
12096              "=%s",value);
12097
12098       if (logging != MagickFalse)
12099         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12100           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
12101     }
12102
12103   value=GetImageOption(image_info,"png:color-type");
12104
12105   if (value != (char *) NULL)
12106     {
12107       /* We must store colortype+1 because 0 is a valid colortype */
12108       if (LocaleCompare(value,"0") == 0)
12109         mng_info->write_png_colortype = 1;
12110
12111       else if (LocaleCompare(value,"1") == 0)
12112         mng_info->write_png_colortype = 2;
12113
12114       else if (LocaleCompare(value,"2") == 0)
12115         mng_info->write_png_colortype = 3;
12116
12117       else if (LocaleCompare(value,"3") == 0)
12118         mng_info->write_png_colortype = 4;
12119
12120       else if (LocaleCompare(value,"4") == 0)
12121         mng_info->write_png_colortype = 5;
12122
12123       else if (LocaleCompare(value,"6") == 0)
12124         mng_info->write_png_colortype = 7;
12125
12126       else
12127         (void) ThrowMagickException(exception,
12128              GetMagickModule(),CoderWarning,
12129              "ignoring invalid defined png:color-type",
12130              "=%s",value);
12131
12132       if (logging != MagickFalse)
12133         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12134           "  png:color-type=%d was defined.\n",
12135           mng_info->write_png_colortype-1);
12136     }
12137
12138   /* Check for chunks to be excluded:
12139    *
12140    * The default is to not exclude any known chunks except for any
12141    * listed in the "unused_chunks" array, above.
12142    *
12143    * Chunks can be listed for exclusion via a "png:exclude-chunk"
12144    * define (in the image properties or in the image artifacts)
12145    * or via a mng_info member.  For convenience, in addition
12146    * to or instead of a comma-separated list of chunks, the
12147    * "exclude-chunk" string can be simply "all" or "none".
12148    *
12149    * Note that the "-strip" option provides a convenient way of
12150    * doing the equivalent of
12151    *
12152    *    -define png:exclude-chunk="bKGD,caNv,cHRM,eXIf,gAMA,iCCP,
12153    *            iTXt,pHYs,sRGB,tEXt,zCCP,zTXt,date"
12154    *
12155    * The exclude-chunk define takes priority over the mng_info.
12156    *
12157    * A "png:include-chunk" define takes  priority over both the
12158    * mng_info and the "png:exclude-chunk" define.  Like the
12159    * "exclude-chunk" string, it can define "all" or "none" as
12160    * well as a comma-separated list.  Chunks that are unknown to
12161    * ImageMagick are always excluded, regardless of their "copy-safe"
12162    * status according to the PNG specification, and even if they
12163    * appear in the "include-chunk" list. Such defines appearing among
12164    * the image options take priority over those found among the image
12165    * artifacts.
12166    *
12167    * Finally, all chunks listed in the "unused_chunks" array are
12168    * automatically excluded, regardless of the other instructions
12169    * or lack thereof.
12170    *
12171    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
12172    * will not be written and the gAMA chunk will only be written if it
12173    * is not between .45 and .46, or approximately (1.0/2.2).
12174    *
12175    * If you exclude tRNS and the image has transparency, the colortype
12176    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
12177    *
12178    * The -strip option causes StripImage() to set the png:include-chunk
12179    * artifact to "none,trns,gama".
12180    */
12181
12182   mng_info->ping_exclude_bKGD=MagickFalse;
12183   mng_info->ping_exclude_caNv=MagickFalse;
12184   mng_info->ping_exclude_cHRM=MagickFalse;
12185   mng_info->ping_exclude_date=MagickFalse;
12186   mng_info->ping_exclude_eXIf=MagickFalse;
12187   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
12188   mng_info->ping_exclude_gAMA=MagickFalse;
12189   mng_info->ping_exclude_iCCP=MagickFalse;
12190   /* mng_info->ping_exclude_iTXt=MagickFalse; */
12191   mng_info->ping_exclude_oFFs=MagickFalse;
12192   mng_info->ping_exclude_pHYs=MagickFalse;
12193   mng_info->ping_exclude_sRGB=MagickFalse;
12194   mng_info->ping_exclude_tEXt=MagickFalse;
12195   mng_info->ping_exclude_tIME=MagickFalse;
12196   mng_info->ping_exclude_tRNS=MagickFalse;
12197   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
12198   mng_info->ping_exclude_zTXt=MagickFalse;
12199
12200   mng_info->ping_preserve_colormap=MagickFalse;
12201
12202   value=GetImageOption(image_info,"png:preserve-colormap");
12203   if (value == NULL)
12204      value=GetImageArtifact(image,"png:preserve-colormap");
12205   if (value != NULL)
12206      mng_info->ping_preserve_colormap=MagickTrue;
12207
12208   mng_info->ping_preserve_iCCP=MagickFalse;
12209
12210   value=GetImageOption(image_info,"png:preserve-iCCP");
12211   if (value == NULL)
12212      value=GetImageArtifact(image,"png:preserve-iCCP");
12213   if (value != NULL)
12214      mng_info->ping_preserve_iCCP=MagickTrue;
12215
12216   /* These compression-level, compression-strategy, and compression-filter
12217    * defines take precedence over values from the -quality option.
12218    */
12219   value=GetImageOption(image_info,"png:compression-level");
12220   if (value == NULL)
12221      value=GetImageArtifact(image,"png:compression-level");
12222   if (value != NULL)
12223   {
12224       /* We have to add 1 to everything because 0 is a valid input,
12225        * and we want to use 0 (the default) to mean undefined.
12226        */
12227       if (LocaleCompare(value,"0") == 0)
12228         mng_info->write_png_compression_level = 1;
12229
12230       else if (LocaleCompare(value,"1") == 0)
12231         mng_info->write_png_compression_level = 2;
12232
12233       else if (LocaleCompare(value,"2") == 0)
12234         mng_info->write_png_compression_level = 3;
12235
12236       else if (LocaleCompare(value,"3") == 0)
12237         mng_info->write_png_compression_level = 4;
12238
12239       else if (LocaleCompare(value,"4") == 0)
12240         mng_info->write_png_compression_level = 5;
12241
12242       else if (LocaleCompare(value,"5") == 0)
12243         mng_info->write_png_compression_level = 6;
12244
12245       else if (LocaleCompare(value,"6") == 0)
12246         mng_info->write_png_compression_level = 7;
12247
12248       else if (LocaleCompare(value,"7") == 0)
12249         mng_info->write_png_compression_level = 8;
12250
12251       else if (LocaleCompare(value,"8") == 0)
12252         mng_info->write_png_compression_level = 9;
12253
12254       else if (LocaleCompare(value,"9") == 0)
12255         mng_info->write_png_compression_level = 10;
12256
12257       else
12258         (void) ThrowMagickException(exception,
12259              GetMagickModule(),CoderWarning,
12260              "ignoring invalid defined png:compression-level",
12261              "=%s",value);
12262     }
12263
12264   value=GetImageOption(image_info,"png:compression-strategy");
12265   if (value == NULL)
12266      value=GetImageArtifact(image,"png:compression-strategy");
12267   if (value != NULL)
12268   {
12269       if (LocaleCompare(value,"0") == 0)
12270         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12271
12272       else if (LocaleCompare(value,"1") == 0)
12273         mng_info->write_png_compression_strategy = Z_FILTERED+1;
12274
12275       else if (LocaleCompare(value,"2") == 0)
12276         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
12277
12278       else if (LocaleCompare(value,"3") == 0)
12279 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
12280         mng_info->write_png_compression_strategy = Z_RLE+1;
12281 #else
12282         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12283 #endif
12284
12285       else if (LocaleCompare(value,"4") == 0)
12286 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
12287         mng_info->write_png_compression_strategy = Z_FIXED+1;
12288 #else
12289         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12290 #endif
12291
12292       else
12293         (void) ThrowMagickException(exception,
12294              GetMagickModule(),CoderWarning,
12295              "ignoring invalid defined png:compression-strategy",
12296              "=%s",value);
12297     }
12298
12299   value=GetImageOption(image_info,"png:compression-filter");
12300   if (value == NULL)
12301      value=GetImageArtifact(image,"png:compression-filter");
12302   if (value != NULL)
12303   {
12304       /* To do: combinations of filters allowed by libpng
12305        * masks 0x08 through 0xf8
12306        *
12307        * Implement this as a comma-separated list of 0,1,2,3,4,5
12308        * where 5 is a special case meaning PNG_ALL_FILTERS.
12309        */
12310
12311       if (LocaleCompare(value,"0") == 0)
12312         mng_info->write_png_compression_filter = 1;
12313
12314       else if (LocaleCompare(value,"1") == 0)
12315         mng_info->write_png_compression_filter = 2;
12316
12317       else if (LocaleCompare(value,"2") == 0)
12318         mng_info->write_png_compression_filter = 3;
12319
12320       else if (LocaleCompare(value,"3") == 0)
12321         mng_info->write_png_compression_filter = 4;
12322
12323       else if (LocaleCompare(value,"4") == 0)
12324         mng_info->write_png_compression_filter = 5;
12325
12326       else if (LocaleCompare(value,"5") == 0)
12327         mng_info->write_png_compression_filter = 6;
12328
12329       else
12330         (void) ThrowMagickException(exception,
12331              GetMagickModule(),CoderWarning,
12332              "ignoring invalid defined png:compression-filter",
12333              "=%s",value);
12334   }
12335
12336   for (source=0; source<8; source++)
12337   {
12338     value = NULL;
12339
12340     if (source == 0)
12341       value=GetImageOption(image_info,"png:exclude-chunks");
12342
12343     if (source == 1)
12344       value=GetImageArtifact(image,"png:exclude-chunks");
12345
12346     if (source == 2)
12347       value=GetImageOption(image_info,"png:exclude-chunk");
12348
12349     if (source == 3)
12350       value=GetImageArtifact(image,"png:exclude-chunk");
12351
12352     if (source == 4)
12353       value=GetImageOption(image_info,"png:include-chunks");
12354
12355     if (source == 5)
12356       value=GetImageArtifact(image,"png:include-chunks");
12357
12358     if (source == 6)
12359       value=GetImageOption(image_info,"png:include-chunk");
12360
12361     if (source == 7)
12362       value=GetImageArtifact(image,"png:include-chunk");
12363
12364     if (value == NULL)
12365        continue;
12366
12367     if (source < 4)
12368       excluding = MagickTrue;
12369     else
12370       excluding = MagickFalse;
12371
12372     if (logging != MagickFalse)
12373       {
12374         if (source == 0 || source == 2)
12375            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12376               "  png:exclude-chunk=%s found in image options.\n", value);
12377         else if (source == 1 || source == 3)
12378            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12379               "  png:exclude-chunk=%s found in image artifacts.\n", value);
12380         else if (source == 4 || source == 6)
12381            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12382               "  png:include-chunk=%s found in image options.\n", value);
12383         else /* if (source == 5 || source == 7) */
12384            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12385               "  png:include-chunk=%s found in image artifacts.\n", value);
12386       }
12387
12388     if (IsOptionMember("all",value) != MagickFalse)
12389       {
12390         mng_info->ping_exclude_bKGD=excluding;
12391         mng_info->ping_exclude_caNv=excluding;
12392         mng_info->ping_exclude_cHRM=excluding;
12393         mng_info->ping_exclude_date=excluding;
12394         mng_info->ping_exclude_EXIF=excluding;
12395         mng_info->ping_exclude_eXIf=excluding;
12396         mng_info->ping_exclude_gAMA=excluding;
12397         mng_info->ping_exclude_iCCP=excluding;
12398         /* mng_info->ping_exclude_iTXt=excluding; */
12399         mng_info->ping_exclude_oFFs=excluding;
12400         mng_info->ping_exclude_pHYs=excluding;
12401         mng_info->ping_exclude_sRGB=excluding;
12402         mng_info->ping_exclude_tEXt=excluding;
12403         mng_info->ping_exclude_tIME=excluding;
12404         mng_info->ping_exclude_tRNS=excluding;
12405         mng_info->ping_exclude_zCCP=excluding;
12406         mng_info->ping_exclude_zTXt=excluding;
12407       }
12408
12409     if (IsOptionMember("none",value) != MagickFalse)
12410       {
12411         mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12412           MagickTrue;
12413         mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
12414           MagickTrue;
12415         mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12416           MagickTrue;
12417         mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12418           MagickTrue;
12419         mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
12420           MagickTrue;
12421         mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12422           MagickTrue;
12423         mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12424           MagickTrue;
12425         mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12426           MagickTrue;
12427         /* mng_info->ping_exclude_iTXt=!excluding; */
12428         mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12429           MagickTrue;
12430         mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12431           MagickTrue;
12432         mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12433           MagickTrue;
12434         mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12435           MagickTrue;
12436         mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12437           MagickTrue;
12438         mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12439           MagickTrue;
12440         mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12441           MagickTrue;
12442         mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12443           MagickTrue;
12444       }
12445
12446     if (IsOptionMember("bkgd",value) != MagickFalse)
12447       mng_info->ping_exclude_bKGD=excluding;
12448
12449     if (IsOptionMember("caNv",value) != MagickFalse)
12450       mng_info->ping_exclude_caNv=excluding;
12451
12452     if (IsOptionMember("chrm",value) != MagickFalse)
12453       mng_info->ping_exclude_cHRM=excluding;
12454
12455     if (IsOptionMember("date",value) != MagickFalse)
12456       mng_info->ping_exclude_date=excluding;
12457
12458     if (IsOptionMember("exif",value) != MagickFalse)
12459       {
12460         mng_info->ping_exclude_EXIF=excluding;
12461         mng_info->ping_exclude_eXIf=excluding;
12462       }
12463
12464     if (IsOptionMember("gama",value) != MagickFalse)
12465       mng_info->ping_exclude_gAMA=excluding;
12466
12467     if (IsOptionMember("iccp",value) != MagickFalse)
12468       mng_info->ping_exclude_iCCP=excluding;
12469
12470 #if 0
12471     if (IsOptionMember("itxt",value) != MagickFalse)
12472       mng_info->ping_exclude_iTXt=excluding;
12473 #endif
12474
12475     if (IsOptionMember("offs",value) != MagickFalse)
12476       mng_info->ping_exclude_oFFs=excluding;
12477
12478     if (IsOptionMember("phys",value) != MagickFalse)
12479       mng_info->ping_exclude_pHYs=excluding;
12480
12481     if (IsOptionMember("srgb",value) != MagickFalse)
12482       mng_info->ping_exclude_sRGB=excluding;
12483
12484     if (IsOptionMember("text",value) != MagickFalse)
12485       mng_info->ping_exclude_tEXt=excluding;
12486
12487     if (IsOptionMember("time",value) != MagickFalse)
12488       mng_info->ping_exclude_tIME=excluding;
12489
12490     if (IsOptionMember("trns",value) != MagickFalse)
12491       mng_info->ping_exclude_tRNS=excluding;
12492
12493     if (IsOptionMember("zccp",value) != MagickFalse)
12494       mng_info->ping_exclude_zCCP=excluding;
12495
12496     if (IsOptionMember("ztxt",value) != MagickFalse)
12497       mng_info->ping_exclude_zTXt=excluding;
12498   }
12499
12500   if (logging != MagickFalse)
12501   {
12502     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12503       "  Chunks to be excluded from the output png:");
12504     if (mng_info->ping_exclude_bKGD != MagickFalse)
12505       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12506           "    bKGD");
12507     if (mng_info->ping_exclude_caNv != MagickFalse)
12508       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12509           "    caNv");
12510     if (mng_info->ping_exclude_cHRM != MagickFalse)
12511       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12512           "    cHRM");
12513     if (mng_info->ping_exclude_date != MagickFalse)
12514       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12515           "    date");
12516     if (mng_info->ping_exclude_EXIF != MagickFalse)
12517       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12518           "    EXIF");
12519     if (mng_info->ping_exclude_eXIf != MagickFalse)
12520       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12521           "    eXIf");
12522     if (mng_info->ping_exclude_gAMA != MagickFalse)
12523       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12524           "    gAMA");
12525     if (mng_info->ping_exclude_iCCP != MagickFalse)
12526       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12527           "    iCCP");
12528 #if 0
12529     if (mng_info->ping_exclude_iTXt != MagickFalse)
12530       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12531           "    iTXt");
12532 #endif
12533
12534     if (mng_info->ping_exclude_oFFs != MagickFalse)
12535       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12536           "    oFFs");
12537     if (mng_info->ping_exclude_pHYs != MagickFalse)
12538       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12539           "    pHYs");
12540     if (mng_info->ping_exclude_sRGB != MagickFalse)
12541       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12542           "    sRGB");
12543     if (mng_info->ping_exclude_tEXt != MagickFalse)
12544       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12545           "    tEXt");
12546     if (mng_info->ping_exclude_tIME != MagickFalse)
12547       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12548           "    tIME");
12549     if (mng_info->ping_exclude_tRNS != MagickFalse)
12550       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12551           "    tRNS");
12552     if (mng_info->ping_exclude_zCCP != MagickFalse)
12553       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12554           "    zCCP");
12555     if (mng_info->ping_exclude_zTXt != MagickFalse)
12556       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12557           "    zTXt");
12558   }
12559
12560   mng_info->need_blob = MagickTrue;
12561
12562   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12563
12564   mng_info=MngInfoFreeStruct(mng_info);
12565
12566   if (logging != MagickFalse)
12567     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12568
12569   return(status);
12570 }
12571
12572 #if defined(JNG_SUPPORTED)
12573
12574 /* Write one JNG image */
12575 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12576    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12577 {
12578   Image
12579     *jpeg_image;
12580
12581   ImageInfo
12582     *jpeg_image_info;
12583
12584   MagickBooleanType
12585     logging,
12586     status;
12587
12588   size_t
12589     length;
12590
12591   unsigned char
12592     *blob,
12593     chunk[80],
12594     *p;
12595
12596   unsigned int
12597     jng_alpha_compression_method,
12598     jng_alpha_sample_depth,
12599     jng_color_type,
12600     transparent;
12601
12602   size_t
12603     jng_alpha_quality,
12604     jng_quality;
12605
12606   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12607     "  Enter WriteOneJNGImage()");
12608
12609   blob=(unsigned char *) NULL;
12610   jpeg_image=(Image *) NULL;
12611   jpeg_image_info=(ImageInfo *) NULL;
12612   length=0;
12613
12614   status=MagickTrue;
12615   transparent=image_info->type==GrayscaleAlphaType ||
12616      image_info->type==TrueColorAlphaType ||
12617      image->alpha_trait != UndefinedPixelTrait;
12618
12619   jng_alpha_sample_depth = 0;
12620
12621   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12622
12623   jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12624
12625   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12626       image_info->quality;
12627
12628   if (jng_alpha_quality >= 1000)
12629     jng_alpha_quality /= 1000;
12630
12631   length=0;
12632
12633   if (transparent != 0)
12634     {
12635       jng_color_type=14;
12636
12637       /* Create JPEG blob, image, and image_info */
12638       if (logging != MagickFalse)
12639         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12640           "  Creating jpeg_image_info for alpha.");
12641
12642       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12643
12644       if (jpeg_image_info == (ImageInfo *) NULL)
12645         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12646
12647       if (logging != MagickFalse)
12648         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12649           "  Creating jpeg_image.");
12650
12651       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12652       if (jpeg_image == (Image *) NULL)
12653         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12654       (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12655       jpeg_image->alpha_trait=UndefinedPixelTrait;
12656       jpeg_image->quality=jng_alpha_quality;
12657       jpeg_image_info->type=GrayscaleType;
12658       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12659       (void) AcquireUniqueFilename(jpeg_image->filename);
12660       (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12661         "%s",jpeg_image->filename);
12662     }
12663   else
12664     {
12665       jng_alpha_compression_method=0;
12666       jng_color_type=10;
12667       jng_alpha_sample_depth=0;
12668     }
12669
12670   /* To do: check bit depth of PNG alpha channel */
12671
12672   /* Check if image is grayscale. */
12673   if (image_info->type != TrueColorAlphaType && image_info->type !=
12674     TrueColorType && SetImageGray(image,exception))
12675     jng_color_type-=2;
12676
12677   if (logging != MagickFalse)
12678     {
12679         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12680           "    JNG Quality           = %d",(int) jng_quality);
12681         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12682           "    JNG Color Type        = %d",jng_color_type);
12683         if (transparent != 0)
12684           {
12685             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12686               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12687             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12688               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12689             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12690               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12691           }
12692     }
12693
12694   if (transparent != 0)
12695     {
12696       if (jng_alpha_compression_method==0)
12697         {
12698           const char
12699             *value;
12700
12701           /* Encode alpha as a grayscale PNG blob */
12702           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12703             exception);
12704           if (status == MagickFalse)
12705             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12706
12707           if (logging != MagickFalse)
12708             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12709               "  Creating PNG blob.");
12710
12711           (void) CopyMagickString(jpeg_image_info->magick,"PNG",
12712              MagickPathExtent);
12713           (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12714           jpeg_image_info->interlace=NoInterlace;
12715
12716           /* Exclude all ancillary chunks */
12717           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12718
12719           blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12720             &length,exception);
12721
12722           /* Retrieve sample depth used */
12723           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12724           if (value != (char *) NULL)
12725             jng_alpha_sample_depth= (unsigned int) value[0];
12726         }
12727       else
12728         {
12729           /* Encode alpha as a grayscale JPEG blob */
12730
12731           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12732             exception);
12733           if (status == MagickFalse)
12734             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12735
12736           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",
12737             MagickPathExtent);
12738           (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12739           jpeg_image_info->interlace=NoInterlace;
12740           if (logging != MagickFalse)
12741             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12742               "  Creating blob.");
12743           blob=(unsigned char *) ImageToBlob(jpeg_image_info,
12744              jpeg_image,&length,
12745            exception);
12746           jng_alpha_sample_depth=8;
12747
12748           if (logging != MagickFalse)
12749             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12750               "  Successfully read jpeg_image into a blob, length=%.20g.",
12751               (double) length);
12752
12753         }
12754       /* Destroy JPEG image and image_info */
12755       jpeg_image=DestroyImage(jpeg_image);
12756       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12757       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12758     }
12759
12760   /* Write JHDR chunk */
12761   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12762   PNGType(chunk,mng_JHDR);
12763   LogPNGChunk(logging,mng_JHDR,16L);
12764   PNGLong(chunk+4,(png_uint_32) image->columns);
12765   PNGLong(chunk+8,(png_uint_32) image->rows);
12766   chunk[12]=jng_color_type;
12767   chunk[13]=8;  /* sample depth */
12768   chunk[14]=8; /*jng_image_compression_method */
12769   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12770   chunk[16]=jng_alpha_sample_depth;
12771   chunk[17]=jng_alpha_compression_method;
12772   chunk[18]=0; /*jng_alpha_filter_method */
12773   chunk[19]=0; /*jng_alpha_interlace_method */
12774   (void) WriteBlob(image,20,chunk);
12775   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12776   if (logging != MagickFalse)
12777     {
12778       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12779         "    JNG width:%15lu",(unsigned long) image->columns);
12780
12781       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12782         "    JNG height:%14lu",(unsigned long) image->rows);
12783
12784       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12785         "    JNG color type:%10d",jng_color_type);
12786
12787       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12788         "    JNG sample depth:%8d",8);
12789
12790       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12791         "    JNG compression:%9d",8);
12792
12793       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12794         "    JNG interlace:%11d",0);
12795
12796       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12797         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
12798
12799       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12800         "    JNG alpha compression:%3d",jng_alpha_compression_method);
12801
12802       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12803         "    JNG alpha filter:%8d",0);
12804
12805       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12806         "    JNG alpha interlace:%5d",0);
12807     }
12808
12809   /*
12810      Write leading ancillary chunks
12811   */
12812
12813   if (transparent != 0)
12814   {
12815     /*
12816       Write JNG bKGD chunk
12817     */
12818
12819     unsigned char
12820       blue,
12821       green,
12822       red;
12823
12824     ssize_t
12825       num_bytes;
12826
12827     if (jng_color_type == 8 || jng_color_type == 12)
12828       num_bytes=6L;
12829     else
12830       num_bytes=10L;
12831     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12832     PNGType(chunk,mng_bKGD);
12833     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12834     red=ScaleQuantumToChar(image->background_color.red);
12835     green=ScaleQuantumToChar(image->background_color.green);
12836     blue=ScaleQuantumToChar(image->background_color.blue);
12837     *(chunk+4)=0;
12838     *(chunk+5)=red;
12839     *(chunk+6)=0;
12840     *(chunk+7)=green;
12841     *(chunk+8)=0;
12842     *(chunk+9)=blue;
12843     (void) WriteBlob(image,(size_t) num_bytes,chunk);
12844     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12845   }
12846
12847   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12848     {
12849       /*
12850         Write JNG sRGB chunk
12851       */
12852       (void) WriteBlobMSBULong(image,1L);
12853       PNGType(chunk,mng_sRGB);
12854       LogPNGChunk(logging,mng_sRGB,1L);
12855
12856       if (image->rendering_intent != UndefinedIntent)
12857         chunk[4]=(unsigned char)
12858           Magick_RenderingIntent_to_PNG_RenderingIntent(
12859           (image->rendering_intent));
12860
12861       else
12862         chunk[4]=(unsigned char)
12863           Magick_RenderingIntent_to_PNG_RenderingIntent(
12864           (PerceptualIntent));
12865
12866       (void) WriteBlob(image,5,chunk);
12867       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12868     }
12869   else
12870     {
12871       if (image->gamma != 0.0)
12872         {
12873           /*
12874              Write JNG gAMA chunk
12875           */
12876           (void) WriteBlobMSBULong(image,4L);
12877           PNGType(chunk,mng_gAMA);
12878           LogPNGChunk(logging,mng_gAMA,4L);
12879           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12880           (void) WriteBlob(image,8,chunk);
12881           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12882         }
12883
12884       if ((mng_info->equal_chrms == MagickFalse) &&
12885           (image->chromaticity.red_primary.x != 0.0))
12886         {
12887           PrimaryInfo
12888             primary;
12889
12890           /*
12891              Write JNG cHRM chunk
12892           */
12893           (void) WriteBlobMSBULong(image,32L);
12894           PNGType(chunk,mng_cHRM);
12895           LogPNGChunk(logging,mng_cHRM,32L);
12896           primary=image->chromaticity.white_point;
12897           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12898           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12899           primary=image->chromaticity.red_primary;
12900           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12901           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12902           primary=image->chromaticity.green_primary;
12903           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12904           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12905           primary=image->chromaticity.blue_primary;
12906           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12907           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12908           (void) WriteBlob(image,36,chunk);
12909           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12910         }
12911     }
12912
12913   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12914     {
12915       /*
12916          Write JNG pHYs chunk
12917       */
12918       (void) WriteBlobMSBULong(image,9L);
12919       PNGType(chunk,mng_pHYs);
12920       LogPNGChunk(logging,mng_pHYs,9L);
12921       if (image->units == PixelsPerInchResolution)
12922         {
12923           PNGLong(chunk+4,(png_uint_32)
12924             (image->resolution.x*100.0/2.54+0.5));
12925
12926           PNGLong(chunk+8,(png_uint_32)
12927             (image->resolution.y*100.0/2.54+0.5));
12928
12929           chunk[12]=1;
12930         }
12931
12932       else
12933         {
12934           if (image->units == PixelsPerCentimeterResolution)
12935             {
12936               PNGLong(chunk+4,(png_uint_32)
12937                 (image->resolution.x*100.0+0.5));
12938
12939               PNGLong(chunk+8,(png_uint_32)
12940                 (image->resolution.y*100.0+0.5));
12941
12942               chunk[12]=1;
12943             }
12944
12945           else
12946             {
12947               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12948               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12949               chunk[12]=0;
12950             }
12951         }
12952       (void) WriteBlob(image,13,chunk);
12953       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12954     }
12955
12956   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12957     {
12958       /*
12959          Write JNG oFFs chunk
12960       */
12961       (void) WriteBlobMSBULong(image,9L);
12962       PNGType(chunk,mng_oFFs);
12963       LogPNGChunk(logging,mng_oFFs,9L);
12964       PNGsLong(chunk+4,(ssize_t) (image->page.x));
12965       PNGsLong(chunk+8,(ssize_t) (image->page.y));
12966       chunk[12]=0;
12967       (void) WriteBlob(image,13,chunk);
12968       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12969     }
12970
12971   if (transparent != 0)
12972     {
12973       if (jng_alpha_compression_method==0)
12974         {
12975           register ssize_t
12976             i;
12977
12978           size_t
12979             len;
12980
12981           /* Write IDAT chunk header */
12982           if (logging != MagickFalse)
12983             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12984               "  Write IDAT chunks from blob, length=%.20g.",(double)
12985               length);
12986
12987           /* Copy IDAT chunks */
12988           len=0;
12989           p=blob+8;
12990           for (i=8; i<(ssize_t) length; i+=len+12)
12991           {
12992             len=(((unsigned int) *(p    ) & 0xff) << 24) +
12993                 (((unsigned int) *(p + 1) & 0xff) << 16) +
12994                 (((unsigned int) *(p + 2) & 0xff) <<  8) +
12995                 (((unsigned int) *(p + 3) & 0xff)      ) ;
12996             p+=4;
12997
12998             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12999               {
13000                 /* Found an IDAT chunk. */
13001                 (void) WriteBlobMSBULong(image,len);
13002                 LogPNGChunk(logging,mng_IDAT,len);
13003                 (void) WriteBlob(image,len+4,p);
13004                 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
13005               }
13006
13007             else
13008               {
13009                 if (logging != MagickFalse)
13010                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13011                     "    Skipping %c%c%c%c chunk, length=%.20g.",
13012                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
13013               }
13014             p+=(8+len);
13015           }
13016         }
13017       else if (length != 0)
13018         {
13019           /* Write JDAA chunk header */
13020           if (logging != MagickFalse)
13021             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13022               "  Write JDAA chunk, length=%.20g.",(double) length);
13023           (void) WriteBlobMSBULong(image,(size_t) length);
13024           PNGType(chunk,mng_JDAA);
13025           LogPNGChunk(logging,mng_JDAA,length);
13026           /* Write JDAT chunk(s) data */
13027           (void) WriteBlob(image,4,chunk);
13028           (void) WriteBlob(image,length,blob);
13029           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
13030              (uInt) length));
13031         }
13032       blob=(unsigned char *) RelinquishMagickMemory(blob);
13033     }
13034
13035   /* Encode image as a JPEG blob */
13036   if (logging != MagickFalse)
13037     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13038       "  Creating jpeg_image_info.");
13039   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
13040   if (jpeg_image_info == (ImageInfo *) NULL)
13041     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13042
13043   if (logging != MagickFalse)
13044     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13045       "  Creating jpeg_image.");
13046
13047   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
13048   if (jpeg_image == (Image *) NULL)
13049     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13050   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13051
13052   (void) AcquireUniqueFilename(jpeg_image->filename);
13053   (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
13054     jpeg_image->filename);
13055
13056   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
13057     exception);
13058
13059   if (logging != MagickFalse)
13060     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13061       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
13062       (double) jpeg_image->rows);
13063
13064   if (status == MagickFalse)
13065     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13066
13067   if (jng_color_type == 8 || jng_color_type == 12)
13068     jpeg_image_info->type=GrayscaleType;
13069
13070   jpeg_image_info->quality=jng_quality;
13071   jpeg_image->quality=jng_quality;
13072   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
13073   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13074
13075   if (logging != MagickFalse)
13076     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13077       "  Creating blob.");
13078
13079   blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
13080     exception);
13081
13082   if (logging != MagickFalse)
13083     {
13084       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13085         "  Successfully read jpeg_image into a blob, length=%.20g.",
13086         (double) length);
13087
13088       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13089         "  Write JDAT chunk, length=%.20g.",(double) length);
13090     }
13091
13092   /* Write JDAT chunk(s) */
13093   (void) WriteBlobMSBULong(image,(size_t) length);
13094   PNGType(chunk,mng_JDAT);
13095   LogPNGChunk(logging,mng_JDAT,length);
13096   (void) WriteBlob(image,4,chunk);
13097   (void) WriteBlob(image,length,blob);
13098   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
13099
13100   jpeg_image=DestroyImage(jpeg_image);
13101   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
13102   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13103   blob=(unsigned char *) RelinquishMagickMemory(blob);
13104
13105   /* Write IEND chunk */
13106   (void) WriteBlobMSBULong(image,0L);
13107   PNGType(chunk,mng_IEND);
13108   LogPNGChunk(logging,mng_IEND,0);
13109   (void) WriteBlob(image,4,chunk);
13110   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13111
13112   if (logging != MagickFalse)
13113     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13114       "  exit WriteOneJNGImage()");
13115
13116   return(status);
13117 }
13118
13119 /*
13120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13121 %                                                                             %
13122 %                                                                             %
13123 %                                                                             %
13124 %   W r i t e J N G I m a g e                                                 %
13125 %                                                                             %
13126 %                                                                             %
13127 %                                                                             %
13128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13129 %
13130 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
13131 %
13132 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
13133 %
13134 %  The format of the WriteJNGImage method is:
13135 %
13136 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13137 %        Image *image,ExceptionInfo *exception)
13138 %
13139 %  A description of each parameter follows:
13140 %
13141 %    o image_info: the image info.
13142 %
13143 %    o image:  The image.
13144 %
13145 %    o exception: return any errors or warnings in this structure.
13146 %
13147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13148 */
13149 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13150   Image *image, ExceptionInfo *exception)
13151 {
13152   MagickBooleanType
13153     logging,
13154     status;
13155
13156   MngInfo
13157     *mng_info;
13158
13159   /*
13160     Open image file.
13161   */
13162   assert(image_info != (const ImageInfo *) NULL);
13163   assert(image_info->signature == MagickCoreSignature);
13164   assert(image != (Image *) NULL);
13165   assert(image->signature == MagickCoreSignature);
13166   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13167   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
13168   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13169   if (status == MagickFalse)
13170     return(status);
13171   if ((image->columns > 65535UL) || (image->rows > 65535UL))
13172     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
13173
13174   /*
13175     Allocate a MngInfo structure.
13176   */
13177   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13178   if (mng_info == (MngInfo *) NULL)
13179     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13180   /*
13181     Initialize members of the MngInfo structure.
13182   */
13183   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13184   mng_info->image=image;
13185
13186   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
13187
13188   status=WriteOneJNGImage(mng_info,image_info,image,exception);
13189   mng_info=MngInfoFreeStruct(mng_info);
13190   (void) CloseBlob(image);
13191
13192   (void) CatchImageException(image);
13193   if (logging != MagickFalse)
13194     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13195   return(status);
13196 }
13197 #endif
13198
13199 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
13200   Image *image, ExceptionInfo *exception)
13201 {
13202   Image
13203     *next_image;
13204
13205   MagickBooleanType
13206     status;
13207
13208   volatile MagickBooleanType
13209     logging;
13210
13211   MngInfo
13212     *mng_info;
13213
13214   int
13215     image_count,
13216     need_iterations,
13217     need_matte;
13218
13219   volatile int
13220 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13221     defined(PNG_MNG_FEATURES_SUPPORTED)
13222     need_local_plte,
13223 #endif
13224     all_images_are_gray,
13225     need_defi,
13226     use_global_plte;
13227
13228   register ssize_t
13229     i;
13230
13231   unsigned char
13232     chunk[800];
13233
13234   volatile unsigned int
13235     write_jng,
13236     write_mng;
13237
13238   volatile size_t
13239     scene;
13240
13241   size_t
13242     final_delay=0,
13243     initial_delay;
13244
13245 #if (PNG_LIBPNG_VER < 10200)
13246     if (image_info->verbose)
13247       printf("Your PNG library (libpng-%s) is rather old.\n",
13248          PNG_LIBPNG_VER_STRING);
13249 #endif
13250
13251   /*
13252     Open image file.
13253   */
13254   assert(image_info != (const ImageInfo *) NULL);
13255   assert(image_info->signature == MagickCoreSignature);
13256   assert(image != (Image *) NULL);
13257   assert(image->signature == MagickCoreSignature);
13258   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13259   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13260   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13261   if (status == MagickFalse)
13262     return(status);
13263
13264   /*
13265     Allocate a MngInfo structure.
13266   */
13267   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13268   if (mng_info == (MngInfo *) NULL)
13269     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13270   /*
13271     Initialize members of the MngInfo structure.
13272   */
13273   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13274   mng_info->image=image;
13275   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13276
13277   /*
13278    * See if user has requested a specific PNG subformat to be used
13279    * for all of the PNGs in the MNG being written, e.g.,
13280    *
13281    *    convert *.png png8:animation.mng
13282    *
13283    * To do: check -define png:bit_depth and png:color_type as well,
13284    * or perhaps use mng:bit_depth and mng:color_type instead for
13285    * global settings.
13286    */
13287
13288   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13289   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13290   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13291
13292   write_jng=MagickFalse;
13293   if (image_info->compression == JPEGCompression)
13294     write_jng=MagickTrue;
13295
13296   mng_info->adjoin=image_info->adjoin &&
13297     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13298
13299   if (logging != MagickFalse)
13300     {
13301       /* Log some info about the input */
13302       Image
13303         *p;
13304
13305       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13306         "  Checking input image(s)\n"
13307         "    Image_info depth: %.20g,    Type: %d",
13308         (double) image_info->depth, image_info->type);
13309
13310       scene=0;
13311       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13312       {
13313
13314         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13315            "    Scene: %.20g\n,   Image depth: %.20g",
13316            (double) scene++, (double) p->depth);
13317
13318         if (p->alpha_trait != UndefinedPixelTrait)
13319           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13320             "      Matte: True");
13321
13322         else
13323           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13324             "      Matte: False");
13325
13326         if (p->storage_class == PseudoClass)
13327           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13328             "      Storage class: PseudoClass");
13329
13330         else
13331           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13332             "      Storage class: DirectClass");
13333
13334         if (p->colors)
13335           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13336             "      Number of colors: %.20g",(double) p->colors);
13337
13338         else
13339           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13340             "      Number of colors: unspecified");
13341
13342         if (mng_info->adjoin == MagickFalse)
13343           break;
13344       }
13345     }
13346
13347   use_global_plte=MagickFalse;
13348   all_images_are_gray=MagickFalse;
13349 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13350   need_local_plte=MagickTrue;
13351 #endif
13352   need_defi=MagickFalse;
13353   need_matte=MagickFalse;
13354   mng_info->framing_mode=1;
13355   mng_info->old_framing_mode=1;
13356
13357   if (write_mng)
13358       if (image_info->page != (char *) NULL)
13359         {
13360           /*
13361             Determine image bounding box.
13362           */
13363           SetGeometry(image,&mng_info->page);
13364           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13365             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13366         }
13367   if (write_mng)
13368     {
13369       unsigned int
13370         need_geom;
13371
13372       unsigned short
13373         red,
13374         green,
13375         blue;
13376
13377       const char *
13378         option;
13379
13380       mng_info->page=image->page;
13381       need_geom=MagickTrue;
13382       if (mng_info->page.width || mng_info->page.height)
13383          need_geom=MagickFalse;
13384       /*
13385         Check all the scenes.
13386       */
13387       initial_delay=image->delay;
13388       need_iterations=MagickFalse;
13389       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13390       mng_info->equal_physs=MagickTrue,
13391       mng_info->equal_gammas=MagickTrue;
13392       mng_info->equal_srgbs=MagickTrue;
13393       mng_info->equal_backgrounds=MagickTrue;
13394       image_count=0;
13395 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13396     defined(PNG_MNG_FEATURES_SUPPORTED)
13397       all_images_are_gray=MagickTrue;
13398       mng_info->equal_palettes=MagickFalse;
13399       need_local_plte=MagickFalse;
13400 #endif
13401       for (next_image=image; next_image != (Image *) NULL; )
13402       {
13403         if (need_geom)
13404           {
13405             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13406               mng_info->page.width=next_image->columns+next_image->page.x;
13407
13408             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13409               mng_info->page.height=next_image->rows+next_image->page.y;
13410           }
13411
13412         if (next_image->page.x || next_image->page.y)
13413           need_defi=MagickTrue;
13414
13415         if (next_image->alpha_trait != UndefinedPixelTrait)
13416           need_matte=MagickTrue;
13417
13418         if ((int) next_image->dispose >= BackgroundDispose)
13419           if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13420                next_image->page.x || next_image->page.y ||
13421               ((next_image->columns < mng_info->page.width) &&
13422                (next_image->rows < mng_info->page.height)))
13423             mng_info->need_fram=MagickTrue;
13424
13425         if (next_image->iterations)
13426           need_iterations=MagickTrue;
13427
13428         final_delay=next_image->delay;
13429
13430         if (final_delay != initial_delay || final_delay > 1UL*
13431            next_image->ticks_per_second)
13432           mng_info->need_fram=1;
13433
13434 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13435     defined(PNG_MNG_FEATURES_SUPPORTED)
13436         /*
13437           check for global palette possibility.
13438         */
13439         if (image->alpha_trait != UndefinedPixelTrait)
13440            need_local_plte=MagickTrue;
13441
13442         if (need_local_plte == 0)
13443           {
13444             if (SetImageGray(image,exception) == MagickFalse)
13445               all_images_are_gray=MagickFalse;
13446             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13447             if (use_global_plte == 0)
13448               use_global_plte=mng_info->equal_palettes;
13449             need_local_plte=!mng_info->equal_palettes;
13450           }
13451 #endif
13452         if (GetNextImageInList(next_image) != (Image *) NULL)
13453           {
13454             if (next_image->background_color.red !=
13455                 next_image->next->background_color.red ||
13456                 next_image->background_color.green !=
13457                 next_image->next->background_color.green ||
13458                 next_image->background_color.blue !=
13459                 next_image->next->background_color.blue)
13460               mng_info->equal_backgrounds=MagickFalse;
13461
13462             if (next_image->gamma != next_image->next->gamma)
13463               mng_info->equal_gammas=MagickFalse;
13464
13465             if (next_image->rendering_intent !=
13466                 next_image->next->rendering_intent)
13467               mng_info->equal_srgbs=MagickFalse;
13468
13469             if ((next_image->units != next_image->next->units) ||
13470                 (next_image->resolution.x != next_image->next->resolution.x) ||
13471                 (next_image->resolution.y != next_image->next->resolution.y))
13472               mng_info->equal_physs=MagickFalse;
13473
13474             if (mng_info->equal_chrms)
13475               {
13476                 if (next_image->chromaticity.red_primary.x !=
13477                     next_image->next->chromaticity.red_primary.x ||
13478                     next_image->chromaticity.red_primary.y !=
13479                     next_image->next->chromaticity.red_primary.y ||
13480                     next_image->chromaticity.green_primary.x !=
13481                     next_image->next->chromaticity.green_primary.x ||
13482                     next_image->chromaticity.green_primary.y !=
13483                     next_image->next->chromaticity.green_primary.y ||
13484                     next_image->chromaticity.blue_primary.x !=
13485                     next_image->next->chromaticity.blue_primary.x ||
13486                     next_image->chromaticity.blue_primary.y !=
13487                     next_image->next->chromaticity.blue_primary.y ||
13488                     next_image->chromaticity.white_point.x !=
13489                     next_image->next->chromaticity.white_point.x ||
13490                     next_image->chromaticity.white_point.y !=
13491                     next_image->next->chromaticity.white_point.y)
13492                   mng_info->equal_chrms=MagickFalse;
13493               }
13494           }
13495         image_count++;
13496         next_image=GetNextImageInList(next_image);
13497       }
13498       if (image_count < 2)
13499         {
13500           mng_info->equal_backgrounds=MagickFalse;
13501           mng_info->equal_chrms=MagickFalse;
13502           mng_info->equal_gammas=MagickFalse;
13503           mng_info->equal_srgbs=MagickFalse;
13504           mng_info->equal_physs=MagickFalse;
13505           use_global_plte=MagickFalse;
13506 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13507           need_local_plte=MagickTrue;
13508 #endif
13509           need_iterations=MagickFalse;
13510         }
13511
13512      if (mng_info->need_fram == MagickFalse)
13513        {
13514          /*
13515            Only certain framing rates 100/n are exactly representable without
13516            the FRAM chunk but we'll allow some slop in VLC files
13517          */
13518          if (final_delay == 0)
13519            {
13520              if (need_iterations != MagickFalse)
13521                {
13522                  /*
13523                    It's probably a GIF with loop; don't run it *too* fast.
13524                  */
13525                  if (mng_info->adjoin)
13526                    {
13527                      final_delay=10;
13528                      (void) ThrowMagickException(exception,GetMagickModule(),
13529                        CoderWarning,
13530                        "input has zero delay between all frames; assuming",
13531                        " 10 cs `%s'","");
13532                    }
13533                }
13534              else
13535                mng_info->ticks_per_second=0;
13536            }
13537          if (final_delay != 0)
13538            mng_info->ticks_per_second=(png_uint_32)
13539               (image->ticks_per_second/final_delay);
13540          if (final_delay > 50)
13541            mng_info->ticks_per_second=2;
13542
13543          if (final_delay > 75)
13544            mng_info->ticks_per_second=1;
13545
13546          if (final_delay > 125)
13547            mng_info->need_fram=MagickTrue;
13548
13549          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13550             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13551             (final_delay != 25) && (final_delay != 50) &&
13552             (final_delay != (size_t) image->ticks_per_second))
13553            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13554        }
13555
13556      if (mng_info->need_fram != MagickFalse)
13557         mng_info->ticks_per_second=image->ticks_per_second;
13558      /*
13559         If pseudocolor, we should also check to see if all the
13560         palettes are identical and write a global PLTE if they are.
13561         ../glennrp Feb 99.
13562      */
13563      /*
13564         Write the MNG version 1.0 signature and MHDR chunk.
13565      */
13566      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13567      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13568      PNGType(chunk,mng_MHDR);
13569      LogPNGChunk(logging,mng_MHDR,28L);
13570      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13571      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13572      PNGLong(chunk+12,mng_info->ticks_per_second);
13573      PNGLong(chunk+16,0L);  /* layer count=unknown */
13574      PNGLong(chunk+20,0L);  /* frame count=unknown */
13575      PNGLong(chunk+24,0L);  /* play time=unknown   */
13576      if (write_jng)
13577        {
13578          if (need_matte)
13579            {
13580              if (need_defi || mng_info->need_fram || use_global_plte)
13581                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13582
13583              else
13584                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13585            }
13586
13587          else
13588            {
13589              if (need_defi || mng_info->need_fram || use_global_plte)
13590                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13591
13592              else
13593                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13594            }
13595        }
13596
13597      else
13598        {
13599          if (need_matte)
13600            {
13601              if (need_defi || mng_info->need_fram || use_global_plte)
13602                PNGLong(chunk+28,11L);    /* simplicity=LC */
13603
13604              else
13605                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13606            }
13607
13608          else
13609            {
13610              if (need_defi || mng_info->need_fram || use_global_plte)
13611                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13612
13613              else
13614                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13615            }
13616        }
13617      (void) WriteBlob(image,32,chunk);
13618      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13619      option=GetImageOption(image_info,"mng:need-cacheoff");
13620      if (option != (const char *) NULL)
13621        {
13622          size_t
13623            length;
13624          /*
13625            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13626          */
13627          PNGType(chunk,mng_nEED);
13628          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13629          (void) WriteBlobMSBULong(image,(size_t) length);
13630          LogPNGChunk(logging,mng_nEED,(size_t) length);
13631          length+=4;
13632          (void) WriteBlob(image,length,chunk);
13633          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13634        }
13635      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13636          (GetNextImageInList(image) != (Image *) NULL) &&
13637          (image->iterations != 1))
13638        {
13639          /*
13640            Write MNG TERM chunk
13641          */
13642          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13643          PNGType(chunk,mng_TERM);
13644          LogPNGChunk(logging,mng_TERM,10L);
13645          chunk[4]=3;  /* repeat animation */
13646          chunk[5]=0;  /* show last frame when done */
13647          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13648             final_delay/MagickMax(image->ticks_per_second,1)));
13649
13650          if (image->iterations == 0)
13651            PNGLong(chunk+10,PNG_UINT_31_MAX);
13652
13653          else
13654            PNGLong(chunk+10,(png_uint_32) image->iterations);
13655
13656          if (logging != MagickFalse)
13657            {
13658              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13659                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13660               final_delay/MagickMax(image->ticks_per_second,1)));
13661
13662              if (image->iterations == 0)
13663                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13664                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13665
13666              else
13667                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13668                  "     Image iterations: %.20g",(double) image->iterations);
13669            }
13670          (void) WriteBlob(image,14,chunk);
13671          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13672        }
13673      /*
13674        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13675      */
13676      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13677           mng_info->equal_srgbs)
13678        {
13679          /*
13680            Write MNG sRGB chunk
13681          */
13682          (void) WriteBlobMSBULong(image,1L);
13683          PNGType(chunk,mng_sRGB);
13684          LogPNGChunk(logging,mng_sRGB,1L);
13685
13686          if (image->rendering_intent != UndefinedIntent)
13687            chunk[4]=(unsigned char)
13688              Magick_RenderingIntent_to_PNG_RenderingIntent(
13689              (image->rendering_intent));
13690
13691          else
13692            chunk[4]=(unsigned char)
13693              Magick_RenderingIntent_to_PNG_RenderingIntent(
13694                (PerceptualIntent));
13695
13696          (void) WriteBlob(image,5,chunk);
13697          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13698          mng_info->have_write_global_srgb=MagickTrue;
13699        }
13700
13701      else
13702        {
13703          if (image->gamma && mng_info->equal_gammas)
13704            {
13705              /*
13706                 Write MNG gAMA chunk
13707              */
13708              (void) WriteBlobMSBULong(image,4L);
13709              PNGType(chunk,mng_gAMA);
13710              LogPNGChunk(logging,mng_gAMA,4L);
13711              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13712              (void) WriteBlob(image,8,chunk);
13713              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13714              mng_info->have_write_global_gama=MagickTrue;
13715            }
13716          if (mng_info->equal_chrms)
13717            {
13718              PrimaryInfo
13719                primary;
13720
13721              /*
13722                 Write MNG cHRM chunk
13723              */
13724              (void) WriteBlobMSBULong(image,32L);
13725              PNGType(chunk,mng_cHRM);
13726              LogPNGChunk(logging,mng_cHRM,32L);
13727              primary=image->chromaticity.white_point;
13728              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13729              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13730              primary=image->chromaticity.red_primary;
13731              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13732              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13733              primary=image->chromaticity.green_primary;
13734              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13735              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13736              primary=image->chromaticity.blue_primary;
13737              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13738              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13739              (void) WriteBlob(image,36,chunk);
13740              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13741              mng_info->have_write_global_chrm=MagickTrue;
13742            }
13743        }
13744      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13745        {
13746          /*
13747             Write MNG pHYs chunk
13748          */
13749          (void) WriteBlobMSBULong(image,9L);
13750          PNGType(chunk,mng_pHYs);
13751          LogPNGChunk(logging,mng_pHYs,9L);
13752
13753          if (image->units == PixelsPerInchResolution)
13754            {
13755              PNGLong(chunk+4,(png_uint_32)
13756                (image->resolution.x*100.0/2.54+0.5));
13757
13758              PNGLong(chunk+8,(png_uint_32)
13759                (image->resolution.y*100.0/2.54+0.5));
13760
13761              chunk[12]=1;
13762            }
13763
13764          else
13765            {
13766              if (image->units == PixelsPerCentimeterResolution)
13767                {
13768                  PNGLong(chunk+4,(png_uint_32)
13769                    (image->resolution.x*100.0+0.5));
13770
13771                  PNGLong(chunk+8,(png_uint_32)
13772                    (image->resolution.y*100.0+0.5));
13773
13774                  chunk[12]=1;
13775                }
13776
13777              else
13778                {
13779                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13780                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13781                  chunk[12]=0;
13782                }
13783            }
13784          (void) WriteBlob(image,13,chunk);
13785          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13786        }
13787      /*
13788        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13789        or does not cover the entire frame.
13790      */
13791      if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13792          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13793          (image->page.width+image->page.x < mng_info->page.width))
13794          || (image->page.height && (image->page.height+image->page.y
13795          < mng_info->page.height))))
13796        {
13797          (void) WriteBlobMSBULong(image,6L);
13798          PNGType(chunk,mng_BACK);
13799          LogPNGChunk(logging,mng_BACK,6L);
13800          red=ScaleQuantumToShort(image->background_color.red);
13801          green=ScaleQuantumToShort(image->background_color.green);
13802          blue=ScaleQuantumToShort(image->background_color.blue);
13803          PNGShort(chunk+4,red);
13804          PNGShort(chunk+6,green);
13805          PNGShort(chunk+8,blue);
13806          (void) WriteBlob(image,10,chunk);
13807          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13808          if (mng_info->equal_backgrounds)
13809            {
13810              (void) WriteBlobMSBULong(image,6L);
13811              PNGType(chunk,mng_bKGD);
13812              LogPNGChunk(logging,mng_bKGD,6L);
13813              (void) WriteBlob(image,10,chunk);
13814              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13815            }
13816        }
13817
13818 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13819      if ((need_local_plte == MagickFalse) &&
13820          (image->storage_class == PseudoClass) &&
13821          (all_images_are_gray == MagickFalse))
13822        {
13823          size_t
13824            data_length;
13825
13826          /*
13827            Write MNG PLTE chunk
13828          */
13829          data_length=3*image->colors;
13830          (void) WriteBlobMSBULong(image,data_length);
13831          PNGType(chunk,mng_PLTE);
13832          LogPNGChunk(logging,mng_PLTE,data_length);
13833
13834          for (i=0; i < (ssize_t) image->colors; i++)
13835          {
13836            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13837              image->colormap[i].red) & 0xff);
13838            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13839              image->colormap[i].green) & 0xff);
13840            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13841              image->colormap[i].blue) & 0xff);
13842          }
13843
13844          (void) WriteBlob(image,data_length+4,chunk);
13845          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13846          mng_info->have_write_global_plte=MagickTrue;
13847        }
13848 #endif
13849     }
13850   scene=0;
13851   mng_info->delay=0;
13852 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13853     defined(PNG_MNG_FEATURES_SUPPORTED)
13854   mng_info->equal_palettes=MagickFalse;
13855 #endif
13856   do
13857   {
13858     if (mng_info->adjoin)
13859     {
13860 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13861     defined(PNG_MNG_FEATURES_SUPPORTED)
13862     /*
13863       If we aren't using a global palette for the entire MNG, check to
13864       see if we can use one for two or more consecutive images.
13865     */
13866     if (need_local_plte && use_global_plte && !all_images_are_gray)
13867       {
13868         if (mng_info->IsPalette)
13869           {
13870             /*
13871               When equal_palettes is true, this image has the same palette
13872               as the previous PseudoClass image
13873             */
13874             mng_info->have_write_global_plte=mng_info->equal_palettes;
13875             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13876             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13877               {
13878                 /*
13879                   Write MNG PLTE chunk
13880                 */
13881                 size_t
13882                   data_length;
13883
13884                 data_length=3*image->colors;
13885                 (void) WriteBlobMSBULong(image,data_length);
13886                 PNGType(chunk,mng_PLTE);
13887                 LogPNGChunk(logging,mng_PLTE,data_length);
13888
13889                 for (i=0; i < (ssize_t) image->colors; i++)
13890                 {
13891                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13892                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13893                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13894                 }
13895
13896                 (void) WriteBlob(image,data_length+4,chunk);
13897                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13898                    (uInt) (data_length+4)));
13899                 mng_info->have_write_global_plte=MagickTrue;
13900               }
13901           }
13902         else
13903           mng_info->have_write_global_plte=MagickFalse;
13904       }
13905 #endif
13906     if (need_defi)
13907       {
13908         ssize_t
13909           previous_x,
13910           previous_y;
13911
13912         if (scene)
13913           {
13914             previous_x=mng_info->page.x;
13915             previous_y=mng_info->page.y;
13916           }
13917         else
13918           {
13919             previous_x=0;
13920             previous_y=0;
13921           }
13922         mng_info->page=image->page;
13923         if ((mng_info->page.x !=  previous_x) ||
13924             (mng_info->page.y != previous_y))
13925           {
13926              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
13927              PNGType(chunk,mng_DEFI);
13928              LogPNGChunk(logging,mng_DEFI,12L);
13929              chunk[4]=0; /* object 0 MSB */
13930              chunk[5]=0; /* object 0 LSB */
13931              chunk[6]=0; /* visible  */
13932              chunk[7]=0; /* abstract */
13933              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13934              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13935              (void) WriteBlob(image,16,chunk);
13936              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13937           }
13938       }
13939     }
13940
13941    mng_info->write_mng=write_mng;
13942
13943    if ((int) image->dispose >= 3)
13944      mng_info->framing_mode=3;
13945
13946    if (mng_info->need_fram && mng_info->adjoin &&
13947        ((image->delay != mng_info->delay) ||
13948         (mng_info->framing_mode != mng_info->old_framing_mode)))
13949      {
13950        if (image->delay == mng_info->delay)
13951          {
13952            /*
13953              Write a MNG FRAM chunk with the new framing mode.
13954            */
13955            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
13956            PNGType(chunk,mng_FRAM);
13957            LogPNGChunk(logging,mng_FRAM,1L);
13958            chunk[4]=(unsigned char) mng_info->framing_mode;
13959            (void) WriteBlob(image,5,chunk);
13960            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13961          }
13962        else
13963          {
13964            /*
13965              Write a MNG FRAM chunk with the delay.
13966            */
13967            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13968            PNGType(chunk,mng_FRAM);
13969            LogPNGChunk(logging,mng_FRAM,10L);
13970            chunk[4]=(unsigned char) mng_info->framing_mode;
13971            chunk[5]=0;  /* frame name separator (no name) */
13972            chunk[6]=2;  /* flag for changing default delay */
13973            chunk[7]=0;  /* flag for changing frame timeout */
13974            chunk[8]=0;  /* flag for changing frame clipping */
13975            chunk[9]=0;  /* flag for changing frame sync_id */
13976            PNGLong(chunk+10,(png_uint_32)
13977              ((mng_info->ticks_per_second*
13978              image->delay)/MagickMax(image->ticks_per_second,1)));
13979            (void) WriteBlob(image,14,chunk);
13980            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13981            mng_info->delay=(png_uint_32) image->delay;
13982          }
13983        mng_info->old_framing_mode=mng_info->framing_mode;
13984      }
13985
13986 #if defined(JNG_SUPPORTED)
13987    if (image_info->compression == JPEGCompression)
13988      {
13989        ImageInfo
13990          *write_info;
13991
13992        if (logging != MagickFalse)
13993          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13994            "  Writing JNG object.");
13995        /* To do: specify the desired alpha compression method. */
13996        write_info=CloneImageInfo(image_info);
13997        write_info->compression=UndefinedCompression;
13998        status=WriteOneJNGImage(mng_info,write_info,image,exception);
13999        write_info=DestroyImageInfo(write_info);
14000      }
14001    else
14002 #endif
14003      {
14004        if (logging != MagickFalse)
14005          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14006            "  Writing PNG object.");
14007
14008        mng_info->need_blob = MagickFalse;
14009        mng_info->ping_preserve_colormap = MagickFalse;
14010
14011        /* We don't want any ancillary chunks written */
14012        mng_info->ping_exclude_bKGD=MagickTrue;
14013        mng_info->ping_exclude_caNv=MagickTrue;
14014        mng_info->ping_exclude_cHRM=MagickTrue;
14015        mng_info->ping_exclude_date=MagickTrue;
14016        mng_info->ping_exclude_EXIF=MagickTrue;
14017        mng_info->ping_exclude_gAMA=MagickTrue;
14018        mng_info->ping_exclude_iCCP=MagickTrue;
14019        /* mng_info->ping_exclude_iTXt=MagickTrue; */
14020        mng_info->ping_exclude_oFFs=MagickTrue;
14021        mng_info->ping_exclude_pHYs=MagickTrue;
14022        mng_info->ping_exclude_sRGB=MagickTrue;
14023        mng_info->ping_exclude_tEXt=MagickTrue;
14024        mng_info->ping_exclude_tRNS=MagickTrue;
14025        mng_info->ping_exclude_zCCP=MagickTrue;
14026        mng_info->ping_exclude_zTXt=MagickTrue;
14027
14028        status=WriteOnePNGImage(mng_info,image_info,image,exception);
14029      }
14030
14031     if (status == MagickFalse)
14032       {
14033         mng_info=MngInfoFreeStruct(mng_info);
14034         (void) CloseBlob(image);
14035         return(MagickFalse);
14036       }
14037     (void) CatchImageException(image);
14038     if (GetNextImageInList(image) == (Image *) NULL)
14039       break;
14040     image=SyncNextImageInList(image);
14041     status=SetImageProgress(image,SaveImagesTag,scene++,
14042       GetImageListLength(image));
14043
14044     if (status == MagickFalse)
14045       break;
14046
14047   } while (mng_info->adjoin);
14048
14049   if (write_mng)
14050     {
14051       while (GetPreviousImageInList(image) != (Image *) NULL)
14052         image=GetPreviousImageInList(image);
14053       /*
14054         Write the MEND chunk.
14055       */
14056       (void) WriteBlobMSBULong(image,0x00000000L);
14057       PNGType(chunk,mng_MEND);
14058       LogPNGChunk(logging,mng_MEND,0L);
14059       (void) WriteBlob(image,4,chunk);
14060       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
14061     }
14062   /*
14063     Relinquish resources.
14064   */
14065   (void) CloseBlob(image);
14066   mng_info=MngInfoFreeStruct(mng_info);
14067
14068   if (logging != MagickFalse)
14069     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
14070
14071   return(MagickTrue);
14072 }
14073 #else /* PNG_LIBPNG_VER > 10011 */
14074
14075 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
14076   Image *image)
14077 {
14078   (void) image;
14079   printf("Your PNG library is too old: You have libpng-%s\n",
14080      PNG_LIBPNG_VER_STRING);
14081
14082   ThrowBinaryException(CoderError,"PNG library is too old",
14083      image_info->filename);
14084 }
14085
14086 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
14087   Image *image)
14088 {
14089   return(WritePNGImage(image_info,image));
14090 }
14091 #endif /* PNG_LIBPNG_VER > 10011 */
14092 #endif