]> granicus.if.org Git - imagemagick/blob - coders/png.c
...
[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 /* After eXIf chunk has been approved:
118 #define eXIf_SUPPORTED
119 */
120
121 /* Experimental; define one or both of these:
122 #define exIf_SUPPORTED
123 #define zxIf_SUPPORTED
124 #define zxIf_write_SUPPORTED
125 */
126
127 /*
128   Features under construction.  Define these to work on them.
129 */
130 #undef MNG_OBJECT_BUFFERS
131 #undef MNG_BASI_SUPPORTED
132 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
133 #define MNG_INSERT_LAYERS   /* Troublesome, but seem to work as of 5.4.4 */
134 #if defined(MAGICKCORE_JPEG_DELEGATE)
135 #  define JNG_SUPPORTED /* Not finished as of 5.5.2.  See "To do" comments. */
136 #endif
137 #if !defined(RGBColorMatchExact)
138 #define IsPNGColorEqual(color,target) \
139        (((color).red == (target).red) && \
140         ((color).green == (target).green) && \
141         ((color).blue == (target).blue))
142 #endif
143
144 /* Table of recognized sRGB ICC profiles */
145 struct sRGB_info_struct
146 {
147     png_uint_32 len;
148     png_uint_32 crc;
149     png_byte intent;
150 };
151
152 const struct sRGB_info_struct sRGB_info[] =
153 {
154     /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
155     { 3048, 0x3b8772b9UL, 0},
156
157     /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
158     { 3052, 0x427ebb21UL, 1},
159
160     /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
161     {60988, 0x306fd8aeUL, 0},
162
163     /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
164      {60960, 0xbbef7812UL, 0},
165
166     /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
167      { 3024, 0x5d5129ceUL, 1},
168
169      /* HP-Microsoft sRGB v2 perceptual */
170      { 3144, 0x182ea552UL, 0},
171
172      /* HP-Microsoft sRGB v2 media-relative */
173      { 3144, 0xf29e526dUL, 1},
174
175      /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
176      {  524, 0xd4938c39UL, 0},
177
178      /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
179      { 3212, 0x034af5a1UL, 0},
180
181      /* Not recognized */
182      {    0, 0x00000000UL, 0},
183 };
184
185 /* Macros for left-bit-replication to ensure that pixels
186  * and PixelInfos all have the same image->depth, and for use
187  * in PNG8 quantization.
188  */
189
190 /* LBR01: Replicate top bit */
191
192 #define LBR01PacketRed(pixelpacket) \
193      (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
194         0 : QuantumRange);
195
196 #define LBR01PacketGreen(pixelpacket) \
197      (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
198         0 : QuantumRange);
199
200 #define LBR01PacketBlue(pixelpacket) \
201      (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
202         0 : QuantumRange);
203
204 #define LBR01PacketAlpha(pixelpacket) \
205      (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
206         0 : QuantumRange);
207
208 #define LBR01PacketRGB(pixelpacket) \
209         { \
210         LBR01PacketRed((pixelpacket)); \
211         LBR01PacketGreen((pixelpacket)); \
212         LBR01PacketBlue((pixelpacket)); \
213         }
214
215 #define LBR01PacketRGBO(pixelpacket) \
216         { \
217         LBR01PacketRGB((pixelpacket)); \
218         LBR01PacketAlpha((pixelpacket)); \
219         }
220
221 #define LBR01PixelRed(pixel) \
222         (SetPixelRed(image, \
223         ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
224         0 : QuantumRange,(pixel)));
225
226 #define LBR01PixelGreen(pixel) \
227         (SetPixelGreen(image, \
228         ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
229         0 : QuantumRange,(pixel)));
230
231 #define LBR01PixelBlue(pixel) \
232         (SetPixelBlue(image, \
233         ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
234         0 : QuantumRange,(pixel)));
235
236 #define LBR01PixelAlpha(pixel) \
237         (SetPixelAlpha(image, \
238         ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
239         0 : QuantumRange,(pixel)));
240
241 #define LBR01PixelRGB(pixel) \
242         { \
243         LBR01PixelRed((pixel)); \
244         LBR01PixelGreen((pixel)); \
245         LBR01PixelBlue((pixel)); \
246         }
247
248 #define LBR01PixelRGBA(pixel) \
249         { \
250         LBR01PixelRGB((pixel)); \
251         LBR01PixelAlpha((pixel)); \
252         }
253
254 /* LBR02: Replicate top 2 bits */
255
256 #define LBR02PacketRed(pixelpacket) \
257    { \
258      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
259      (pixelpacket).red=ScaleCharToQuantum( \
260        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
261    }
262 #define LBR02PacketGreen(pixelpacket) \
263    { \
264      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
265      (pixelpacket).green=ScaleCharToQuantum( \
266        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
267    }
268 #define LBR02PacketBlue(pixelpacket) \
269    { \
270      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
271      (pixelpacket).blue=ScaleCharToQuantum( \
272        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
273    }
274 #define LBR02PacketAlpha(pixelpacket) \
275    { \
276      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
277      (pixelpacket).alpha=ScaleCharToQuantum( \
278        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
279    }
280
281 #define LBR02PacketRGB(pixelpacket) \
282         { \
283         LBR02PacketRed((pixelpacket)); \
284         LBR02PacketGreen((pixelpacket)); \
285         LBR02PacketBlue((pixelpacket)); \
286         }
287
288 #define LBR02PacketRGBO(pixelpacket) \
289         { \
290         LBR02PacketRGB((pixelpacket)); \
291         LBR02PacketAlpha((pixelpacket)); \
292         }
293
294 #define LBR02PixelRed(pixel) \
295    { \
296      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
297        & 0xc0; \
298      SetPixelRed(image, ScaleCharToQuantum( \
299        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
300        (pixel)); \
301    }
302 #define LBR02PixelGreen(pixel) \
303    { \
304      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
305        & 0xc0; \
306      SetPixelGreen(image, ScaleCharToQuantum( \
307        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
308        (pixel)); \
309    }
310 #define LBR02PixelBlue(pixel) \
311    { \
312      unsigned char lbr_bits= \
313        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
314      SetPixelBlue(image, ScaleCharToQuantum( \
315        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
316        (pixel)); \
317    }
318 #define LBR02PixelAlpha(pixel) \
319    { \
320      unsigned char lbr_bits= \
321        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
322      SetPixelAlpha(image, ScaleCharToQuantum( \
323        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
324        (pixel) ); \
325    }
326
327 #define LBR02PixelRGB(pixel) \
328         { \
329         LBR02PixelRed((pixel)); \
330         LBR02PixelGreen((pixel)); \
331         LBR02PixelBlue((pixel)); \
332         }
333
334 #define LBR02PixelRGBA(pixel) \
335         { \
336         LBR02PixelRGB((pixel)); \
337         LBR02PixelAlpha((pixel)); \
338         }
339
340 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
341    PNG8 quantization) */
342
343 #define LBR03PacketRed(pixelpacket) \
344    { \
345      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
346      (pixelpacket).red=ScaleCharToQuantum( \
347        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
348    }
349 #define LBR03PacketGreen(pixelpacket) \
350    { \
351      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
352      (pixelpacket).green=ScaleCharToQuantum( \
353        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
354    }
355 #define LBR03PacketBlue(pixelpacket) \
356    { \
357      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
358      (pixelpacket).blue=ScaleCharToQuantum( \
359        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
360    }
361
362 #define LBR03PacketRGB(pixelpacket) \
363         { \
364         LBR03PacketRed((pixelpacket)); \
365         LBR03PacketGreen((pixelpacket)); \
366         LBR03PacketBlue((pixelpacket)); \
367         }
368
369 #define LBR03PixelRed(pixel) \
370    { \
371      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
372        & 0xe0; \
373      SetPixelRed(image, ScaleCharToQuantum( \
374        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
375    }
376 #define LBR03Green(pixel) \
377    { \
378      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
379        & 0xe0; \
380      SetPixelGreen(image, ScaleCharToQuantum( \
381        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
382    }
383 #define LBR03Blue(pixel) \
384    { \
385      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
386        & 0xe0; \
387      SetPixelBlue(image, ScaleCharToQuantum( \
388        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
389    }
390
391 #define LBR03RGB(pixel) \
392         { \
393         LBR03PixelRed((pixel)); \
394         LBR03Green((pixel)); \
395         LBR03Blue((pixel)); \
396         }
397
398 /* LBR04: Replicate top 4 bits */
399
400 #define LBR04PacketRed(pixelpacket) \
401    { \
402      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
403      (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
404    }
405 #define LBR04PacketGreen(pixelpacket) \
406    { \
407      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
408      (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
409    }
410 #define LBR04PacketBlue(pixelpacket) \
411    { \
412      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
413      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
414    }
415 #define LBR04PacketAlpha(pixelpacket) \
416    { \
417      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
418      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
419    }
420
421 #define LBR04PacketRGB(pixelpacket) \
422         { \
423         LBR04PacketRed((pixelpacket)); \
424         LBR04PacketGreen((pixelpacket)); \
425         LBR04PacketBlue((pixelpacket)); \
426         }
427
428 #define LBR04PacketRGBO(pixelpacket) \
429         { \
430         LBR04PacketRGB((pixelpacket)); \
431         LBR04PacketAlpha((pixelpacket)); \
432         }
433
434 #define LBR04PixelRed(pixel) \
435    { \
436      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
437        & 0xf0; \
438      SetPixelRed(image,\
439        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
440    }
441 #define LBR04PixelGreen(pixel) \
442    { \
443      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
444        & 0xf0; \
445      SetPixelGreen(image,\
446        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
447    }
448 #define LBR04PixelBlue(pixel) \
449    { \
450      unsigned char lbr_bits= \
451        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
452      SetPixelBlue(image,\
453        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
454    }
455 #define LBR04PixelAlpha(pixel) \
456    { \
457      unsigned char lbr_bits= \
458        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
459      SetPixelAlpha(image,\
460        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
461    }
462
463 #define LBR04PixelRGB(pixel) \
464         { \
465         LBR04PixelRed((pixel)); \
466         LBR04PixelGreen((pixel)); \
467         LBR04PixelBlue((pixel)); \
468         }
469
470 #define LBR04PixelRGBA(pixel) \
471         { \
472         LBR04PixelRGB((pixel)); \
473         LBR04PixelAlpha((pixel)); \
474         }
475
476 /*
477   Establish thread safety.
478   setjmp/longjmp is claimed to be safe on these platforms:
479   setjmp/longjmp is alleged to be unsafe on these platforms:
480 */
481 #ifdef PNG_SETJMP_SUPPORTED
482 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
483 #   define IMPNG_SETJMP_NOT_THREAD_SAFE
484 # endif
485
486 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
487 static SemaphoreInfo
488   *ping_semaphore = (SemaphoreInfo *) NULL;
489 # endif
490 #endif
491
492 /*
493   This temporary until I set up malloc'ed object attributes array.
494   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
495   waste more memory.
496 */
497 #define MNG_MAX_OBJECTS 256
498
499 /*
500   If this not defined, spec is interpreted strictly.  If it is
501   defined, an attempt will be made to recover from some errors,
502   including
503       o global PLTE too short
504 */
505 #undef MNG_LOOSE
506
507 /*
508   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
509   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
510   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
511   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
512   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
513   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
514   will be enabled by default in libpng-1.2.0.
515 */
516 #ifdef PNG_MNG_FEATURES_SUPPORTED
517 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
518 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
519 #  endif
520 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
521 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
522 #  endif
523 #endif
524
525 /*
526   Maximum valid size_t in PNG/MNG chunks is (2^31)-1
527   This macro is only defined in libpng-1.0.3 and later.
528   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
529 */
530 #ifndef PNG_UINT_31_MAX
531 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
532 #endif
533
534 /*
535   Constant strings for known chunk types.  If you need to add a chunk,
536   add a string holding the name here.   To make the code more
537   portable, we use ASCII numbers like this, not characters.
538 */
539
540 /* until registration of eXIf */
541 static const png_byte mng_exIf[5]={101, 120,  73, 102, (png_byte) '\0'};
542
543 /* after registration of eXIf */
544 static const png_byte mng_eXIf[5]={101,  88,  73, 102, (png_byte) '\0'};
545
546 static const png_byte mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
547 static const png_byte mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
548 static const png_byte mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
549 static const png_byte mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
550 static const png_byte mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
551 static const png_byte mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
552 static const png_byte mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
553 static const png_byte mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
554 static const png_byte mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
555 static const png_byte mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
556 static const png_byte mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
557 static const png_byte mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
558 static const png_byte mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
559 static const png_byte mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
560 static const png_byte mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
561 static const png_byte mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
562 static const png_byte mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
563 static const png_byte mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
564 static const png_byte mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
565 static const png_byte mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
566 static const png_byte mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
567 static const png_byte mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
568 static const png_byte mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
569 static const png_byte mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
570 static const png_byte mng_caNv[5]={ 99,  97,  78, 118, (png_byte) '\0'};
571 static const png_byte mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
572 static const png_byte mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
573 static const png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
574 static const png_byte mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
575 static const png_byte mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
576 static const png_byte mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
577 static const png_byte mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
578 static const png_byte mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
579 static const png_byte mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
580 static const png_byte mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
581 #if defined(zxIf_SUPPORTED)
582 static const png_byte mng_uxIf[5]={117, 120,  73, 102, (png_byte) '\0'};
583 static const png_byte mng_zxIf[5]={122, 120,  73, 102, (png_byte) '\0'};
584 #endif
585
586 #if defined(JNG_SUPPORTED)
587 static const png_byte mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
588 static const png_byte mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
589 static const png_byte mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
590 static const png_byte mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
591 static const png_byte mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
592 static const png_byte mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
593 #endif
594
595 #if 0
596 /* Other known chunks that are not yet supported by ImageMagick: */
597 static const png_byte mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
598 static const png_byte mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
599 static const png_byte mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
600 static const png_byte mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
601 static const png_byte mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
602 static const png_byte mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
603 static const png_byte mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
604 #endif
605
606 typedef struct _MngBox
607 {
608   long
609     left,
610     right,
611     top,
612     bottom;
613 } MngBox;
614
615 typedef struct _MngPair
616 {
617   volatile long
618     a,
619     b;
620 } MngPair;
621
622 #ifdef MNG_OBJECT_BUFFERS
623 typedef struct _MngBuffer
624 {
625
626   size_t
627     height,
628     width;
629
630   Image
631     *image;
632
633   png_color
634     plte[256];
635
636   int
637     reference_count;
638
639   unsigned char
640     alpha_sample_depth,
641     compression_method,
642     color_type,
643     concrete,
644     filter_method,
645     frozen,
646     image_type,
647     interlace_method,
648     pixel_sample_depth,
649     plte_length,
650     sample_depth,
651     viewable;
652 } MngBuffer;
653 #endif
654
655 typedef struct _MngInfo
656 {
657
658 #ifdef MNG_OBJECT_BUFFERS
659   MngBuffer
660     *ob[MNG_MAX_OBJECTS];
661 #endif
662
663   Image *
664     image;
665
666   RectangleInfo
667     page;
668
669   int
670     adjoin,
671 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
672     bytes_in_read_buffer,
673     found_empty_plte,
674 #endif
675     equal_backgrounds,
676     equal_chrms,
677     equal_gammas,
678 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
679     defined(PNG_MNG_FEATURES_SUPPORTED)
680     equal_palettes,
681 #endif
682     equal_physs,
683     equal_srgbs,
684     framing_mode,
685     have_global_bkgd,
686     have_global_chrm,
687     have_global_gama,
688     have_global_phys,
689     have_global_sbit,
690     have_global_srgb,
691     have_saved_bkgd_index,
692     have_write_global_chrm,
693     have_write_global_gama,
694     have_write_global_plte,
695     have_write_global_srgb,
696     need_fram,
697     object_id,
698     old_framing_mode,
699     saved_bkgd_index;
700
701   int
702     new_number_colors;
703
704   ssize_t
705     image_found,
706     loop_count[256],
707     loop_iteration[256],
708     scenes_found,
709     x_off[MNG_MAX_OBJECTS],
710     y_off[MNG_MAX_OBJECTS];
711
712   MngBox
713     clip,
714     frame,
715     image_box,
716     object_clip[MNG_MAX_OBJECTS];
717
718   unsigned char
719     /* These flags could be combined into one byte */
720     exists[MNG_MAX_OBJECTS],
721     frozen[MNG_MAX_OBJECTS],
722     loop_active[256],
723     invisible[MNG_MAX_OBJECTS],
724     viewable[MNG_MAX_OBJECTS];
725
726   MagickOffsetType
727     loop_jump[256];
728
729   png_colorp
730     global_plte;
731
732   png_color_8
733     global_sbit;
734
735   png_byte
736 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
737     read_buffer[8],
738 #endif
739     global_trns[256];
740
741   float
742     global_gamma;
743
744   ChromaticityInfo
745     global_chrm;
746
747   RenderingIntent
748     global_srgb_intent;
749
750   unsigned int
751     delay,
752     global_plte_length,
753     global_trns_length,
754     global_x_pixels_per_unit,
755     global_y_pixels_per_unit,
756     mng_width,
757     mng_height,
758     ticks_per_second;
759
760   MagickBooleanType
761     need_blob;
762
763   unsigned int
764     IsPalette,
765     global_phys_unit_type,
766     basi_warning,
767     clon_warning,
768     dhdr_warning,
769     jhdr_warning,
770     magn_warning,
771     past_warning,
772     phyg_warning,
773     phys_warning,
774     sbit_warning,
775     show_warning,
776     mng_type,
777     write_mng,
778     write_png_colortype,
779     write_png_depth,
780     write_png_compression_level,
781     write_png_compression_strategy,
782     write_png_compression_filter,
783     write_png8,
784     write_png24,
785     write_png32,
786     write_png48,
787     write_png64;
788
789 #ifdef MNG_BASI_SUPPORTED
790   size_t
791     basi_width,
792     basi_height;
793
794   unsigned int
795     basi_depth,
796     basi_color_type,
797     basi_compression_method,
798     basi_filter_type,
799     basi_interlace_method,
800     basi_red,
801     basi_green,
802     basi_blue,
803     basi_alpha,
804     basi_viewable;
805 #endif
806
807   png_uint_16
808     magn_first,
809     magn_last,
810     magn_mb,
811     magn_ml,
812     magn_mr,
813     magn_mt,
814     magn_mx,
815     magn_my,
816     magn_methx,
817     magn_methy;
818
819   PixelInfo
820     mng_global_bkgd;
821
822   /* Added at version 6.6.6-7 */
823   MagickBooleanType
824     ping_exclude_bKGD,
825     ping_exclude_cHRM,
826     ping_exclude_date,
827     ping_exclude_eXIf,
828     ping_exclude_EXIF,
829     ping_exclude_gAMA,
830     ping_exclude_iCCP,
831     /* ping_exclude_iTXt, */
832     ping_exclude_oFFs,
833     ping_exclude_pHYs,
834     ping_exclude_sRGB,
835     ping_exclude_tEXt,
836     ping_exclude_tRNS,
837     ping_exclude_vpAg,
838     ping_exclude_caNv,
839     ping_exclude_zCCP, /* hex-encoded iCCP */
840     ping_exclude_zTXt,
841     ping_preserve_colormap,
842   /* Added at version 6.8.5-7 */
843     ping_preserve_iCCP,
844   /* Added at version 6.8.9-9 */
845     ping_exclude_tIME;
846
847 } MngInfo;
848 #endif /* VER */
849 \f
850 /*
851   Forward declarations.
852 */
853 static MagickBooleanType
854   WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
855
856 static MagickBooleanType
857   WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
858
859 #if defined(JNG_SUPPORTED)
860 static MagickBooleanType
861   WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
862 #endif
863
864 #if PNG_LIBPNG_VER > 10011
865
866
867 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
868 static MagickBooleanType
869 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
870 {
871     /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
872      *
873      * This is true if the high byte and the next highest byte of
874      * each sample of the image, the colormap, and the background color
875      * are equal to each other.  We check this by seeing if the samples
876      * are unchanged when we scale them down to 8 and back up to Quantum.
877      *
878      * We don't use the method GetImageDepth() because it doesn't check
879      * background and doesn't handle PseudoClass specially.
880      */
881
882 #define QuantumToCharToQuantumEqQuantum(quantum) \
883  ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
884
885     MagickBooleanType
886       ok_to_reduce=MagickFalse;
887
888     if (image->depth >= 16)
889       {
890
891         const Quantum
892           *p;
893
894         ok_to_reduce=
895            QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
896            QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
897            QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
898            MagickTrue : MagickFalse;
899
900         if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
901           {
902             int indx;
903
904             for (indx=0; indx < (ssize_t) image->colors; indx++)
905               {
906                 ok_to_reduce=(
907                    QuantumToCharToQuantumEqQuantum(
908                    image->colormap[indx].red) &&
909                    QuantumToCharToQuantumEqQuantum(
910                    image->colormap[indx].green) &&
911                    QuantumToCharToQuantumEqQuantum(
912                    image->colormap[indx].blue)) ?
913                    MagickTrue : MagickFalse;
914
915                 if (ok_to_reduce == MagickFalse)
916                    break;
917               }
918           }
919
920         if ((ok_to_reduce != MagickFalse) &&
921             (image->storage_class != PseudoClass))
922           {
923             ssize_t
924               y;
925
926             register ssize_t
927               x;
928
929             for (y=0; y < (ssize_t) image->rows; y++)
930             {
931               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
932
933               if (p == (const Quantum *) NULL)
934                 {
935                   ok_to_reduce = MagickFalse;
936                   break;
937                 }
938
939               for (x=(ssize_t) image->columns-1; x >= 0; x--)
940               {
941                 ok_to_reduce=
942                    QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
943                    QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
944                    QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
945                    MagickTrue : MagickFalse;
946
947                 if (ok_to_reduce == MagickFalse)
948                   break;
949
950                 p+=GetPixelChannels(image);
951               }
952               if (x >= 0)
953                 break;
954             }
955           }
956
957         if (ok_to_reduce != MagickFalse)
958           {
959             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
960                 "    OK to reduce PNG bit depth to 8 without loss of info");
961           }
962         else
963           {
964             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
965                 "    Not OK to reduce PNG bit depth to 8 without losing info");
966           }
967       }
968
969     return ok_to_reduce;
970 }
971 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
972
973 static const char* PngColorTypeToString(const unsigned int color_type)
974 {
975   const char
976     *result = "Unknown";
977
978   switch (color_type)
979     {
980     case PNG_COLOR_TYPE_GRAY:
981       result = "Gray";
982       break;
983     case PNG_COLOR_TYPE_GRAY_ALPHA:
984       result = "Gray+Alpha";
985       break;
986     case PNG_COLOR_TYPE_PALETTE:
987       result = "Palette";
988       break;
989     case PNG_COLOR_TYPE_RGB:
990       result = "RGB";
991       break;
992     case PNG_COLOR_TYPE_RGB_ALPHA:
993       result = "RGB+Alpha";
994       break;
995     }
996
997   return result;
998 }
999
1000 static int
1001 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1002 {
1003   switch (intent)
1004   {
1005     case PerceptualIntent:
1006        return 0;
1007
1008     case RelativeIntent:
1009        return 1;
1010
1011     case SaturationIntent:
1012        return 2;
1013
1014     case AbsoluteIntent:
1015        return 3;
1016
1017     default:
1018        return -1;
1019   }
1020 }
1021
1022 static RenderingIntent
1023 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1024 {
1025   switch (ping_intent)
1026   {
1027     case 0:
1028       return PerceptualIntent;
1029
1030     case 1:
1031       return RelativeIntent;
1032
1033     case 2:
1034       return SaturationIntent;
1035
1036     case 3:
1037       return AbsoluteIntent;
1038
1039     default:
1040       return UndefinedIntent;
1041     }
1042 }
1043
1044 static const char *
1045 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1046 {
1047   switch (ping_intent)
1048   {
1049     case 0:
1050       return "Perceptual Intent";
1051
1052     case 1:
1053       return "Relative Intent";
1054
1055     case 2:
1056       return "Saturation Intent";
1057
1058     case 3:
1059       return "Absolute Intent";
1060
1061     default:
1062       return "Undefined Intent";
1063     }
1064 }
1065
1066 static const char *
1067 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1068 {
1069   switch (ping_colortype)
1070   {
1071     case 0:
1072       return "Grayscale";
1073
1074     case 2:
1075       return "Truecolor";
1076
1077     case 3:
1078       return "Indexed";
1079
1080     case 4:
1081       return "GrayAlpha";
1082
1083     case 6:
1084       return "RGBA";
1085
1086     default:
1087       return "UndefinedColorType";
1088     }
1089 }
1090
1091 #endif /* PNG_LIBPNG_VER > 10011 */
1092 #endif /* MAGICKCORE_PNG_DELEGATE */
1093 \f
1094 /*
1095 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1096 %                                                                             %
1097 %                                                                             %
1098 %                                                                             %
1099 %   I s M N G                                                                 %
1100 %                                                                             %
1101 %                                                                             %
1102 %                                                                             %
1103 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1104 %
1105 %  IsMNG() returns MagickTrue if the image format type, identified by the
1106 %  magick string, is MNG.
1107 %
1108 %  The format of the IsMNG method is:
1109 %
1110 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1111 %
1112 %  A description of each parameter follows:
1113 %
1114 %    o magick: compare image format pattern against these bytes.
1115 %
1116 %    o length: Specifies the length of the magick string.
1117 %
1118 %
1119 */
1120 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1121 {
1122   if (length < 8)
1123     return(MagickFalse);
1124
1125   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1126     return(MagickTrue);
1127
1128   return(MagickFalse);
1129 }
1130 \f
1131 /*
1132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1133 %                                                                             %
1134 %                                                                             %
1135 %                                                                             %
1136 %   I s J N G                                                                 %
1137 %                                                                             %
1138 %                                                                             %
1139 %                                                                             %
1140 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1141 %
1142 %  IsJNG() returns MagickTrue if the image format type, identified by the
1143 %  magick string, is JNG.
1144 %
1145 %  The format of the IsJNG method is:
1146 %
1147 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1148 %
1149 %  A description of each parameter follows:
1150 %
1151 %    o magick: compare image format pattern against these bytes.
1152 %
1153 %    o length: Specifies the length of the magick string.
1154 %
1155 %
1156 */
1157 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1158 {
1159   if (length < 8)
1160     return(MagickFalse);
1161
1162   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1163     return(MagickTrue);
1164
1165   return(MagickFalse);
1166 }
1167 \f
1168 /*
1169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1170 %                                                                             %
1171 %                                                                             %
1172 %                                                                             %
1173 %   I s P N G                                                                 %
1174 %                                                                             %
1175 %                                                                             %
1176 %                                                                             %
1177 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1178 %
1179 %  IsPNG() returns MagickTrue if the image format type, identified by the
1180 %  magick string, is PNG.
1181 %
1182 %  The format of the IsPNG method is:
1183 %
1184 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1185 %
1186 %  A description of each parameter follows:
1187 %
1188 %    o magick: compare image format pattern against these bytes.
1189 %
1190 %    o length: Specifies the length of the magick string.
1191 %
1192 */
1193 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1194 {
1195   if (length < 8)
1196     return(MagickFalse);
1197
1198   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1199     return(MagickTrue);
1200
1201   return(MagickFalse);
1202 }
1203 \f
1204 #if defined(MAGICKCORE_PNG_DELEGATE)
1205 #if defined(__cplusplus) || defined(c_plusplus)
1206 extern "C" {
1207 #endif
1208
1209 #if (PNG_LIBPNG_VER > 10011)
1210 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1211 {
1212   unsigned char
1213     buffer[4];
1214
1215   assert(image != (Image *) NULL);
1216   assert(image->signature == MagickCoreSignature);
1217   buffer[0]=(unsigned char) (value >> 24);
1218   buffer[1]=(unsigned char) (value >> 16);
1219   buffer[2]=(unsigned char) (value >> 8);
1220   buffer[3]=(unsigned char) value;
1221   return((size_t) WriteBlob(image,4,buffer));
1222 }
1223
1224 static void PNGLong(png_bytep p,png_uint_32 value)
1225 {
1226   *p++=(png_byte) ((value >> 24) & 0xff);
1227   *p++=(png_byte) ((value >> 16) & 0xff);
1228   *p++=(png_byte) ((value >> 8) & 0xff);
1229   *p++=(png_byte) (value & 0xff);
1230 }
1231
1232 static void PNGsLong(png_bytep p,png_int_32 value)
1233 {
1234   *p++=(png_byte) ((value >> 24) & 0xff);
1235   *p++=(png_byte) ((value >> 16) & 0xff);
1236   *p++=(png_byte) ((value >> 8) & 0xff);
1237   *p++=(png_byte) (value & 0xff);
1238 }
1239
1240 static void PNGShort(png_bytep p,png_uint_16 value)
1241 {
1242   *p++=(png_byte) ((value >> 8) & 0xff);
1243   *p++=(png_byte) (value & 0xff);
1244 }
1245
1246 static void PNGType(png_bytep p,const png_byte *type)
1247 {
1248   (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1249 }
1250
1251 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1252    size_t length)
1253 {
1254   if (logging != MagickFalse)
1255     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1256       "  Writing %c%c%c%c chunk, length: %.20g",
1257       type[0],type[1],type[2],type[3],(double) length);
1258 }
1259 #endif /* PNG_LIBPNG_VER > 10011 */
1260
1261 #if defined(__cplusplus) || defined(c_plusplus)
1262 }
1263 #endif
1264
1265 #if PNG_LIBPNG_VER > 10011
1266 /*
1267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1268 %                                                                             %
1269 %                                                                             %
1270 %                                                                             %
1271 %   R e a d P N G I m a g e                                                   %
1272 %                                                                             %
1273 %                                                                             %
1274 %                                                                             %
1275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1276 %
1277 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1278 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1279 %  allocates the memory necessary for the new Image structure and returns a
1280 %  pointer to the new image or set of images.
1281 %
1282 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1283 %
1284 %  The format of the ReadPNGImage method is:
1285 %
1286 %     Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1287 %
1288 %  A description of each parameter follows:
1289 %
1290 %    o image_info: the image info.
1291 %
1292 %    o exception: return any errors or warnings in this structure.
1293 %
1294 %  To do, more or less in chronological order (as of version 5.5.2,
1295 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1296 %
1297 %    Get 16-bit cheap transparency working.
1298 %
1299 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1300 %
1301 %    Preserve all unknown and not-yet-handled known chunks found in input
1302 %    PNG file and copy them into output PNG files according to the PNG
1303 %    copying rules.
1304 %
1305 %    (At this point, PNG encoding should be in full MNG compliance)
1306 %
1307 %    Provide options for choice of background to use when the MNG BACK
1308 %    chunk is not present or is not mandatory (i.e., leave transparent,
1309 %    user specified, MNG BACK, PNG bKGD)
1310 %
1311 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1312 %    efficiently by linking in the duplicate frames.].
1313 %
1314 %    Decode and act on the MHDR simplicity profile (offer option to reject
1315 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1316 %
1317 %    Upgrade to full MNG without Delta-PNG.
1318 %
1319 %        o  BACK [done a while ago except for background image ID]
1320 %        o  MOVE [done 15 May 1999]
1321 %        o  CLIP [done 15 May 1999]
1322 %        o  DISC [done 19 May 1999]
1323 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1324 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1325 %        o  SHOW
1326 %        o  PAST
1327 %        o  BASI
1328 %        o  MNG-level tEXt/iTXt/zTXt
1329 %        o  pHYg
1330 %        o  pHYs
1331 %        o  sBIT
1332 %        o  bKGD
1333 %        o  iTXt (wait for libpng implementation).
1334 %
1335 %    Use the scene signature to discover when an identical scene is
1336 %    being reused, and just point to the original image->exception instead
1337 %    of storing another set of pixels.  This not specific to MNG
1338 %    but could be applied generally.
1339 %
1340 %    Upgrade to full MNG with Delta-PNG.
1341 %
1342 %    JNG tEXt/iTXt/zTXt
1343 %
1344 %    We will not attempt to read files containing the CgBI chunk.
1345 %    They are really Xcode files meant for display on the iPhone.
1346 %    These are not valid PNG files and it is impossible to recover
1347 %    the original PNG from files that have been converted to Xcode-PNG,
1348 %    since irretrievable loss of color data has occurred due to the
1349 %    use of premultiplied alpha.
1350 */
1351
1352 #if defined(__cplusplus) || defined(c_plusplus)
1353 extern "C" {
1354 #endif
1355
1356 /*
1357   This the function that does the actual reading of data.  It is
1358   the same as the one supplied in libpng, except that it receives the
1359   datastream from the ReadBlob() function instead of standard input.
1360 */
1361 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1362 {
1363   Image
1364     *image;
1365
1366   image=(Image *) png_get_io_ptr(png_ptr);
1367   if (length != 0)
1368     {
1369       png_size_t
1370         check;
1371
1372       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1373       if (check != length)
1374         {
1375           char
1376             msg[MagickPathExtent];
1377
1378           (void) FormatLocaleString(msg,MagickPathExtent,
1379             "Expected %.20g bytes; found %.20g bytes",(double) length,
1380             (double) check);
1381           png_warning(png_ptr,msg);
1382           png_error(png_ptr,"Read Exception");
1383         }
1384     }
1385 }
1386
1387 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1388     !defined(PNG_MNG_FEATURES_SUPPORTED)
1389 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1390  * older than libpng-1.0.3a, which was the first to allow the empty
1391  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1392  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1393  * encountered after an empty PLTE, so we have to look ahead for bKGD
1394  * chunks and remove them from the datastream that is passed to libpng,
1395  * and store their contents for later use.
1396  */
1397 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1398 {
1399   MngInfo
1400     *mng_info;
1401
1402   Image
1403     *image;
1404
1405   png_size_t
1406     check;
1407
1408   register ssize_t
1409     i;
1410
1411   i=0;
1412   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1413   image=(Image *) mng_info->image;
1414   while (mng_info->bytes_in_read_buffer && length)
1415   {
1416     data[i]=mng_info->read_buffer[i];
1417     mng_info->bytes_in_read_buffer--;
1418     length--;
1419     i++;
1420   }
1421   if (length != 0)
1422     {
1423       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1424
1425       if (check != length)
1426         png_error(png_ptr,"Read Exception");
1427
1428       if (length == 4)
1429         {
1430           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1431               (data[3] == 0))
1432             {
1433               check=(png_size_t) ReadBlob(image,(size_t) length,
1434                 (char *) mng_info->read_buffer);
1435               mng_info->read_buffer[4]=0;
1436               mng_info->bytes_in_read_buffer=4;
1437               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1438                 mng_info->found_empty_plte=MagickTrue;
1439               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1440                 {
1441                   mng_info->found_empty_plte=MagickFalse;
1442                   mng_info->have_saved_bkgd_index=MagickFalse;
1443                 }
1444             }
1445
1446           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1447               (data[3] == 1))
1448             {
1449               check=(png_size_t) ReadBlob(image,(size_t) length,
1450                 (char *) mng_info->read_buffer);
1451               mng_info->read_buffer[4]=0;
1452               mng_info->bytes_in_read_buffer=4;
1453               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1454                 if (mng_info->found_empty_plte)
1455                   {
1456                     /*
1457                       Skip the bKGD data byte and CRC.
1458                     */
1459                     check=(png_size_t)
1460                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1461                     check=(png_size_t) ReadBlob(image,(size_t) length,
1462                       (char *) mng_info->read_buffer);
1463                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1464                     mng_info->have_saved_bkgd_index=MagickTrue;
1465                     mng_info->bytes_in_read_buffer=0;
1466                   }
1467             }
1468         }
1469     }
1470 }
1471 #endif
1472
1473 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1474 {
1475   Image
1476     *image;
1477
1478   image=(Image *) png_get_io_ptr(png_ptr);
1479   if (length != 0)
1480     {
1481       png_size_t
1482         check;
1483
1484       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1485
1486       if (check != length)
1487         png_error(png_ptr,"WriteBlob Failed");
1488     }
1489 }
1490
1491 static void png_flush_data(png_structp png_ptr)
1492 {
1493   (void) png_ptr;
1494 }
1495
1496 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1497 static int PalettesAreEqual(Image *a,Image *b)
1498 {
1499   ssize_t
1500     i;
1501
1502   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1503     return((int) MagickFalse);
1504
1505   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1506     return((int) MagickFalse);
1507
1508   if (a->colors != b->colors)
1509     return((int) MagickFalse);
1510
1511   for (i=0; i < (ssize_t) a->colors; i++)
1512   {
1513     if ((a->colormap[i].red != b->colormap[i].red) ||
1514         (a->colormap[i].green != b->colormap[i].green) ||
1515         (a->colormap[i].blue != b->colormap[i].blue))
1516       return((int) MagickFalse);
1517   }
1518
1519   return((int) MagickTrue);
1520 }
1521 #endif
1522
1523 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1524 {
1525   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1526       mng_info->exists[i] && !mng_info->frozen[i])
1527     {
1528 #ifdef MNG_OBJECT_BUFFERS
1529       if (mng_info->ob[i] != (MngBuffer *) NULL)
1530         {
1531           if (mng_info->ob[i]->reference_count > 0)
1532             mng_info->ob[i]->reference_count--;
1533
1534           if (mng_info->ob[i]->reference_count == 0)
1535             {
1536               if (mng_info->ob[i]->image != (Image *) NULL)
1537                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1538
1539               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1540             }
1541         }
1542       mng_info->ob[i]=(MngBuffer *) NULL;
1543 #endif
1544       mng_info->exists[i]=MagickFalse;
1545       mng_info->invisible[i]=MagickFalse;
1546       mng_info->viewable[i]=MagickFalse;
1547       mng_info->frozen[i]=MagickFalse;
1548       mng_info->x_off[i]=0;
1549       mng_info->y_off[i]=0;
1550       mng_info->object_clip[i].left=0;
1551       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1552       mng_info->object_clip[i].top=0;
1553       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1554     }
1555 }
1556
1557 static void MngInfoFreeStruct(MngInfo *mng_info,
1558     MagickBooleanType *have_mng_structure)
1559 {
1560   if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1561     {
1562       register ssize_t
1563         i;
1564
1565       for (i=1; i < MNG_MAX_OBJECTS; i++)
1566         MngInfoDiscardObject(mng_info,i);
1567
1568       if (mng_info->global_plte != (png_colorp) NULL)
1569         mng_info->global_plte=(png_colorp)
1570           RelinquishMagickMemory(mng_info->global_plte);
1571
1572       mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1573       *have_mng_structure=MagickFalse;
1574     }
1575 }
1576
1577 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1578 {
1579   MngBox
1580     box;
1581
1582   box=box1;
1583   if (box.left < box2.left)
1584     box.left=box2.left;
1585
1586   if (box.top < box2.top)
1587     box.top=box2.top;
1588
1589   if (box.right > box2.right)
1590     box.right=box2.right;
1591
1592   if (box.bottom > box2.bottom)
1593     box.bottom=box2.bottom;
1594
1595   return box;
1596 }
1597
1598 static MngBox mng_read_box(MngBox previous_box,char delta_type,
1599   unsigned char *p)
1600 {
1601    MngBox
1602       box;
1603
1604   /*
1605     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1606   */
1607   box.left=(ssize_t) ((p[0]  << 24) | (p[1]  << 16) | (p[2]  << 8) | p[3]);
1608   box.right=(ssize_t) ((p[4]  << 24) | (p[5]  << 16) | (p[6]  << 8) | p[7]);
1609   box.top=(ssize_t) ((p[8]  << 24) | (p[9]  << 16) | (p[10] << 8) | p[11]);
1610   box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1611   if (delta_type != 0)
1612     {
1613       box.left+=previous_box.left;
1614       box.right+=previous_box.right;
1615       box.top+=previous_box.top;
1616       box.bottom+=previous_box.bottom;
1617     }
1618
1619   return(box);
1620 }
1621
1622 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1623   unsigned char *p)
1624 {
1625   MngPair
1626     pair;
1627   /*
1628     Read two ssize_ts from CLON, MOVE or PAST chunk
1629   */
1630   pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1631   pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1632
1633   if (delta_type != 0)
1634     {
1635       pair.a+=previous_pair.a;
1636       pair.b+=previous_pair.b;
1637     }
1638
1639   return(pair);
1640 }
1641
1642 static long mng_get_long(unsigned char *p)
1643 {
1644   return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1645 }
1646
1647 typedef struct _PNGErrorInfo
1648 {
1649   Image
1650     *image;
1651
1652   ExceptionInfo
1653     *exception;
1654 } PNGErrorInfo;
1655
1656 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1657 {
1658   ExceptionInfo
1659     *exception;
1660
1661   Image
1662     *image;
1663
1664   PNGErrorInfo
1665     *error_info;
1666
1667   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1668   image=error_info->image;
1669   exception=error_info->exception;
1670
1671   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1672     "  libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1673
1674   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1675     "`%s'",image->filename);
1676
1677 #if (PNG_LIBPNG_VER < 10500)
1678   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1679    * are building with libpng-1.4.x and can be ignored.
1680    */
1681   longjmp(ping->jmpbuf,1);
1682 #else
1683   png_longjmp(ping,1);
1684 #endif
1685 }
1686
1687 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1688 {
1689   ExceptionInfo
1690     *exception;
1691
1692   Image
1693     *image;
1694
1695   PNGErrorInfo
1696     *error_info;
1697
1698   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1699     png_error(ping, message);
1700
1701   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1702   image=error_info->image;
1703   exception=error_info->exception;
1704   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1705     "  libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1706
1707   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1708     message,"`%s'",image->filename);
1709 }
1710
1711 #ifdef PNG_USER_MEM_SUPPORTED
1712 #if PNG_LIBPNG_VER >= 10400
1713 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1714 #else
1715 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1716 #endif
1717 {
1718   (void) png_ptr;
1719   return((png_voidp) AcquireMagickMemory((size_t) size));
1720 }
1721
1722 /*
1723   Free a pointer.  It is removed from the list at the same time.
1724 */
1725 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1726 {
1727   (void) png_ptr;
1728   ptr=RelinquishMagickMemory(ptr);
1729   return((png_free_ptr) NULL);
1730 }
1731 #endif
1732
1733 #if defined(__cplusplus) || defined(c_plusplus)
1734 }
1735 #endif
1736
1737 static int
1738 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1739    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1740 {
1741   register ssize_t
1742     i;
1743
1744   register unsigned char
1745     *dp;
1746
1747   register png_charp
1748     sp;
1749
1750   png_uint_32
1751     length,
1752     nibbles;
1753
1754   StringInfo
1755     *profile;
1756
1757   const unsigned char
1758     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1759                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1760                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1761                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1762                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1763                  13,14,15};
1764
1765   sp=text[ii].text+1;
1766   /* look for newline */
1767   while (*sp != '\n')
1768      sp++;
1769
1770   /* look for length */
1771   while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1772      sp++;
1773
1774   length=(png_uint_32) StringToLong(sp);
1775
1776   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1777        "      length: %lu",(unsigned long) length);
1778
1779   while (*sp != ' ' && *sp != '\n')
1780      sp++;
1781
1782   /* allocate space */
1783   if (length == 0)
1784   {
1785     png_warning(ping,"invalid profile length");
1786     return(MagickFalse);
1787   }
1788
1789   profile=BlobToStringInfo((const void *) NULL,length);
1790
1791   if (profile == (StringInfo *) NULL)
1792   {
1793     png_warning(ping, "unable to copy profile");
1794     return(MagickFalse);
1795   }
1796
1797   /* copy profile, skipping white space and column 1 "=" signs */
1798   dp=GetStringInfoDatum(profile);
1799   nibbles=length*2;
1800
1801   for (i=0; i < (ssize_t) nibbles; i++)
1802   {
1803     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1804     {
1805       if (*sp == '\0')
1806         {
1807           png_warning(ping, "ran out of profile data");
1808           profile=DestroyStringInfo(profile);
1809           return(MagickFalse);
1810         }
1811       sp++;
1812     }
1813
1814     if (i%2 == 0)
1815       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1816
1817     else
1818       (*dp++)+=unhex[(int) *sp++];
1819   }
1820   /*
1821     We have already read "Raw profile type.
1822   */
1823   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1824   profile=DestroyStringInfo(profile);
1825
1826   if (image_info->verbose)
1827     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1828
1829   return MagickTrue;
1830 }
1831
1832 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1833 #ifdef zxIf_SUPPORTED
1834
1835 /* exif_inf() was derived from zlib-1.2.11/examples/zpipe.c/inf()
1836    Not copyrighted -- provided to the public domain
1837    Version 1.4  11 December 2005  Mark Adler */
1838
1839 #include <assert.h>
1840
1841 /* Decompress from source to dest (copied from zlib-1.2.11/examples).
1842    inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
1843    allocated for processing, Z_DATA_ERROR if the deflate data is
1844    invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
1845    the version of the library linked do not match, or Z_ERRNO if there
1846    is an error reading or writing the files. */
1847
1848 int exif_inf(png_structp png_ptr, unsigned char *source,
1849     unsigned char **dest, size_t n, png_uint_32 inflated_size)
1850 {
1851     /* *source: compressed data stream (input)
1852        *dest:   inflated data (output)
1853        n: length of input
1854
1855        Returns one of the following:
1856        return(-1);  chunk had an error
1857        return(n);  success, n is length of inflated data
1858      */
1859
1860     int ret;
1861     z_stream strm;
1862
1863     size_t inflated_length = inflated_size;
1864
1865     if (inflated_length >= PNG_USER_CHUNK_MALLOC_MAX - 1U ||
1866         inflated_length == 0)
1867         return (-1);
1868
1869     /* allocate dest */
1870 #if PNG_LIBPNG_VER >= 14000
1871     *dest=(unsigned char *) png_malloc(png_ptr,
1872        (png_alloc_size_t) inflated_length);
1873 #else
1874     *dest=(unsigned char *) png_malloc(png_ptr,
1875        (png_size_t) inflated_length);
1876 #endif
1877     /* allocate inflate state */
1878     strm.zalloc = Z_NULL;
1879     strm.zfree = Z_NULL;
1880     strm.opaque = Z_NULL;
1881     strm.avail_in = 0;
1882     strm.next_in = Z_NULL;
1883     ret = inflateInit(&strm);
1884     if (ret != Z_OK)
1885         return (-1);
1886     /* decompress until deflate stream ends or end of file */
1887     do {
1888         strm.avail_in = (int)n;
1889         strm.next_in = source;
1890
1891         /* run inflate() on input until output buffer not full */
1892         do {
1893             strm.avail_out = (int)inflated_length;
1894             strm.next_out = *dest;
1895             ret = inflate(&strm, Z_NO_FLUSH);
1896             assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
1897             switch (ret) {
1898             case Z_NEED_DICT:
1899             case Z_DATA_ERROR:
1900             case Z_MEM_ERROR:
1901                 (void)inflateEnd(&strm);
1902                 return (-1);
1903             }
1904         } while (strm.avail_out == 0);
1905         /* done when inflate() says it's done */
1906     } while (ret != Z_STREAM_END);
1907
1908     /* clean up and return */
1909
1910     /* To do: take care of too little or too much data */
1911
1912     (void)inflateEnd(&strm);
1913     return (inflated_length);
1914 }
1915 #endif /* zxIf */
1916
1917 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1918 {
1919   Image
1920     *image;
1921
1922
1923   /* The unknown chunk structure contains the chunk data:
1924      png_byte name[5];
1925      png_byte *data;
1926      png_size_t size;
1927
1928      Note that libpng has already taken care of the CRC handling.
1929
1930      Returns one of the following:
1931          return(-n);  chunk had an error
1932          return(0);  did not recognize
1933          return(n);  success
1934   */
1935
1936   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1937      "    read_user_chunk: found %c%c%c%c chunk",
1938        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1939
1940 #if defined(zxIf_SUPPORTED)
1941   if ((chunk->name[0]  == 122 || chunk->name[0] == 117 ) &&
1942       (chunk->name[1] ==   88 || chunk->name[1] == 120 ) &&
1943       chunk->name[2] ==   73 &&
1944       chunk-> name[3] == 102)
1945     {
1946       /* process uxIf or zxIf chunk */
1947       StringInfo *
1948         profile;
1949
1950       PNGErrorInfo
1951         *error_info;
1952
1953       unsigned char
1954         *p;
1955
1956       png_byte
1957         *s;
1958
1959       size_t
1960         i;
1961
1962       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1963         "     recognized uxIf|zxIf chunk");
1964
1965       image=(Image *) png_get_user_chunk_ptr(ping);
1966
1967       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1968
1969       profile=BlobToStringInfo((const void *) NULL,chunk->size+6);
1970
1971       if (profile == (StringInfo *) NULL)
1972         {
1973           (void) ThrowMagickException(error_info->exception,GetMagickModule(),
1974             ResourceLimitError,"MemoryAllocationFailed","`%s'",
1975             image->filename);
1976           return(-1);
1977         }
1978       p=GetStringInfoDatum(profile);
1979
1980       /* Initialize profile with "Exif\0\0" */
1981       *p++ ='E';
1982       *p++ ='x';
1983       *p++ ='i';
1984       *p++ ='f';
1985       *p++ ='\0';
1986       *p++ ='\0';
1987
1988       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1989         "     initialized uxIf|zxIf chunk");
1990
1991       switch (chunk->data[0])
1992         {
1993           PNGErrorInfo
1994             *error_info;
1995           case 'E':
1996           case 'I':
1997           {
1998             /* Uncompressed */
1999             /* copy chunk->data to profile */
2000             s=chunk->data;
2001             for (i=0; i<chunk->size; i++)
2002               *p++ = *s++;
2003
2004             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2005                "     SetImageProfile with %lu bytes",
2006                (unsigned long) chunk->size+6);
2007             error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2008             (void) SetImageProfile(image,"exif",profile,
2009               error_info->exception);
2010             return(1);
2011           }
2012           case '\0':
2013           {
2014             /* Zlib compressed */
2015
2016             unsigned char *
2017               temp;
2018
2019             png_uint_32 inflated_size;
2020
2021             png_free(ping,profile);
2022
2023             if (chunk->size < 5)
2024                return(-1);
2025
2026             s=chunk->data;
2027             s++;  // skip compression byte
2028
2029             inflated_size = (png_uint_32)
2030                (((s[0] & 0xff) << 24) | ((s[1] & 0xff) << 16) |
2031                 ((s[2] & 0xff) <<  8) | ((s[3] & 0xff)      ));
2032
2033             s+=4;
2034
2035             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2036                "     inflated_size = %lu bytes",inflated_size);
2037
2038             /* uncompress chunk->data to temporary profile */
2039             inflated_size=exif_inf(ping,s,&temp,chunk->size-1,inflated_size);
2040
2041             if (inflated_size <= 0)
2042             {
2043                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2044                   "     inflated_size = %lu bytes",inflated_size);
2045                return(-1);
2046             }
2047            error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2048
2049            profile=BlobToStringInfo((const void *) NULL,inflated_size+6);
2050
2051            if (profile == (StringInfo *) NULL)
2052              {
2053                (void) ThrowMagickException(error_info->exception,
2054                  GetMagickModule(),
2055                  ResourceLimitError,"MemoryAllocationFailed","`%s'",
2056                  image->filename);
2057                return(-1);
2058              }
2059            p=GetStringInfoDatum(profile);
2060            /* Initialize profile with "Exif\0\0" */
2061            *p++ ='E';
2062            *p++ ='x';
2063            *p++ ='i';
2064            *p++ ='f';
2065            *p++ ='\0';
2066            *p++ ='\0';
2067
2068            for (i=0; i<inflated_size; i++)
2069               *p++=temp[i];
2070
2071             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2072                "     SetImageProfile with %lu bytes",
2073                (unsigned long) inflated_size+6);
2074             error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2075             (void) SetImageProfile(image,"exif",profile,
2076               error_info->exception);
2077
2078             png_free(ping,temp);
2079
2080             return(1);
2081           }
2082        }
2083     }
2084 #endif /* zxIf_SUPPORTED */
2085
2086   if (chunk->name[0]  == 101 &&
2087       (chunk->name[1] ==  88 || chunk->name[1] == 120 ) &&
2088       chunk->name[2] ==   73 &&
2089       chunk-> name[3] == 102)
2090     {
2091       /* process eXIf or exIf chunk */
2092
2093       PNGErrorInfo
2094         *error_info;
2095
2096       StringInfo
2097         *profile;
2098
2099       unsigned char
2100         *p;
2101
2102       png_byte
2103         *s;
2104
2105       size_t
2106         i;
2107
2108       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2109         " recognized eXIf|exIf chunk");
2110
2111       image=(Image *) png_get_user_chunk_ptr(ping);
2112
2113       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2114
2115       profile=BlobToStringInfo((const void *) NULL,chunk->size+6);
2116
2117       if (profile == (StringInfo *) NULL)
2118         {
2119           (void) ThrowMagickException(error_info->exception,GetMagickModule(),
2120             ResourceLimitError,"MemoryAllocationFailed","`%s'",
2121             image->filename);
2122           return(-1);
2123         }
2124       p=GetStringInfoDatum(profile);
2125
2126       /* Initialize profile with "Exif\0\0" */
2127       *p++ ='E';
2128       *p++ ='x';
2129       *p++ ='i';
2130       *p++ ='f';
2131       *p++ ='\0';
2132       *p++ ='\0';
2133
2134       /* copy chunk->data to profile */
2135       s=chunk->data;
2136       for (i=0; i<chunk->size; i++)
2137         *p++ = *s++;
2138
2139       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2140       (void) SetImageProfile(image,"exif",profile,
2141         error_info->exception);
2142
2143       return(1);
2144     }
2145
2146   /* vpAg (deprecated, replaced by caNv) */
2147   if (chunk->name[0] == 118 &&
2148       chunk->name[1] == 112 &&
2149       chunk->name[2] ==  65 &&
2150       chunk->name[3] == 103)
2151     {
2152      /* recognized vpAg */
2153
2154      if (chunk->size != 9)
2155        return(-1); /* Error return */
2156
2157      if (chunk->data[8] != 0)
2158        return(0);  /* ImageMagick requires pixel units */
2159
2160      image=(Image *) png_get_user_chunk_ptr(ping);
2161
2162      image->page.width=(size_t) ((chunk->data[0] << 24) |
2163         (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
2164
2165      image->page.height=(size_t) ((chunk->data[4] << 24) |
2166         (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
2167
2168      return(1);
2169     }
2170
2171   /* caNv */
2172   if (chunk->name[0] ==  99 &&
2173       chunk->name[1] ==  97 &&
2174       chunk->name[2] ==  78 &&
2175       chunk->name[3] == 118)
2176     {
2177      /* recognized caNv */
2178
2179      if (chunk->size != 16)
2180        return(-1); /* Error return */
2181
2182      image=(Image *) png_get_user_chunk_ptr(ping);
2183
2184      image->page.width=(size_t) ((chunk->data[0] << 24) |
2185         (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
2186
2187      image->page.height=(size_t) ((chunk->data[4] << 24) |
2188         (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
2189
2190      image->page.x=(size_t) ((chunk->data[8] << 24) |
2191         (chunk->data[9] << 16) | (chunk->data[10] << 8) | chunk->data[11]);
2192
2193      image->page.y=(size_t) ((chunk->data[12] << 24) |
2194         (chunk->data[13] << 16) | (chunk->data[14] << 8) | chunk->data[15]);
2195
2196      return(1);
2197     }
2198
2199   return(0); /* Did not recognize */
2200 }
2201 #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
2202
2203 #if defined(PNG_tIME_SUPPORTED)
2204 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
2205   ExceptionInfo *exception)
2206 {
2207   png_timep
2208     time;
2209
2210   if (png_get_tIME(ping,info,&time))
2211     {
2212       char
2213         timestamp[21];
2214
2215       FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
2216         time->year,time->month,time->day,time->hour,time->minute,time->second);
2217       SetImageProperty(image,"png:tIME",timestamp,exception);
2218     }
2219 }
2220 #endif
2221
2222 /*
2223 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2224 %                                                                             %
2225 %                                                                             %
2226 %                                                                             %
2227 %   R e a d O n e P N G I m a g e                                             %
2228 %                                                                             %
2229 %                                                                             %
2230 %                                                                             %
2231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2232 %
2233 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2234 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
2235 %  necessary for the new Image structure and returns a pointer to the new
2236 %  image.
2237 %
2238 %  The format of the ReadOnePNGImage method is:
2239 %
2240 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2241 %         ExceptionInfo *exception)
2242 %
2243 %  A description of each parameter follows:
2244 %
2245 %    o mng_info: Specifies a pointer to a MngInfo structure.
2246 %
2247 %    o image_info: the image info.
2248 %
2249 %    o exception: return any errors or warnings in this structure.
2250 %
2251 */
2252 static Image *ReadOnePNGImage(MngInfo *mng_info,
2253     const ImageInfo *image_info, ExceptionInfo *exception)
2254 {
2255   /* Read one PNG image */
2256
2257   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2258
2259   Image
2260     *image;
2261
2262   char
2263     im_vers[32],
2264     libpng_runv[32],
2265     libpng_vers[32],
2266     zlib_runv[32],
2267     zlib_vers[32];
2268
2269   int
2270     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2271     num_raw_profiles,
2272     num_text,
2273     num_text_total,
2274     num_passes,
2275     number_colors,
2276     pass,
2277     ping_bit_depth,
2278     ping_color_type,
2279     ping_file_depth,
2280     ping_interlace_method,
2281     ping_compression_method,
2282     ping_filter_method,
2283     ping_num_trans,
2284     unit_type;
2285
2286   double
2287     file_gamma;
2288
2289   MagickBooleanType
2290     logging,
2291     ping_found_cHRM,
2292     ping_found_gAMA,
2293     ping_found_iCCP,
2294     ping_found_sRGB,
2295     ping_found_sRGB_cHRM,
2296     ping_preserve_iCCP,
2297     status;
2298
2299   MemoryInfo
2300     *volatile pixel_info;
2301
2302   PixelInfo
2303     transparent_color;
2304
2305   PNGErrorInfo
2306     error_info;
2307
2308   png_bytep
2309      ping_trans_alpha;
2310
2311   png_color_16p
2312      ping_background,
2313      ping_trans_color;
2314
2315   png_info
2316     *end_info,
2317     *ping_info;
2318
2319   png_struct
2320     *ping;
2321
2322   png_textp
2323     text;
2324
2325   png_uint_32
2326     ping_height,
2327     ping_width,
2328     x_resolution,
2329     y_resolution;
2330
2331   QuantumInfo
2332     *quantum_info;
2333
2334   ssize_t
2335     ping_rowbytes,
2336     y;
2337
2338   register unsigned char
2339     *p;
2340
2341   register ssize_t
2342     i,
2343     x;
2344
2345   register Quantum
2346     *q;
2347
2348   size_t
2349     length,
2350     row_offset;
2351
2352   ssize_t
2353     j;
2354
2355   unsigned char
2356     *ping_pixels;
2357
2358 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2359   png_byte unused_chunks[]=
2360   {
2361     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2362     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2363     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2364     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2365     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2366 #if !defined(PNG_tIME_SUPPORTED)
2367     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2368 #endif
2369 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2370                           /* ignore the APNG chunks */
2371      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2372     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2373     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2374 #endif
2375   };
2376 #endif
2377
2378   /* Define these outside of the following "if logging()" block so they will
2379    * show in debuggers.
2380    */
2381   *im_vers='\0';
2382   (void) ConcatenateMagickString(im_vers,
2383          MagickLibVersionText,32);
2384   (void) ConcatenateMagickString(im_vers,
2385          MagickLibAddendum,32);
2386
2387   *libpng_vers='\0';
2388   (void) ConcatenateMagickString(libpng_vers,
2389          PNG_LIBPNG_VER_STRING,32);
2390   *libpng_runv='\0';
2391   (void) ConcatenateMagickString(libpng_runv,
2392          png_get_libpng_ver(NULL),32);
2393
2394   *zlib_vers='\0';
2395   (void) ConcatenateMagickString(zlib_vers,
2396          ZLIB_VERSION,32);
2397   *zlib_runv='\0';
2398   (void) ConcatenateMagickString(zlib_runv,
2399          zlib_version,32);
2400
2401   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2402        "  Enter ReadOnePNGImage()\n"
2403        "    IM version     = %s\n"
2404        "    Libpng version = %s",
2405        im_vers, libpng_vers);
2406
2407   if (logging != MagickFalse)
2408   {
2409     if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2410     {
2411    (void) LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2412         libpng_runv);
2413     }
2414     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
2415         zlib_vers);
2416     if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2417     {
2418     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2419         zlib_runv);
2420     }
2421   }
2422
2423 #if (PNG_LIBPNG_VER < 10200)
2424   if (image_info->verbose)
2425     printf("Your PNG library (libpng-%s) is rather old.\n",
2426        PNG_LIBPNG_VER_STRING);
2427 #endif
2428
2429 #if (PNG_LIBPNG_VER >= 10400)
2430 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
2431   if (image_info->verbose)
2432     {
2433       printf("Your PNG library (libpng-%s) is an old beta version.\n",
2434            PNG_LIBPNG_VER_STRING);
2435       printf("Please update it.\n");
2436     }
2437 #  endif
2438 #endif
2439
2440
2441   quantum_info = (QuantumInfo *) NULL;
2442   image=mng_info->image;
2443
2444   if (logging != MagickFalse)
2445   {
2446     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2447        "    Before reading:\n"
2448        "      image->alpha_trait=%d"
2449        "      image->rendering_intent=%d\n"
2450        "      image->colorspace=%d\n"
2451        "      image->gamma=%f",
2452        (int) image->alpha_trait, (int) image->rendering_intent,
2453        (int) image->colorspace, image->gamma);
2454   }
2455   intent=
2456     Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2457
2458   /* Set to an out-of-range color unless tRNS chunk is present */
2459   transparent_color.red=65537;
2460   transparent_color.green=65537;
2461   transparent_color.blue=65537;
2462   transparent_color.alpha=65537;
2463
2464   number_colors=0;
2465   num_text = 0;
2466   num_text_total = 0;
2467   num_raw_profiles = 0;
2468
2469   ping_found_cHRM = MagickFalse;
2470   ping_found_gAMA = MagickFalse;
2471   ping_found_iCCP = MagickFalse;
2472   ping_found_sRGB = MagickFalse;
2473   ping_found_sRGB_cHRM = MagickFalse;
2474   ping_preserve_iCCP = MagickFalse;
2475
2476
2477   /*
2478     Allocate the PNG structures
2479   */
2480 #ifdef PNG_USER_MEM_SUPPORTED
2481  error_info.image=image;
2482  error_info.exception=exception;
2483  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2484    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2485    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2486 #else
2487   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2488     MagickPNGErrorHandler,MagickPNGWarningHandler);
2489 #endif
2490   if (ping == (png_struct *) NULL)
2491     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2492
2493   ping_info=png_create_info_struct(ping);
2494
2495   if (ping_info == (png_info *) NULL)
2496     {
2497       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2498       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2499     }
2500
2501   end_info=png_create_info_struct(ping);
2502
2503   if (end_info == (png_info *) NULL)
2504     {
2505       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2506       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2507     }
2508
2509   pixel_info=(MemoryInfo *) NULL;
2510
2511   if (setjmp(png_jmpbuf(ping)))
2512     {
2513       /*
2514         PNG image is corrupt.
2515       */
2516       png_destroy_read_struct(&ping,&ping_info,&end_info);
2517
2518 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2519       UnlockSemaphoreInfo(ping_semaphore);
2520 #endif
2521
2522       if (pixel_info != (MemoryInfo *) NULL)
2523         pixel_info=RelinquishVirtualMemory(pixel_info);
2524
2525       if (logging != MagickFalse)
2526         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2527           "  exit ReadOnePNGImage() with error.");
2528
2529       return(GetFirstImageInList(image));
2530     }
2531
2532   /* {  For navigation to end of SETJMP-protected block.  Within this
2533    *    block, use png_error() instead of Throwing an Exception, to ensure
2534    *    that libpng is able to clean up, and that the semaphore is unlocked.
2535    */
2536
2537 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2538   LockSemaphoreInfo(ping_semaphore);
2539 #endif
2540
2541 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2542   /* Allow benign errors */
2543   png_set_benign_errors(ping, 1);
2544 #endif
2545
2546 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2547   /* Reject images with too many rows or columns */
2548   png_set_user_limits(ping,
2549     (png_uint_32) MagickMin(0x7fffffffL,
2550         GetMagickResourceLimit(WidthResource)),
2551     (png_uint_32) MagickMin(0x7fffffffL,
2552         GetMagickResourceLimit(HeightResource)));
2553 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2554
2555   /*
2556     Prepare PNG for reading.
2557   */
2558
2559   mng_info->image_found++;
2560   png_set_sig_bytes(ping,8);
2561
2562   if (LocaleCompare(image_info->magick,"MNG") == 0)
2563     {
2564 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2565       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2566       png_set_read_fn(ping,image,png_get_data);
2567 #else
2568 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2569       png_permit_empty_plte(ping,MagickTrue);
2570       png_set_read_fn(ping,image,png_get_data);
2571 #else
2572       mng_info->image=image;
2573       mng_info->bytes_in_read_buffer=0;
2574       mng_info->found_empty_plte=MagickFalse;
2575       mng_info->have_saved_bkgd_index=MagickFalse;
2576       png_set_read_fn(ping,mng_info,mng_get_data);
2577 #endif
2578 #endif
2579     }
2580
2581   else
2582     png_set_read_fn(ping,image,png_get_data);
2583
2584   {
2585     const char
2586       *value;
2587
2588     value=GetImageOption(image_info,"profile:skip");
2589
2590     if (IsOptionMember("ICC",value) == MagickFalse)
2591     {
2592
2593        value=GetImageOption(image_info,"png:preserve-iCCP");
2594
2595        if (value == NULL)
2596           value=GetImageArtifact(image,"png:preserve-iCCP");
2597
2598        if (value != NULL)
2599           ping_preserve_iCCP=MagickTrue;
2600
2601 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2602        /* Don't let libpng check for ICC/sRGB profile because we're going
2603         * to do that anyway.  This feature was added at libpng-1.6.12.
2604         * If logging, go ahead and check and issue a warning as appropriate.
2605         */
2606        if (logging == MagickFalse)
2607           png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2608 #endif
2609     }
2610 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2611     else
2612     {
2613        png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
2614     }
2615 #endif
2616   }
2617 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2618   /* Ignore unused chunks and all unknown chunks except for eXIf,
2619      zxIf, caNv, and vpAg */
2620 # if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2621   png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2622 # else
2623   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2624 # endif
2625 #if defined(zxIf_SUPPORTED)
2626   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_uxIf, 1);
2627   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_zxIf, 1);
2628 #endif
2629   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_eXIf, 1);
2630   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
2631   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
2632   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2633      (int)sizeof(unused_chunks)/5);
2634   /* Callback for other unknown chunks */
2635   png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
2636 #endif
2637
2638 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2639 #  if (PNG_LIBPNG_VER >= 10400)
2640     /* Limit the size of the chunk storage cache used for sPLT, text,
2641      * and unknown chunks.
2642      */
2643     png_set_chunk_cache_max(ping, 32767);
2644 #  endif
2645 #endif
2646
2647 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2648     /* Disable new libpng-1.5.10 feature */
2649     png_set_check_for_invalid_index (ping, 0);
2650 #endif
2651
2652 #if (PNG_LIBPNG_VER < 10400)
2653 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2654    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2655   /* Disable thread-unsafe features of pnggccrd */
2656   if (png_access_version_number() >= 10200)
2657   {
2658     png_uint_32 mmx_disable_mask=0;
2659     png_uint_32 asm_flags;
2660
2661     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2662                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2663                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2664                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2665     asm_flags=png_get_asm_flags(ping);
2666     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2667   }
2668 #  endif
2669 #endif
2670
2671   png_read_info(ping,ping_info);
2672
2673   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2674                &ping_bit_depth,&ping_color_type,
2675                &ping_interlace_method,&ping_compression_method,
2676                &ping_filter_method);
2677
2678   ping_file_depth = ping_bit_depth;
2679
2680   /* Swap bytes if requested */
2681   if (ping_file_depth == 16)
2682   {
2683      const char
2684        *value;
2685
2686      value=GetImageOption(image_info,"png:swap-bytes");
2687
2688      if (value == NULL)
2689         value=GetImageArtifact(image,"png:swap-bytes");
2690
2691      if (value != NULL)
2692         png_set_swap(ping);
2693   }
2694
2695   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2696   {
2697       char
2698         msg[MagickPathExtent];
2699
2700       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2701          (int) ping_color_type);
2702       (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2703
2704       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2705          (int) ping_bit_depth);
2706       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2707   }
2708
2709   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2710                       &ping_trans_color);
2711
2712   (void) png_get_bKGD(ping, ping_info, &ping_background);
2713
2714   if (ping_bit_depth < 8)
2715     {
2716        png_set_packing(ping);
2717        ping_bit_depth = 8;
2718     }
2719
2720   image->depth=ping_bit_depth;
2721   image->depth=GetImageQuantumDepth(image,MagickFalse);
2722   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2723
2724   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2725       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2726     {
2727       image->rendering_intent=UndefinedIntent;
2728       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2729       (void) ResetMagickMemory(&image->chromaticity,0,
2730         sizeof(image->chromaticity));
2731     }
2732
2733   if (logging != MagickFalse)
2734     {
2735       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2736         "    PNG width: %.20g, height: %.20g\n"
2737         "    PNG color_type: %d, bit_depth: %d\n"
2738         "    PNG compression_method: %d\n"
2739         "    PNG interlace_method: %d, filter_method: %d",
2740         (double) ping_width, (double) ping_height,
2741         ping_color_type, ping_bit_depth,
2742         ping_compression_method,
2743         ping_interlace_method,ping_filter_method);
2744
2745     }
2746
2747   if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2748     {
2749       ping_found_iCCP=MagickTrue;
2750       if (logging != MagickFalse)
2751         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2752           "    Found PNG iCCP chunk.");
2753     }
2754
2755   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2756     {
2757       ping_found_gAMA=MagickTrue;
2758       if (logging != MagickFalse)
2759         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2760           "    Found PNG gAMA chunk.");
2761     }
2762
2763   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2764     {
2765       ping_found_cHRM=MagickTrue;
2766       if (logging != MagickFalse)
2767         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2768           "    Found PNG cHRM chunk.");
2769     }
2770
2771   if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2772       PNG_INFO_sRGB))
2773     {
2774       ping_found_sRGB=MagickTrue;
2775       if (logging != MagickFalse)
2776         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2777           "    Found PNG sRGB chunk.");
2778     }
2779
2780 #ifdef PNG_READ_iCCP_SUPPORTED
2781     if (ping_found_iCCP !=MagickTrue &&
2782       ping_found_sRGB != MagickTrue &&
2783       png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2784     {
2785       ping_found_iCCP=MagickTrue;
2786       if (logging != MagickFalse)
2787         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2788           "    Found PNG iCCP chunk.");
2789     }
2790
2791   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2792     {
2793       int
2794         compression;
2795
2796 #if (PNG_LIBPNG_VER < 10500)
2797       png_charp
2798         info;
2799 #else
2800       png_bytep
2801         info;
2802 #endif
2803
2804       png_charp
2805         name;
2806
2807       png_uint_32
2808         profile_length;
2809
2810       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2811         &profile_length);
2812
2813       if (profile_length != 0)
2814         {
2815           StringInfo
2816             *profile;
2817
2818           if (logging != MagickFalse)
2819             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2820               "    Reading PNG iCCP chunk.");
2821
2822           profile=BlobToStringInfo(info,profile_length);
2823
2824           if (profile == (StringInfo *) NULL)
2825           {
2826             png_warning(ping, "ICC profile is NULL");
2827             profile=DestroyStringInfo(profile);
2828           }
2829           else
2830           {
2831             if (ping_preserve_iCCP == MagickFalse)
2832             {
2833                  int
2834                    icheck,
2835                    got_crc=0;
2836
2837
2838                  png_uint_32
2839                    length,
2840                    profile_crc=0;
2841
2842                  unsigned char
2843                    *data;
2844
2845                  length=(png_uint_32) GetStringInfoLength(profile);
2846
2847                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2848                  {
2849                    if (length == sRGB_info[icheck].len)
2850                    {
2851                      if (got_crc == 0)
2852                      {
2853                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2854                          "    Got a %lu-byte ICC profile (potentially sRGB)",
2855                          (unsigned long) length);
2856
2857                        data=GetStringInfoDatum(profile);
2858                        profile_crc=crc32(0,data,length);
2859
2860                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2861                            "      with crc=%8x",(unsigned int) profile_crc);
2862                        got_crc++;
2863                      }
2864
2865                      if (profile_crc == sRGB_info[icheck].crc)
2866                      {
2867                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2868                             "      It is sRGB with rendering intent = %s",
2869                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
2870                              sRGB_info[icheck].intent));
2871                         if (image->rendering_intent==UndefinedIntent)
2872                         {
2873                           image->rendering_intent=
2874                           Magick_RenderingIntent_from_PNG_RenderingIntent(
2875                              sRGB_info[icheck].intent);
2876                         }
2877                         break;
2878                      }
2879                    }
2880                  }
2881                  if (sRGB_info[icheck].len == 0)
2882                  {
2883                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2884                         "    Got %lu-byte ICC profile not recognized as sRGB",
2885                         (unsigned long) length);
2886                     (void) SetImageProfile(image,"icc",profile,exception);
2887                  }
2888             }
2889             else /* Preserve-iCCP */
2890             {
2891                     (void) SetImageProfile(image,"icc",profile,exception);
2892             }
2893
2894             profile=DestroyStringInfo(profile);
2895           }
2896       }
2897     }
2898 #endif
2899
2900 #if defined(PNG_READ_sRGB_SUPPORTED)
2901   {
2902     if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2903         PNG_INFO_sRGB))
2904     {
2905       if (png_get_sRGB(ping,ping_info,&intent))
2906       {
2907         if (image->rendering_intent == UndefinedIntent)
2908           image->rendering_intent=
2909              Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2910
2911         if (logging != MagickFalse)
2912           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2913             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2914       }
2915     }
2916
2917     else if (mng_info->have_global_srgb)
2918       {
2919         if (image->rendering_intent == UndefinedIntent)
2920           image->rendering_intent=
2921             Magick_RenderingIntent_from_PNG_RenderingIntent
2922             (mng_info->global_srgb_intent);
2923       }
2924   }
2925 #endif
2926
2927
2928   {
2929      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2930        if (mng_info->have_global_gama)
2931          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2932
2933      if (png_get_gAMA(ping,ping_info,&file_gamma))
2934        {
2935          image->gamma=(float) file_gamma;
2936          if (logging != MagickFalse)
2937            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2938              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2939        }
2940   }
2941
2942   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2943     {
2944       if (mng_info->have_global_chrm != MagickFalse)
2945         {
2946           (void) png_set_cHRM(ping,ping_info,
2947             mng_info->global_chrm.white_point.x,
2948             mng_info->global_chrm.white_point.y,
2949             mng_info->global_chrm.red_primary.x,
2950             mng_info->global_chrm.red_primary.y,
2951             mng_info->global_chrm.green_primary.x,
2952             mng_info->global_chrm.green_primary.y,
2953             mng_info->global_chrm.blue_primary.x,
2954             mng_info->global_chrm.blue_primary.y);
2955         }
2956     }
2957
2958   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2959     {
2960       (void) png_get_cHRM(ping,ping_info,
2961         &image->chromaticity.white_point.x,
2962         &image->chromaticity.white_point.y,
2963         &image->chromaticity.red_primary.x,
2964         &image->chromaticity.red_primary.y,
2965         &image->chromaticity.green_primary.x,
2966         &image->chromaticity.green_primary.y,
2967         &image->chromaticity.blue_primary.x,
2968         &image->chromaticity.blue_primary.y);
2969
2970        ping_found_cHRM=MagickTrue;
2971
2972        if (image->chromaticity.red_primary.x>0.6399f &&
2973            image->chromaticity.red_primary.x<0.6401f &&
2974            image->chromaticity.red_primary.y>0.3299f &&
2975            image->chromaticity.red_primary.y<0.3301f &&
2976            image->chromaticity.green_primary.x>0.2999f &&
2977            image->chromaticity.green_primary.x<0.3001f &&
2978            image->chromaticity.green_primary.y>0.5999f &&
2979            image->chromaticity.green_primary.y<0.6001f &&
2980            image->chromaticity.blue_primary.x>0.1499f &&
2981            image->chromaticity.blue_primary.x<0.1501f &&
2982            image->chromaticity.blue_primary.y>0.0599f &&
2983            image->chromaticity.blue_primary.y<0.0601f &&
2984            image->chromaticity.white_point.x>0.3126f &&
2985            image->chromaticity.white_point.x<0.3128f &&
2986            image->chromaticity.white_point.y>0.3289f &&
2987            image->chromaticity.white_point.y<0.3291f)
2988           ping_found_sRGB_cHRM=MagickTrue;
2989     }
2990
2991   if (image->rendering_intent != UndefinedIntent)
2992     {
2993       if (ping_found_sRGB != MagickTrue &&
2994           (ping_found_gAMA != MagickTrue ||
2995           (image->gamma > .45 && image->gamma < .46)) &&
2996           (ping_found_cHRM != MagickTrue ||
2997           ping_found_sRGB_cHRM != MagickFalse) &&
2998           ping_found_iCCP != MagickTrue)
2999       {
3000          png_set_sRGB(ping,ping_info,
3001             Magick_RenderingIntent_to_PNG_RenderingIntent
3002             (image->rendering_intent));
3003          file_gamma=1.000f/2.200f;
3004          ping_found_sRGB=MagickTrue;
3005          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3006            "    Setting sRGB as if in input");
3007       }
3008     }
3009
3010 #if defined(PNG_oFFs_SUPPORTED)
3011   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3012     {
3013       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
3014       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
3015
3016       if (logging != MagickFalse)
3017         if (image->page.x || image->page.y)
3018           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3019             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
3020             image->page.x,(double) image->page.y);
3021     }
3022 #endif
3023 #if defined(PNG_pHYs_SUPPORTED)
3024   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3025     {
3026       if (mng_info->have_global_phys)
3027         {
3028           png_set_pHYs(ping,ping_info,
3029                        mng_info->global_x_pixels_per_unit,
3030                        mng_info->global_y_pixels_per_unit,
3031                        mng_info->global_phys_unit_type);
3032         }
3033     }
3034
3035   x_resolution=0;
3036   y_resolution=0;
3037   unit_type=0;
3038   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3039     {
3040       /*
3041         Set image resolution.
3042       */
3043       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
3044         &unit_type);
3045       image->resolution.x=(double) x_resolution;
3046       image->resolution.y=(double) y_resolution;
3047
3048       if (unit_type == PNG_RESOLUTION_METER)
3049         {
3050           image->units=PixelsPerCentimeterResolution;
3051           image->resolution.x=(double) x_resolution/100.0;
3052           image->resolution.y=(double) y_resolution/100.0;
3053         }
3054
3055       if (logging != MagickFalse)
3056         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3057           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
3058           (double) x_resolution,(double) y_resolution,unit_type);
3059     }
3060 #endif
3061
3062   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3063     {
3064       png_colorp
3065         palette;
3066
3067       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3068
3069       if ((number_colors == 0) &&
3070           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
3071         {
3072           if (mng_info->global_plte_length)
3073             {
3074               png_set_PLTE(ping,ping_info,mng_info->global_plte,
3075                 (int) mng_info->global_plte_length);
3076
3077               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3078               {
3079                 if (mng_info->global_trns_length)
3080                   {
3081                     png_warning(ping,
3082                       "global tRNS has more entries than global PLTE");
3083                   }
3084                 else
3085                   {
3086                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
3087                        (int) mng_info->global_trns_length,NULL);
3088                   }
3089                }
3090 #ifdef PNG_READ_bKGD_SUPPORTED
3091               if (
3092 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3093                    mng_info->have_saved_bkgd_index ||
3094 #endif
3095                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3096                     {
3097                       png_color_16
3098                          background;
3099
3100 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3101                       if (mng_info->have_saved_bkgd_index)
3102                         background.index=mng_info->saved_bkgd_index;
3103 #endif
3104                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
3105                         background.index=ping_background->index;
3106
3107                       background.red=(png_uint_16)
3108                         mng_info->global_plte[background.index].red;
3109
3110                       background.green=(png_uint_16)
3111                         mng_info->global_plte[background.index].green;
3112
3113                       background.blue=(png_uint_16)
3114                         mng_info->global_plte[background.index].blue;
3115
3116                       background.gray=(png_uint_16)
3117                         mng_info->global_plte[background.index].green;
3118
3119                       png_set_bKGD(ping,ping_info,&background);
3120                     }
3121 #endif
3122                 }
3123               else
3124                 png_error(ping,"No global PLTE in file");
3125             }
3126         }
3127
3128 #ifdef PNG_READ_bKGD_SUPPORTED
3129   if (mng_info->have_global_bkgd &&
3130           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
3131       image->background_color=mng_info->mng_global_bkgd;
3132
3133   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3134     {
3135       unsigned int
3136         bkgd_scale;
3137
3138       /* Set image background color.
3139        * Scale background components to 16-bit, then scale
3140        * to quantum depth
3141        */
3142
3143         bkgd_scale = 1;
3144
3145         if (ping_file_depth == 1)
3146            bkgd_scale = 255;
3147
3148         else if (ping_file_depth == 2)
3149            bkgd_scale = 85;
3150
3151         else if (ping_file_depth == 4)
3152            bkgd_scale = 17;
3153
3154         if (ping_file_depth <= 8)
3155            bkgd_scale *= 257;
3156
3157         ping_background->red *= bkgd_scale;
3158         ping_background->green *= bkgd_scale;
3159         ping_background->blue *= bkgd_scale;
3160
3161         if (logging != MagickFalse)
3162           {
3163             if (logging != MagickFalse)
3164               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3165                  "    Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
3166                  "    bkgd_scale=%d.  ping_background=(%d,%d,%d)",
3167                  ping_background->red,ping_background->green,
3168                  ping_background->blue,
3169                  bkgd_scale,ping_background->red,
3170                  ping_background->green,ping_background->blue);
3171           }
3172
3173         image->background_color.red=
3174             ScaleShortToQuantum(ping_background->red);
3175
3176         image->background_color.green=
3177             ScaleShortToQuantum(ping_background->green);
3178
3179         image->background_color.blue=
3180           ScaleShortToQuantum(ping_background->blue);
3181
3182         image->background_color.alpha=OpaqueAlpha;
3183
3184         if (logging != MagickFalse)
3185           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3186             "    image->background_color=(%.20g,%.20g,%.20g).",
3187             (double) image->background_color.red,
3188             (double) image->background_color.green,
3189             (double) image->background_color.blue);
3190     }
3191 #endif /* PNG_READ_bKGD_SUPPORTED */
3192
3193   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3194     {
3195       /*
3196         Image has a tRNS chunk.
3197       */
3198       int
3199         max_sample;
3200
3201       size_t
3202         one=1;
3203
3204       if (logging != MagickFalse)
3205         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3206           "    Reading PNG tRNS chunk.");
3207
3208       max_sample = (int) ((one << ping_file_depth) - 1);
3209
3210       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3211           (int)ping_trans_color->gray > max_sample) ||
3212           (ping_color_type == PNG_COLOR_TYPE_RGB &&
3213           ((int)ping_trans_color->red > max_sample ||
3214           (int)ping_trans_color->green > max_sample ||
3215           (int)ping_trans_color->blue > max_sample)))
3216         {
3217           if (logging != MagickFalse)
3218             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3219               "    Ignoring PNG tRNS chunk with out-of-range sample.");
3220           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3221           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3222           image->alpha_trait=UndefinedPixelTrait;
3223         }
3224       else
3225         {
3226           int
3227             scale_to_short;
3228
3229           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3230
3231           /* Scale transparent_color to short */
3232           transparent_color.red= scale_to_short*ping_trans_color->red;
3233           transparent_color.green= scale_to_short*ping_trans_color->green;
3234           transparent_color.blue= scale_to_short*ping_trans_color->blue;
3235           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3236
3237           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3238             {
3239               if (logging != MagickFalse)
3240               {
3241                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3242                   "    Raw tRNS graylevel = %d, scaled graylevel = %d.",
3243                   (int) ping_trans_color->gray,(int) transparent_color.alpha);
3244
3245               }
3246               transparent_color.red=transparent_color.alpha;
3247               transparent_color.green=transparent_color.alpha;
3248               transparent_color.blue=transparent_color.alpha;
3249             }
3250         }
3251     }
3252 #if defined(PNG_READ_sBIT_SUPPORTED)
3253   if (mng_info->have_global_sbit)
3254     {
3255       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3256         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3257     }
3258 #endif
3259   num_passes=png_set_interlace_handling(ping);
3260
3261   png_read_update_info(ping,ping_info);
3262
3263   ping_rowbytes=png_get_rowbytes(ping,ping_info);
3264
3265   /*
3266     Initialize image structure.
3267   */
3268   mng_info->image_box.left=0;
3269   mng_info->image_box.right=(ssize_t) ping_width;
3270   mng_info->image_box.top=0;
3271   mng_info->image_box.bottom=(ssize_t) ping_height;
3272   if (mng_info->mng_type == 0)
3273     {
3274       mng_info->mng_width=ping_width;
3275       mng_info->mng_height=ping_height;
3276       mng_info->frame=mng_info->image_box;
3277       mng_info->clip=mng_info->image_box;
3278     }
3279
3280   else
3281     {
3282       image->page.y=mng_info->y_off[mng_info->object_id];
3283     }
3284
3285   image->compression=ZipCompression;
3286   image->columns=ping_width;
3287   image->rows=ping_height;
3288
3289   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3290       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3291     {
3292       double
3293         image_gamma = image->gamma;
3294
3295       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3296          "    image->gamma=%f",(float) image_gamma);
3297
3298       if (image_gamma > 0.75)
3299         {
3300           /* Set image->rendering_intent to Undefined,
3301            * image->colorspace to GRAY, and reset image->chromaticity.
3302            */
3303           image->intensity = Rec709LuminancePixelIntensityMethod;
3304           SetImageColorspace(image,GRAYColorspace,exception);
3305         }
3306       else
3307         {
3308           RenderingIntent
3309             save_rendering_intent = image->rendering_intent;
3310           ChromaticityInfo
3311             save_chromaticity = image->chromaticity;
3312
3313           SetImageColorspace(image,GRAYColorspace,exception);
3314           image->rendering_intent = save_rendering_intent;
3315           image->chromaticity = save_chromaticity;
3316         }
3317
3318       image->gamma = image_gamma;
3319     }
3320
3321   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3322       "    image->colorspace=%d",(int) image->colorspace);
3323
3324   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3325       ((int) ping_bit_depth < 16 &&
3326       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3327     {
3328       size_t
3329         one;
3330
3331       image->storage_class=PseudoClass;
3332       one=1;
3333       image->colors=one << ping_file_depth;
3334 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3335       if (image->colors > 256)
3336         image->colors=256;
3337 #else
3338       if (image->colors > 65536L)
3339         image->colors=65536L;
3340 #endif
3341       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3342         {
3343           png_colorp
3344             palette;
3345
3346           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3347           image->colors=(size_t) number_colors;
3348
3349           if (logging != MagickFalse)
3350             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3351               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3352         }
3353     }
3354
3355   if (image->storage_class == PseudoClass)
3356     {
3357       /*
3358         Initialize image colormap.
3359       */
3360       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3361         png_error(ping,"Memory allocation failed");
3362
3363       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3364         {
3365           png_colorp
3366             palette;
3367
3368           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3369
3370           for (i=0; i < (ssize_t) number_colors; i++)
3371           {
3372             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3373             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3374             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3375           }
3376
3377           for ( ; i < (ssize_t) image->colors; i++)
3378           {
3379             image->colormap[i].red=0;
3380             image->colormap[i].green=0;
3381             image->colormap[i].blue=0;
3382           }
3383         }
3384
3385       else
3386         {
3387           Quantum
3388             scale;
3389
3390           scale = (Quantum) (65535.0/((1UL << ping_file_depth)-1.0));
3391
3392 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
3393           scale = ScaleShortToQuantum(scale);
3394 #endif
3395
3396           for (i=0; i < (ssize_t) image->colors; i++)
3397           {
3398             image->colormap[i].red=(Quantum) (i*scale);
3399             image->colormap[i].green=(Quantum) (i*scale);
3400             image->colormap[i].blue=(Quantum) (i*scale);
3401           }
3402        }
3403     }
3404
3405    /* Set some properties for reporting by "identify" */
3406     {
3407       char
3408         msg[MagickPathExtent];
3409
3410      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3411         ping_interlace_method in value */
3412
3413      (void) FormatLocaleString(msg,MagickPathExtent,
3414          "%d, %d",(int) ping_width, (int) ping_height);
3415      (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3416
3417      (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3418         (int) ping_file_depth);
3419      (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3420
3421      (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3422          (int) ping_color_type,
3423          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3424      (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3425
3426      if (ping_interlace_method == 0)
3427        {
3428          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3429             (int) ping_interlace_method);
3430        }
3431      else if (ping_interlace_method == 1)
3432        {
3433          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3434             (int) ping_interlace_method);
3435        }
3436      else
3437        {
3438          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3439             (int) ping_interlace_method);
3440        }
3441        (void) SetImageProperty(image,"png:IHDR.interlace_method",
3442          msg,exception);
3443
3444      if (number_colors != 0)
3445        {
3446          (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3447             (int) number_colors);
3448          (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3449             exception);
3450        }
3451    }
3452 #if defined(PNG_tIME_SUPPORTED)
3453    read_tIME_chunk(image,ping,ping_info,exception);
3454 #endif
3455
3456
3457   /*
3458     Read image scanlines.
3459   */
3460   if (image->delay != 0)
3461     mng_info->scenes_found++;
3462
3463   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3464       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3465       (image_info->first_scene+image_info->number_scenes))))
3466     {
3467       /* This happens later in non-ping decodes */
3468       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3469         image->storage_class=DirectClass;
3470       image->alpha_trait=
3471         (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3472          ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3473          (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3474         BlendPixelTrait : UndefinedPixelTrait;
3475
3476       if (logging != MagickFalse)
3477         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3478           "    Skipping PNG image data for scene %.20g",(double)
3479           mng_info->scenes_found-1);
3480       png_destroy_read_struct(&ping,&ping_info,&end_info);
3481
3482 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3483       UnlockSemaphoreInfo(ping_semaphore);
3484 #endif
3485
3486       if (logging != MagickFalse)
3487         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3488           "  exit ReadOnePNGImage().");
3489
3490       return(image);
3491     }
3492
3493   if (logging != MagickFalse)
3494     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3495       "    Reading PNG IDAT chunk(s)");
3496
3497   status=SetImageExtent(image,image->columns,image->rows,exception);
3498   if (status == MagickFalse)
3499     return(DestroyImageList(image));
3500
3501   if (num_passes > 1)
3502     pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3503       sizeof(*ping_pixels));
3504   else
3505     pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3506
3507   if (pixel_info == (MemoryInfo *) NULL)
3508     png_error(ping,"Memory allocation failed");
3509   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3510
3511   if (logging != MagickFalse)
3512     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3513       "    Converting PNG pixels to pixel packets");
3514   /*
3515     Convert PNG pixels to pixel packets.
3516   */
3517   quantum_info=AcquireQuantumInfo(image_info,image);
3518
3519   if (quantum_info == (QuantumInfo *) NULL)
3520      png_error(ping,"Failed to allocate quantum_info");
3521
3522   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3523
3524   {
3525
3526    MagickBooleanType
3527      found_transparent_pixel;
3528
3529   found_transparent_pixel=MagickFalse;
3530
3531   if (image->storage_class == DirectClass)
3532     {
3533       for (pass=0; pass < num_passes; pass++)
3534       {
3535         /*
3536           Convert image to DirectClass pixel packets.
3537         */
3538         image->alpha_trait=
3539             (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3540             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3541             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3542             BlendPixelTrait : UndefinedPixelTrait;
3543
3544         for (y=0; y < (ssize_t) image->rows; y++)
3545         {
3546           if (num_passes > 1)
3547             row_offset=ping_rowbytes*y;
3548
3549           else
3550             row_offset=0;
3551
3552           png_read_row(ping,ping_pixels+row_offset,NULL);
3553
3554           if (pass < num_passes-1)
3555             continue;
3556
3557           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3558
3559           if (q == (Quantum *) NULL)
3560             break;
3561
3562           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3563             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3564               GrayQuantum,ping_pixels+row_offset,exception);
3565
3566           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3567             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3568               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3569
3570           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3571             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3572               RGBAQuantum,ping_pixels+row_offset,exception);
3573
3574           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3575             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3576               IndexQuantum,ping_pixels+row_offset,exception);
3577
3578           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3579             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3580               RGBQuantum,ping_pixels+row_offset,exception);
3581
3582           if (found_transparent_pixel == MagickFalse)
3583             {
3584               /* Is there a transparent pixel in the row? */
3585               if (y== 0 && logging != MagickFalse)
3586                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3587                    "    Looking for cheap transparent pixel");
3588
3589               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3590               {
3591                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3592                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3593                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3594                   {
3595                     if (logging != MagickFalse)
3596                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3597                         "    ...got one.");
3598
3599                     found_transparent_pixel = MagickTrue;
3600                     break;
3601                   }
3602                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3603                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3604                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3605                     transparent_color.red &&
3606                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3607                     transparent_color.green &&
3608                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3609                     transparent_color.blue))
3610                   {
3611                     if (logging != MagickFalse)
3612                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3613                         "    ...got one.");
3614                     found_transparent_pixel = MagickTrue;
3615                     break;
3616                   }
3617                 q+=GetPixelChannels(image);
3618               }
3619             }
3620
3621           if (num_passes == 1)
3622             {
3623               status=SetImageProgress(image,LoadImageTag,
3624                   (MagickOffsetType) y, image->rows);
3625
3626               if (status == MagickFalse)
3627                 break;
3628             }
3629           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3630             break;
3631         }
3632
3633         if (num_passes != 1)
3634           {
3635             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3636             if (status == MagickFalse)
3637               break;
3638           }
3639       }
3640     }
3641
3642   else /* image->storage_class != DirectClass */
3643
3644     for (pass=0; pass < num_passes; pass++)
3645     {
3646       Quantum
3647         *quantum_scanline;
3648
3649       register Quantum
3650         *r;
3651
3652       /*
3653         Convert grayscale image to PseudoClass pixel packets.
3654       */
3655       if (logging != MagickFalse)
3656         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3657           "    Converting grayscale pixels to pixel packets");
3658
3659       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3660         BlendPixelTrait : UndefinedPixelTrait;
3661
3662       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3663         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3664         sizeof(*quantum_scanline));
3665
3666       if (quantum_scanline == (Quantum *) NULL)
3667         png_error(ping,"Memory allocation failed");
3668
3669       for (y=0; y < (ssize_t) image->rows; y++)
3670       {
3671         Quantum
3672            alpha;
3673
3674         if (num_passes > 1)
3675           row_offset=ping_rowbytes*y;
3676
3677         else
3678           row_offset=0;
3679
3680         png_read_row(ping,ping_pixels+row_offset,NULL);
3681
3682         if (pass < num_passes-1)
3683           continue;
3684
3685         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3686
3687         if (q == (Quantum *) NULL)
3688           break;
3689
3690         p=ping_pixels+row_offset;
3691         r=quantum_scanline;
3692
3693         switch (ping_bit_depth)
3694         {
3695           case 8:
3696           {
3697
3698             if (ping_color_type == 4)
3699               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3700               {
3701                 *r++=*p++;
3702
3703                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3704
3705                 SetPixelAlpha(image,alpha,q);
3706
3707                 if (alpha != OpaqueAlpha)
3708                   found_transparent_pixel = MagickTrue;
3709
3710                 q+=GetPixelChannels(image);
3711               }
3712
3713             else
3714               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3715                 *r++=*p++;
3716
3717             break;
3718           }
3719
3720           case 16:
3721           {
3722             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3723             {
3724 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3725               unsigned short
3726                 quantum;
3727
3728               if (image->colors > 256)
3729                 quantum=((*p++) << 8);
3730
3731               else
3732                 quantum=0;
3733
3734               quantum|=(*p++);
3735               *r=ScaleShortToQuantum(quantum);
3736               r++;
3737
3738               if (ping_color_type == 4)
3739                 {
3740                   if (image->colors > 256)
3741                     quantum=((*p++) << 8);
3742                   else
3743                     quantum=0;
3744
3745                   quantum|=(*p++);
3746
3747                   alpha=ScaleShortToQuantum(quantum);
3748                   SetPixelAlpha(image,alpha,q);
3749
3750                   if (alpha != OpaqueAlpha)
3751                     found_transparent_pixel = MagickTrue;
3752
3753                   q+=GetPixelChannels(image);
3754                 }
3755
3756 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3757               *r++=(*p++);
3758               p++; /* strip low byte */
3759
3760               if (ping_color_type == 4)
3761                 {
3762                   SetPixelAlpha(image,*p++,q);
3763
3764                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3765                     found_transparent_pixel = MagickTrue;
3766
3767                   p++;
3768                   q+=GetPixelChannels(image);
3769                 }
3770 #endif
3771             }
3772
3773             break;
3774           }
3775
3776           default:
3777             break;
3778         }
3779
3780         /*
3781           Transfer image scanline.
3782         */
3783         r=quantum_scanline;
3784
3785         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3786
3787         if (q == (Quantum *) NULL)
3788           break;
3789         for (x=0; x < (ssize_t) image->columns; x++)
3790         {
3791           SetPixelIndex(image,*r++,q);
3792           q+=GetPixelChannels(image);
3793         }
3794
3795         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3796           break;
3797
3798         if (num_passes == 1)
3799           {
3800             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3801               image->rows);
3802
3803             if (status == MagickFalse)
3804               break;
3805           }
3806       }
3807
3808       if (num_passes != 1)
3809         {
3810           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3811
3812           if (status == MagickFalse)
3813             break;
3814         }
3815
3816       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3817     }
3818
3819     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3820       UndefinedPixelTrait;
3821
3822     if (logging != MagickFalse)
3823       {
3824         if (found_transparent_pixel != MagickFalse)
3825           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3826             "    Found transparent pixel");
3827         else
3828           {
3829             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3830               "    No transparent pixel was found");
3831
3832             ping_color_type&=0x03;
3833           }
3834       }
3835     }
3836
3837   if (quantum_info != (QuantumInfo *) NULL)
3838     quantum_info=DestroyQuantumInfo(quantum_info);
3839
3840   if (image->storage_class == PseudoClass)
3841     {
3842       PixelTrait
3843         alpha_trait;
3844
3845       alpha_trait=image->alpha_trait;
3846       image->alpha_trait=UndefinedPixelTrait;
3847       (void) SyncImage(image,exception);
3848       image->alpha_trait=alpha_trait;
3849     }
3850
3851   png_read_end(ping,end_info);
3852
3853   if (logging != MagickFalse)
3854   {
3855     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3856        "  image->storage_class=%d\n",(int) image->storage_class);
3857   }
3858
3859   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3860       (ssize_t) image_info->first_scene && image->delay != 0)
3861     {
3862       png_destroy_read_struct(&ping,&ping_info,&end_info);
3863       pixel_info=RelinquishVirtualMemory(pixel_info);
3864       image->colors=2;
3865       (void) SetImageBackgroundColor(image,exception);
3866 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3867       UnlockSemaphoreInfo(ping_semaphore);
3868 #endif
3869       if (logging != MagickFalse)
3870         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3871           "  exit ReadOnePNGImage() early.");
3872       return(image);
3873     }
3874
3875   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3876     {
3877       ClassType
3878         storage_class;
3879
3880       /*
3881         Image has a transparent background.
3882       */
3883       storage_class=image->storage_class;
3884       image->alpha_trait=BlendPixelTrait;
3885
3886 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3887
3888       if (storage_class == PseudoClass)
3889         {
3890           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3891             {
3892               for (x=0; x < ping_num_trans; x++)
3893               {
3894                  image->colormap[x].alpha_trait=BlendPixelTrait;
3895                  image->colormap[x].alpha =
3896                    ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3897               }
3898             }
3899
3900           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3901             {
3902               for (x=0; x < (int) image->colors; x++)
3903               {
3904                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3905                      transparent_color.alpha)
3906                  {
3907                     image->colormap[x].alpha_trait=BlendPixelTrait;
3908                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3909                  }
3910               }
3911             }
3912           (void) SyncImage(image,exception);
3913         }
3914
3915 #if 1 /* Should have already been done above, but glennrp problem P10
3916        * needs this.
3917        */
3918       else
3919         {
3920           for (y=0; y < (ssize_t) image->rows; y++)
3921           {
3922             image->storage_class=storage_class;
3923             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3924
3925             if (q == (Quantum *) NULL)
3926               break;
3927
3928
3929             /* Caution: on a Q8 build, this does not distinguish between
3930              * 16-bit colors that differ only in the low byte
3931              */
3932             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3933             {
3934               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3935                   transparent_color.red &&
3936                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3937                   transparent_color.green &&
3938                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3939                   transparent_color.blue)
3940                 {
3941                   SetPixelAlpha(image,TransparentAlpha,q);
3942                 }
3943
3944 #if 0 /* I have not found a case where this is needed. */
3945               else
3946                 {
3947                   SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3948                 }
3949 #endif
3950
3951               q+=GetPixelChannels(image);
3952             }
3953
3954             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3955                break;
3956           }
3957         }
3958 #endif
3959
3960       image->storage_class=DirectClass;
3961     }
3962
3963   for (j = 0; j < 2; j++)
3964   {
3965     if (j == 0)
3966       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3967           MagickTrue : MagickFalse;
3968     else
3969       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3970           MagickTrue : MagickFalse;
3971
3972     if (status != MagickFalse)
3973       for (i=0; i < (ssize_t) num_text; i++)
3974       {
3975         /* Check for a profile */
3976
3977         if (logging != MagickFalse)
3978           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3979             "    Reading PNG text chunk");
3980
3981         if (strlen(text[i].key) > 16 &&
3982             memcmp(text[i].key, "Raw profile type ",17) == 0)
3983           {
3984             const char
3985               *value;
3986
3987             value=GetImageOption(image_info,"profile:skip");
3988
3989             if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3990             {
3991                (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3992                   (int) i,exception);
3993                num_raw_profiles++;
3994                if (logging != MagickFalse)
3995                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3996                    "    Read raw profile %s",text[i].key+17);
3997             }
3998             else
3999             {
4000                if (logging != MagickFalse)
4001                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4002                    "    Skipping raw profile %s",text[i].key+17);
4003             }
4004           }
4005
4006         else
4007           {
4008             char
4009               *value;
4010
4011             length=text[i].text_length;
4012             value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
4013               sizeof(*value));
4014             if (value == (char *) NULL)
4015               {
4016                 png_error(ping,"Memory allocation failed");
4017                 break;
4018               }
4019             *value='\0';
4020             (void) ConcatenateMagickString(value,text[i].text,length+2);
4021
4022             /* Don't save "density" or "units" property if we have a pHYs
4023              * chunk
4024              */
4025             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
4026                 (LocaleCompare(text[i].key,"density") != 0 &&
4027                 LocaleCompare(text[i].key,"units") != 0))
4028                (void) SetImageProperty(image,text[i].key,value,exception);
4029
4030             if (logging != MagickFalse)
4031             {
4032               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4033                 "      length: %lu\n"
4034                 "      Keyword: %s",
4035                 (unsigned long) length,
4036                 text[i].key);
4037             }
4038
4039             value=DestroyString(value);
4040           }
4041       }
4042     num_text_total += num_text;
4043   }
4044
4045 #ifdef MNG_OBJECT_BUFFERS
4046   /*
4047     Store the object if necessary.
4048   */
4049   if (object_id && !mng_info->frozen[object_id])
4050     {
4051       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
4052         {
4053           /*
4054             create a new object buffer.
4055           */
4056           mng_info->ob[object_id]=(MngBuffer *)
4057             AcquireMagickMemory(sizeof(MngBuffer));
4058
4059           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
4060             {
4061               mng_info->ob[object_id]->image=(Image *) NULL;
4062               mng_info->ob[object_id]->reference_count=1;
4063             }
4064         }
4065
4066       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
4067           mng_info->ob[object_id]->frozen)
4068         {
4069           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
4070              png_error(ping,"Memory allocation failed");
4071
4072           if (mng_info->ob[object_id]->frozen)
4073             png_error(ping,"Cannot overwrite frozen MNG object buffer");
4074         }
4075
4076       else
4077         {
4078
4079           if (mng_info->ob[object_id]->image != (Image *) NULL)
4080             mng_info->ob[object_id]->image=DestroyImage
4081                 (mng_info->ob[object_id]->image);
4082
4083           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
4084             exception);
4085
4086           if (mng_info->ob[object_id]->image != (Image *) NULL)
4087             mng_info->ob[object_id]->image->file=(FILE *) NULL;
4088
4089           else
4090             png_error(ping, "Cloning image for object buffer failed");
4091
4092           if (ping_width > 250000L || ping_height > 250000L)
4093              png_error(ping,"PNG Image dimensions are too large.");
4094
4095           mng_info->ob[object_id]->width=ping_width;
4096           mng_info->ob[object_id]->height=ping_height;
4097           mng_info->ob[object_id]->color_type=ping_color_type;
4098           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
4099           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
4100           mng_info->ob[object_id]->compression_method=
4101              ping_compression_method;
4102           mng_info->ob[object_id]->filter_method=ping_filter_method;
4103
4104           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
4105             {
4106               png_colorp
4107                 plte;
4108
4109               /*
4110                 Copy the PLTE to the object buffer.
4111               */
4112               png_get_PLTE(ping,ping_info,&plte,&number_colors);
4113               mng_info->ob[object_id]->plte_length=number_colors;
4114
4115               for (i=0; i < number_colors; i++)
4116               {
4117                 mng_info->ob[object_id]->plte[i]=plte[i];
4118               }
4119             }
4120
4121           else
4122               mng_info->ob[object_id]->plte_length=0;
4123         }
4124     }
4125 #endif
4126
4127    /* Set image->alpha_trait to MagickTrue if the input colortype supports
4128     * alpha or if a valid tRNS chunk is present, no matter whether there
4129     * is actual transparency present.
4130     */
4131     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
4132         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
4133         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
4134         BlendPixelTrait : UndefinedPixelTrait;
4135
4136 #if 0  /* I'm not sure what's wrong here but it does not work. */
4137     if (image->alpha_trait != UndefinedPixelTrait)
4138     {
4139       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
4140         (void) SetImageType(image,GrayscaleAlphaType,exception);
4141
4142       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4143         (void) SetImageType(image,PaletteAlphaType,exception);
4144
4145       else
4146         (void) SetImageType(image,TrueColorAlphaType,exception);
4147     }
4148
4149     else
4150     {
4151       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
4152         (void) SetImageType(image,GrayscaleType,exception);
4153
4154       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4155         (void) SetImageType(image,PaletteType,exception);
4156
4157       else
4158         (void) SetImageType(image,TrueColorType,exception);
4159     }
4160 #endif
4161
4162    /* Set more properties for identify to retrieve */
4163    {
4164      char
4165        msg[MagickPathExtent];
4166
4167      if (num_text_total != 0)
4168        {
4169          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
4170          (void) FormatLocaleString(msg,MagickPathExtent,
4171             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
4172          (void) SetImageProperty(image,"png:text",msg,
4173                 exception);
4174        }
4175
4176      if (num_raw_profiles != 0)
4177        {
4178          (void) FormatLocaleString(msg,MagickPathExtent,
4179             "%d were found", num_raw_profiles);
4180          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
4181                 exception);
4182        }
4183
4184      /* cHRM chunk: */
4185      if (ping_found_cHRM != MagickFalse)
4186        {
4187          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4188             "chunk was found (see Chromaticity, above)");
4189          (void) SetImageProperty(image,"png:cHRM",msg,
4190                 exception);
4191        }
4192
4193      /* bKGD chunk: */
4194      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
4195        {
4196          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4197             "chunk was found (see Background color, above)");
4198          (void) SetImageProperty(image,"png:bKGD",msg,
4199                 exception);
4200        }
4201
4202      (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4203         "chunk was found");
4204
4205 #if defined(PNG_iCCP_SUPPORTED)
4206      /* iCCP chunk: */
4207      if (ping_found_iCCP != MagickFalse)
4208         (void) SetImageProperty(image,"png:iCCP",msg,
4209                 exception);
4210 #endif
4211
4212      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
4213         (void) SetImageProperty(image,"png:tRNS",msg,
4214                 exception);
4215
4216 #if defined(PNG_sRGB_SUPPORTED)
4217      /* sRGB chunk: */
4218      if (ping_found_sRGB != MagickFalse)
4219        {
4220          (void) FormatLocaleString(msg,MagickPathExtent,
4221             "intent=%d (%s)",
4222             (int) intent,
4223             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
4224          (void) SetImageProperty(image,"png:sRGB",msg,
4225                  exception);
4226        }
4227 #endif
4228
4229      /* gAMA chunk: */
4230      if (ping_found_gAMA != MagickFalse)
4231        {
4232          (void) FormatLocaleString(msg,MagickPathExtent,
4233             "gamma=%.8g (See Gamma, above)",
4234             file_gamma);
4235          (void) SetImageProperty(image,"png:gAMA",msg,
4236                 exception);
4237        }
4238
4239 #if defined(PNG_pHYs_SUPPORTED)
4240      /* pHYs chunk: */
4241      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
4242        {
4243          (void) FormatLocaleString(msg,MagickPathExtent,
4244             "x_res=%.10g, y_res=%.10g, units=%d",
4245             (double) x_resolution,(double) y_resolution, unit_type);
4246          (void) SetImageProperty(image,"png:pHYs",msg,
4247                 exception);
4248        }
4249 #endif
4250
4251 #if defined(PNG_oFFs_SUPPORTED)
4252      /* oFFs chunk: */
4253      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4254        {
4255          (void) FormatLocaleString(msg,MagickPathExtent,
4256             "x_off=%.20g, y_off=%.20g",
4257             (double) image->page.x,(double) image->page.y);
4258          (void) SetImageProperty(image,"png:oFFs",msg,
4259                 exception);
4260        }
4261 #endif
4262
4263 #if defined(PNG_tIME_SUPPORTED)
4264      read_tIME_chunk(image,ping,end_info,exception);
4265 #endif
4266
4267      /* caNv chunk: */
4268      if ((image->page.width != 0 && image->page.width != image->columns) ||
4269          (image->page.height != 0 && image->page.height != image->rows) ||
4270          (image->page.x != 0 || image->page.y != 0))
4271        {
4272          (void) FormatLocaleString(msg,MagickPathExtent,
4273             "width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
4274             (double) image->page.width,(double) image->page.height,
4275             (double) image->page.x,(double) image->page.y);
4276          (void) SetImageProperty(image,"png:caNv",msg,
4277                 exception);
4278        }
4279
4280      /* vpAg chunk: */
4281      if ((image->page.width != 0 && image->page.width != image->columns) ||
4282          (image->page.height != 0 && image->page.height != image->rows))
4283        {
4284          (void) FormatLocaleString(msg,MagickPathExtent,
4285             "width=%.20g, height=%.20g",
4286             (double) image->page.width,(double) image->page.height);
4287          (void) SetImageProperty(image,"png:vpAg",msg,
4288                 exception);
4289        }
4290    }
4291
4292   /*
4293     Relinquish resources.
4294   */
4295   png_destroy_read_struct(&ping,&ping_info,&end_info);
4296
4297   pixel_info=RelinquishVirtualMemory(pixel_info);
4298
4299   if (logging != MagickFalse)
4300     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4301       "  exit ReadOnePNGImage()");
4302
4303 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4304   UnlockSemaphoreInfo(ping_semaphore);
4305 #endif
4306
4307   /* }  for navigation to beginning of SETJMP-protected block, revert to
4308    *    Throwing an Exception when an error occurs.
4309    */
4310
4311   return(image);
4312
4313 /* end of reading one PNG image */
4314 }
4315
4316 static Image *ReadPNGImage(const ImageInfo *image_info,
4317   ExceptionInfo *exception)
4318 {
4319   Image
4320     *image;
4321
4322   MagickBooleanType
4323     have_mng_structure,
4324     logging,
4325     status;
4326
4327   MngInfo
4328     *mng_info;
4329
4330   char
4331     magic_number[MagickPathExtent];
4332
4333   ssize_t
4334     count;
4335
4336   /*
4337     Open image file.
4338   */
4339   assert(image_info != (const ImageInfo *) NULL);
4340   assert(image_info->signature == MagickCoreSignature);
4341
4342   if (image_info->debug != MagickFalse)
4343     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4344       image_info->filename);
4345
4346   assert(exception != (ExceptionInfo *) NULL);
4347   assert(exception->signature == MagickCoreSignature);
4348   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4349   image=AcquireImage(image_info,exception);
4350   mng_info=(MngInfo *) NULL;
4351   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4352
4353   if (status == MagickFalse)
4354     ThrowReaderException(FileOpenError,"UnableToOpenFile");
4355
4356   /*
4357     Verify PNG signature.
4358   */
4359   count=ReadBlob(image,8,(unsigned char *) magic_number);
4360
4361   if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4362     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4363
4364   /*
4365     Allocate a MngInfo structure.
4366   */
4367   have_mng_structure=MagickFalse;
4368   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4369
4370   if (mng_info == (MngInfo *) NULL)
4371     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4372
4373   /*
4374     Initialize members of the MngInfo structure.
4375   */
4376   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4377   mng_info->image=image;
4378   have_mng_structure=MagickTrue;
4379
4380   image=ReadOnePNGImage(mng_info,image_info,exception);
4381   MngInfoFreeStruct(mng_info,&have_mng_structure);
4382
4383   if (image == (Image *) NULL)
4384     {
4385       if (logging != MagickFalse)
4386         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4387           "exit ReadPNGImage() with error");
4388
4389       return((Image *) NULL);
4390     }
4391
4392   (void) CloseBlob(image);
4393
4394   if ((image->columns == 0) || (image->rows == 0))
4395     {
4396       if (logging != MagickFalse)
4397         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4398           "exit ReadPNGImage() with error.");
4399
4400       ThrowReaderException(CorruptImageError,"CorruptImage");
4401     }
4402
4403   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4404       ((image->gamma < .45) || (image->gamma > .46)) &&
4405            !(image->chromaticity.red_primary.x>0.6399f &&
4406            image->chromaticity.red_primary.x<0.6401f &&
4407            image->chromaticity.red_primary.y>0.3299f &&
4408            image->chromaticity.red_primary.y<0.3301f &&
4409            image->chromaticity.green_primary.x>0.2999f &&
4410            image->chromaticity.green_primary.x<0.3001f &&
4411            image->chromaticity.green_primary.y>0.5999f &&
4412            image->chromaticity.green_primary.y<0.6001f &&
4413            image->chromaticity.blue_primary.x>0.1499f &&
4414            image->chromaticity.blue_primary.x<0.1501f &&
4415            image->chromaticity.blue_primary.y>0.0599f &&
4416            image->chromaticity.blue_primary.y<0.0601f &&
4417            image->chromaticity.white_point.x>0.3126f &&
4418            image->chromaticity.white_point.x<0.3128f &&
4419            image->chromaticity.white_point.y>0.3289f &&
4420            image->chromaticity.white_point.y<0.3291f))
4421     {
4422        SetImageColorspace(image,RGBColorspace,exception);
4423     }
4424
4425   if (logging != MagickFalse)
4426     {
4427        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4428            "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4429                (double) image->page.width,(double) image->page.height,
4430                (double) image->page.x,(double) image->page.y);
4431        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4432            "  image->colorspace: %d", (int) image->colorspace);
4433     }
4434
4435   if (logging != MagickFalse)
4436     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4437
4438   return(image);
4439 }
4440
4441
4442
4443 #if defined(JNG_SUPPORTED)
4444 /*
4445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4446 %                                                                             %
4447 %                                                                             %
4448 %                                                                             %
4449 %   R e a d O n e J N G I m a g e                                             %
4450 %                                                                             %
4451 %                                                                             %
4452 %                                                                             %
4453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4454 %
4455 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4456 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
4457 %  necessary for the new Image structure and returns a pointer to the new
4458 %  image.
4459 %
4460 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4461 %
4462 %  The format of the ReadOneJNGImage method is:
4463 %
4464 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4465 %         ExceptionInfo *exception)
4466 %
4467 %  A description of each parameter follows:
4468 %
4469 %    o mng_info: Specifies a pointer to a MngInfo structure.
4470 %
4471 %    o image_info: the image info.
4472 %
4473 %    o exception: return any errors or warnings in this structure.
4474 %
4475 */
4476 static Image *ReadOneJNGImage(MngInfo *mng_info,
4477     const ImageInfo *image_info, ExceptionInfo *exception)
4478 {
4479   Image
4480     *alpha_image,
4481     *color_image,
4482     *image,
4483     *jng_image;
4484
4485   ImageInfo
4486     *alpha_image_info,
4487     *color_image_info;
4488
4489   MagickBooleanType
4490     logging;
4491
4492   ssize_t
4493     y;
4494
4495   MagickBooleanType
4496     status;
4497
4498   png_uint_32
4499     jng_height,
4500     jng_width;
4501
4502   png_byte
4503     jng_color_type,
4504     jng_image_sample_depth,
4505     jng_image_compression_method,
4506     jng_image_interlace_method,
4507     jng_alpha_sample_depth,
4508     jng_alpha_compression_method,
4509     jng_alpha_filter_method,
4510     jng_alpha_interlace_method;
4511
4512   register const Quantum
4513     *s;
4514
4515   register ssize_t
4516     i,
4517     x;
4518
4519   register Quantum
4520     *q;
4521
4522   register unsigned char
4523     *p;
4524
4525   unsigned int
4526     read_JSEP,
4527     reading_idat;
4528
4529   size_t
4530     length;
4531
4532   jng_alpha_compression_method=0;
4533   jng_alpha_sample_depth=8;
4534   jng_color_type=0;
4535   jng_height=0;
4536   jng_width=0;
4537   alpha_image=(Image *) NULL;
4538   color_image=(Image *) NULL;
4539   alpha_image_info=(ImageInfo *) NULL;
4540   color_image_info=(ImageInfo *) NULL;
4541
4542   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4543     "  Enter ReadOneJNGImage()");
4544
4545   image=mng_info->image;
4546
4547   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4548     {
4549       /*
4550         Allocate next image structure.
4551       */
4552       if (logging != MagickFalse)
4553         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4554            "  AcquireNextImage()");
4555
4556       AcquireNextImage(image_info,image,exception);
4557
4558       if (GetNextImageInList(image) == (Image *) NULL)
4559         return((Image *) NULL);
4560
4561       image=SyncNextImageInList(image);
4562     }
4563   mng_info->image=image;
4564
4565   /*
4566     Signature bytes have already been read.
4567   */
4568
4569   read_JSEP=MagickFalse;
4570   reading_idat=MagickFalse;
4571   for (;;)
4572   {
4573     char
4574       type[MagickPathExtent];
4575
4576     unsigned char
4577       *chunk;
4578
4579     unsigned int
4580       count;
4581
4582     /*
4583       Read a new JNG chunk.
4584     */
4585     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4586       2*GetBlobSize(image));
4587
4588     if (status == MagickFalse)
4589       break;
4590
4591     type[0]='\0';
4592     (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4593     length=ReadBlobMSBLong(image);
4594     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4595
4596     if (logging != MagickFalse)
4597       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4598         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4599         type[0],type[1],type[2],type[3],(double) length);
4600
4601     if (length > PNG_UINT_31_MAX || count == 0)
4602       ThrowReaderException(CorruptImageError,"CorruptImage");
4603
4604     p=NULL;
4605     chunk=(unsigned char *) NULL;
4606
4607     if (length != 0)
4608       {
4609         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4610
4611         if (chunk == (unsigned char *) NULL)
4612           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4613
4614         for (i=0; i < (ssize_t) length; i++)
4615           chunk[i]=(unsigned char) ReadBlobByte(image);
4616
4617         p=chunk;
4618       }
4619
4620     (void) ReadBlobMSBLong(image);  /* read crc word */
4621
4622     if (memcmp(type,mng_JHDR,4) == 0)
4623       {
4624         if (length == 16)
4625           {
4626             jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4627               (p[2] << 8) | p[3]);
4628             jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4629               (p[6] << 8) | p[7]);
4630             if ((jng_width == 0) || (jng_height == 0))
4631               ThrowReaderException(CorruptImageError,
4632                 "NegativeOrZeroImageSize");
4633             jng_color_type=p[8];
4634             jng_image_sample_depth=p[9];
4635             jng_image_compression_method=p[10];
4636             jng_image_interlace_method=p[11];
4637
4638             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4639               NoInterlace;
4640
4641             jng_alpha_sample_depth=p[12];
4642             jng_alpha_compression_method=p[13];
4643             jng_alpha_filter_method=p[14];
4644             jng_alpha_interlace_method=p[15];
4645
4646             if (logging != MagickFalse)
4647               {
4648                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4649                   "    jng_width:      %16lu,    jng_height:     %16lu\n"
4650                   "    jng_color_type: %16d,     jng_image_sample_depth: %3d\n"
4651                   "    jng_image_compression_method:%3d",
4652                   (unsigned long) jng_width, (unsigned long) jng_height,
4653                   jng_color_type, jng_image_sample_depth,
4654                   jng_image_compression_method);
4655
4656                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4657                   "    jng_image_interlace_method:  %3d"
4658                   "    jng_alpha_sample_depth:      %3d",
4659                   jng_image_interlace_method,
4660                   jng_alpha_sample_depth);
4661
4662                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4663                   "    jng_alpha_compression_method:%3d\n"
4664                   "    jng_alpha_filter_method:     %3d\n"
4665                   "    jng_alpha_interlace_method:  %3d",
4666                   jng_alpha_compression_method,
4667                   jng_alpha_filter_method,
4668                   jng_alpha_interlace_method);
4669               }
4670           }
4671
4672         if (length != 0)
4673           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4674
4675         continue;
4676       }
4677
4678
4679     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4680         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4681          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4682       {
4683         /*
4684            o create color_image
4685            o open color_blob, attached to color_image
4686            o if (color type has alpha)
4687                open alpha_blob, attached to alpha_image
4688         */
4689
4690         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4691
4692         if (color_image_info == (ImageInfo *) NULL)
4693           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4694
4695         GetImageInfo(color_image_info);
4696         color_image=AcquireImage(color_image_info,exception);
4697
4698         if (color_image == (Image *) NULL)
4699           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4700
4701         if (logging != MagickFalse)
4702           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4703             "    Creating color_blob.");
4704
4705         (void) AcquireUniqueFilename(color_image->filename);
4706         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4707           exception);
4708
4709         if (status == MagickFalse)
4710           return((Image *) NULL);
4711
4712         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4713           {
4714             alpha_image_info=(ImageInfo *)
4715               AcquireMagickMemory(sizeof(ImageInfo));
4716
4717             if (alpha_image_info == (ImageInfo *) NULL)
4718               ThrowReaderException(ResourceLimitError,
4719                 "MemoryAllocationFailed");
4720
4721             GetImageInfo(alpha_image_info);
4722             alpha_image=AcquireImage(alpha_image_info,exception);
4723
4724             if (alpha_image == (Image *) NULL)
4725               {
4726                 alpha_image=DestroyImage(alpha_image);
4727                 ThrowReaderException(ResourceLimitError,
4728                   "MemoryAllocationFailed");
4729               }
4730
4731             if (logging != MagickFalse)
4732               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4733                 "    Creating alpha_blob.");
4734
4735             (void) AcquireUniqueFilename(alpha_image->filename);
4736             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4737               exception);
4738
4739             if (status == MagickFalse)
4740               return((Image *) NULL);
4741
4742             if (jng_alpha_compression_method == 0)
4743               {
4744                 unsigned char
4745                   data[18];
4746
4747                 if (logging != MagickFalse)
4748                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4749                     "    Writing IHDR chunk to alpha_blob.");
4750
4751                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4752                   "\211PNG\r\n\032\n");
4753
4754                 (void) WriteBlobMSBULong(alpha_image,13L);
4755                 PNGType(data,mng_IHDR);
4756                 LogPNGChunk(logging,mng_IHDR,13L);
4757                 PNGLong(data+4,jng_width);
4758                 PNGLong(data+8,jng_height);
4759                 data[12]=jng_alpha_sample_depth;
4760                 data[13]=0; /* color_type gray */
4761                 data[14]=0; /* compression method 0 */
4762                 data[15]=0; /* filter_method 0 */
4763                 data[16]=0; /* interlace_method 0 */
4764                 (void) WriteBlob(alpha_image,17,data);
4765                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4766               }
4767           }
4768         reading_idat=MagickTrue;
4769       }
4770
4771     if (memcmp(type,mng_JDAT,4) == 0)
4772       {
4773         /* Copy chunk to color_image->blob */
4774
4775         if (logging != MagickFalse)
4776           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4777             "    Copying JDAT chunk data to color_blob.");
4778
4779         (void) WriteBlob(color_image,length,chunk);
4780
4781         if (length != 0)
4782           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4783
4784         continue;
4785       }
4786
4787     if (memcmp(type,mng_IDAT,4) == 0)
4788       {
4789         png_byte
4790            data[5];
4791
4792         /* Copy IDAT header and chunk data to alpha_image->blob */
4793
4794         if (alpha_image != NULL && image_info->ping == MagickFalse)
4795           {
4796             if (logging != MagickFalse)
4797               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4798                 "    Copying IDAT chunk data to alpha_blob.");
4799
4800             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4801             PNGType(data,mng_IDAT);
4802             LogPNGChunk(logging,mng_IDAT,length);
4803             (void) WriteBlob(alpha_image,4,data);
4804             (void) WriteBlob(alpha_image,length,chunk);
4805             (void) WriteBlobMSBULong(alpha_image,
4806               crc32(crc32(0,data,4),chunk,(uInt) length));
4807           }
4808
4809         if (length != 0)
4810           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4811
4812         continue;
4813       }
4814
4815     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4816       {
4817         /* Copy chunk data to alpha_image->blob */
4818
4819         if (alpha_image != NULL && image_info->ping == MagickFalse)
4820           {
4821             if (logging != MagickFalse)
4822               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4823                 "    Copying JDAA chunk data to alpha_blob.");
4824
4825             (void) WriteBlob(alpha_image,length,chunk);
4826           }
4827
4828         if (length != 0)
4829           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4830
4831         continue;
4832       }
4833
4834     if (memcmp(type,mng_JSEP,4) == 0)
4835       {
4836         read_JSEP=MagickTrue;
4837
4838         if (length != 0)
4839           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4840
4841         continue;
4842       }
4843
4844     if (memcmp(type,mng_bKGD,4) == 0)
4845       {
4846         if (length == 2)
4847           {
4848             image->background_color.red=ScaleCharToQuantum(p[1]);
4849             image->background_color.green=image->background_color.red;
4850             image->background_color.blue=image->background_color.red;
4851           }
4852
4853         if (length == 6)
4854           {
4855             image->background_color.red=ScaleCharToQuantum(p[1]);
4856             image->background_color.green=ScaleCharToQuantum(p[3]);
4857             image->background_color.blue=ScaleCharToQuantum(p[5]);
4858           }
4859
4860         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4861         continue;
4862       }
4863
4864     if (memcmp(type,mng_gAMA,4) == 0)
4865       {
4866         if (length == 4)
4867           image->gamma=((float) mng_get_long(p))*0.00001;
4868
4869         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4870         continue;
4871       }
4872
4873     if (memcmp(type,mng_cHRM,4) == 0)
4874       {
4875         if (length == 32)
4876           {
4877             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4878             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4879             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4880             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4881             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4882             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4883             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4884             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4885           }
4886
4887         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4888         continue;
4889       }
4890
4891     if (memcmp(type,mng_sRGB,4) == 0)
4892       {
4893         if (length == 1)
4894           {
4895             image->rendering_intent=
4896               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4897             image->gamma=1.000f/2.200f;
4898             image->chromaticity.red_primary.x=0.6400f;
4899             image->chromaticity.red_primary.y=0.3300f;
4900             image->chromaticity.green_primary.x=0.3000f;
4901             image->chromaticity.green_primary.y=0.6000f;
4902             image->chromaticity.blue_primary.x=0.1500f;
4903             image->chromaticity.blue_primary.y=0.0600f;
4904             image->chromaticity.white_point.x=0.3127f;
4905             image->chromaticity.white_point.y=0.3290f;
4906           }
4907
4908         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4909         continue;
4910       }
4911
4912     if (memcmp(type,mng_oFFs,4) == 0)
4913       {
4914         if (length > 8)
4915           {
4916             image->page.x=(ssize_t) mng_get_long(p);
4917             image->page.y=(ssize_t) mng_get_long(&p[4]);
4918
4919             if ((int) p[8] != 0)
4920               {
4921                 image->page.x/=10000;
4922                 image->page.y/=10000;
4923               }
4924           }
4925
4926         if (length != 0)
4927           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4928
4929         continue;
4930       }
4931
4932     if (memcmp(type,mng_pHYs,4) == 0)
4933       {
4934         if (length > 8)
4935           {
4936             image->resolution.x=(double) mng_get_long(p);
4937             image->resolution.y=(double) mng_get_long(&p[4]);
4938             if ((int) p[8] == PNG_RESOLUTION_METER)
4939               {
4940                 image->units=PixelsPerCentimeterResolution;
4941                 image->resolution.x=image->resolution.x/100.0f;
4942                 image->resolution.y=image->resolution.y/100.0f;
4943               }
4944           }
4945
4946         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4947         continue;
4948       }
4949
4950 #if 0
4951     if (memcmp(type,mng_iCCP,4) == 0)
4952       {
4953         /* To do: */
4954         if (length != 0)
4955           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4956
4957         continue;
4958       }
4959 #endif
4960
4961     if (length != 0)
4962       chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4963
4964     if (memcmp(type,mng_IEND,4))
4965       continue;
4966
4967     break;
4968   }
4969
4970
4971   /* IEND found */
4972
4973   /*
4974     Finish up reading image data:
4975
4976        o read main image from color_blob.
4977
4978        o close color_blob.
4979
4980        o if (color_type has alpha)
4981             if alpha_encoding is PNG
4982                read secondary image from alpha_blob via ReadPNG
4983             if alpha_encoding is JPEG
4984                read secondary image from alpha_blob via ReadJPEG
4985
4986        o close alpha_blob.
4987
4988        o copy intensity of secondary image into
4989          alpha samples of main image.
4990
4991        o destroy the secondary image.
4992   */
4993
4994   if (color_image_info == (ImageInfo *) NULL)
4995     {
4996       assert(color_image == (Image *) NULL);
4997       assert(alpha_image == (Image *) NULL);
4998       return((Image *) NULL);
4999     }
5000
5001   if (color_image == (Image *) NULL)
5002     {
5003       assert(alpha_image == (Image *) NULL);
5004       return((Image *) NULL);
5005     }
5006
5007   (void) SeekBlob(color_image,0,SEEK_SET);
5008
5009   if (logging != MagickFalse)
5010     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5011       "    Reading jng_image from color_blob.");
5012
5013   assert(color_image_info != (ImageInfo *) NULL);
5014   (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,"%s",
5015     color_image->filename);
5016
5017   color_image_info->ping=MagickFalse;   /* To do: avoid this */
5018   jng_image=ReadImage(color_image_info,exception);
5019
5020   (void) RelinquishUniqueFileResource(color_image->filename);
5021   color_image=DestroyImage(color_image);
5022   color_image_info=DestroyImageInfo(color_image_info);
5023
5024   if (jng_image == (Image *) NULL)
5025     return((Image *) NULL);
5026
5027   if (logging != MagickFalse)
5028     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5029       "    Copying jng_image pixels to main image.");
5030
5031   image->rows=jng_height;
5032   image->columns=jng_width;
5033
5034   status=SetImageExtent(image,image->columns,image->rows,exception);
5035   if (status == MagickFalse)
5036     return(DestroyImageList(image));
5037
5038   for (y=0; y < (ssize_t) image->rows; y++)
5039   {
5040     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5041     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5042     for (x=(ssize_t) image->columns; x != 0; x--)
5043     {
5044       SetPixelRed(image,GetPixelRed(jng_image,s),q);
5045       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
5046       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
5047       q+=GetPixelChannels(image);
5048       s+=GetPixelChannels(jng_image);
5049     }
5050
5051     if (SyncAuthenticPixels(image,exception) == MagickFalse)
5052       break;
5053   }
5054
5055   jng_image=DestroyImage(jng_image);
5056
5057   if (image_info->ping == MagickFalse)
5058     {
5059      if (jng_color_type >= 12)
5060        {
5061          if (jng_alpha_compression_method == 0)
5062            {
5063              png_byte
5064                data[5];
5065              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
5066              PNGType(data,mng_IEND);
5067              LogPNGChunk(logging,mng_IEND,0L);
5068              (void) WriteBlob(alpha_image,4,data);
5069              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
5070            }
5071
5072          (void) CloseBlob(alpha_image);
5073
5074          if (logging != MagickFalse)
5075            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5076              "    Reading alpha from alpha_blob.");
5077
5078          (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
5079            "%s",alpha_image->filename);
5080
5081          jng_image=ReadImage(alpha_image_info,exception);
5082
5083          if (jng_image != (Image *) NULL)
5084            for (y=0; y < (ssize_t) image->rows; y++)
5085            {
5086              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
5087                exception);
5088              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5089
5090              if (image->alpha_trait != UndefinedPixelTrait)
5091                for (x=(ssize_t) image->columns; x != 0; x--)
5092                {
5093                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5094                   q+=GetPixelChannels(image);
5095                   s+=GetPixelChannels(jng_image);
5096                }
5097
5098              else
5099                for (x=(ssize_t) image->columns; x != 0; x--)
5100                {
5101                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5102                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
5103                     image->alpha_trait=BlendPixelTrait;
5104                   q+=GetPixelChannels(image);
5105                   s+=GetPixelChannels(jng_image);
5106                }
5107
5108              if (SyncAuthenticPixels(image,exception) == MagickFalse)
5109                break;
5110            }
5111          (void) RelinquishUniqueFileResource(alpha_image->filename);
5112          alpha_image=DestroyImage(alpha_image);
5113          alpha_image_info=DestroyImageInfo(alpha_image_info);
5114          if (jng_image != (Image *) NULL)
5115            jng_image=DestroyImage(jng_image);
5116        }
5117     }
5118
5119   /* Read the JNG image.  */
5120
5121   if (mng_info->mng_type == 0)
5122     {
5123       mng_info->mng_width=jng_width;
5124       mng_info->mng_height=jng_height;
5125     }
5126
5127   if (image->page.width == 0 && image->page.height == 0)
5128     {
5129       image->page.width=jng_width;
5130       image->page.height=jng_height;
5131     }
5132
5133   if (image->page.x == 0 && image->page.y == 0)
5134     {
5135       image->page.x=mng_info->x_off[mng_info->object_id];
5136       image->page.y=mng_info->y_off[mng_info->object_id];
5137     }
5138
5139   else
5140     {
5141       image->page.y=mng_info->y_off[mng_info->object_id];
5142     }
5143
5144   mng_info->image_found++;
5145   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
5146     2*GetBlobSize(image));
5147
5148   if (status == MagickFalse)
5149     return((Image *) NULL);
5150
5151   if (logging != MagickFalse)
5152     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5153       "  exit ReadOneJNGImage()");
5154
5155   return(image);
5156 }
5157
5158 /*
5159 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5160 %                                                                             %
5161 %                                                                             %
5162 %                                                                             %
5163 %   R e a d J N G I m a g e                                                   %
5164 %                                                                             %
5165 %                                                                             %
5166 %                                                                             %
5167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5168 %
5169 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
5170 %  (including the 8-byte signature)  and returns it.  It allocates the memory
5171 %  necessary for the new Image structure and returns a pointer to the new
5172 %  image.
5173 %
5174 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
5175 %
5176 %  The format of the ReadJNGImage method is:
5177 %
5178 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
5179 %         *exception)
5180 %
5181 %  A description of each parameter follows:
5182 %
5183 %    o image_info: the image info.
5184 %
5185 %    o exception: return any errors or warnings in this structure.
5186 %
5187 */
5188
5189 static Image *ReadJNGImage(const ImageInfo *image_info,
5190                 ExceptionInfo *exception)
5191 {
5192   Image
5193     *image;
5194
5195   MagickBooleanType
5196     have_mng_structure,
5197     logging,
5198     status;
5199
5200   MngInfo
5201     *mng_info;
5202
5203   char
5204     magic_number[MagickPathExtent];
5205
5206   size_t
5207     count;
5208
5209   /*
5210     Open image file.
5211   */
5212   assert(image_info != (const ImageInfo *) NULL);
5213   assert(image_info->signature == MagickCoreSignature);
5214   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5215      image_info->filename);
5216   assert(exception != (ExceptionInfo *) NULL);
5217   assert(exception->signature == MagickCoreSignature);
5218   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
5219   image=AcquireImage(image_info,exception);
5220   mng_info=(MngInfo *) NULL;
5221   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5222
5223   if (status == MagickFalse)
5224     return((Image *) NULL);
5225
5226   if (LocaleCompare(image_info->magick,"JNG") != 0)
5227     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5228
5229   /* Verify JNG signature.  */
5230
5231   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5232
5233   if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
5234     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5235
5236   /* Allocate a MngInfo structure.  */
5237
5238   have_mng_structure=MagickFalse;
5239   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
5240
5241   if (mng_info == (MngInfo *) NULL)
5242     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5243
5244   /* Initialize members of the MngInfo structure.  */
5245
5246   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5247   have_mng_structure=MagickTrue;
5248
5249   mng_info->image=image;
5250   image=ReadOneJNGImage(mng_info,image_info,exception);
5251   MngInfoFreeStruct(mng_info,&have_mng_structure);
5252
5253   if (image == (Image *) NULL)
5254     {
5255       if (logging != MagickFalse)
5256         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5257           "exit ReadJNGImage() with error");
5258
5259       return((Image *) NULL);
5260     }
5261   (void) CloseBlob(image);
5262
5263   if (image->columns == 0 || image->rows == 0)
5264     {
5265       if (logging != MagickFalse)
5266         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5267           "exit ReadJNGImage() with error");
5268
5269       ThrowReaderException(CorruptImageError,"CorruptImage");
5270     }
5271
5272   if (logging != MagickFalse)
5273     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5274
5275   return(image);
5276 }
5277 #endif
5278
5279 static Image *ReadMNGImage(const ImageInfo *image_info,
5280      ExceptionInfo *exception)
5281 {
5282   char
5283     page_geometry[MagickPathExtent];
5284
5285   Image
5286     *image;
5287
5288   MagickBooleanType
5289     logging,
5290     have_mng_structure;
5291
5292   volatile int
5293     first_mng_object,
5294     object_id,
5295     term_chunk_found,
5296     skip_to_iend;
5297
5298   volatile ssize_t
5299     image_count=0;
5300
5301   MagickBooleanType
5302     status;
5303
5304   MagickOffsetType
5305     offset;
5306
5307   MngInfo
5308     *mng_info;
5309
5310   MngBox
5311     default_fb,
5312     fb,
5313     previous_fb;
5314
5315 #if defined(MNG_INSERT_LAYERS)
5316   PixelInfo
5317     mng_background_color;
5318 #endif
5319
5320   register unsigned char
5321     *p;
5322
5323   register ssize_t
5324     i;
5325
5326   size_t
5327     count;
5328
5329   ssize_t
5330     loop_level;
5331
5332   volatile short
5333     skipping_loop;
5334
5335 #if defined(MNG_INSERT_LAYERS)
5336   unsigned int
5337     mandatory_back=0;
5338 #endif
5339
5340   volatile unsigned int
5341 #ifdef MNG_OBJECT_BUFFERS
5342     mng_background_object=0,
5343 #endif
5344     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5345
5346   size_t
5347     default_frame_timeout,
5348     frame_timeout,
5349 #if defined(MNG_INSERT_LAYERS)
5350     image_height,
5351     image_width,
5352 #endif
5353     length;
5354
5355   /* These delays are all measured in image ticks_per_second,
5356    * not in MNG ticks_per_second
5357    */
5358   volatile size_t
5359     default_frame_delay,
5360     final_delay,
5361     final_image_delay,
5362     frame_delay,
5363 #if defined(MNG_INSERT_LAYERS)
5364     insert_layers,
5365 #endif
5366     mng_iterations=1,
5367     simplicity=0,
5368     subframe_height=0,
5369     subframe_width=0;
5370
5371   previous_fb.top=0;
5372   previous_fb.bottom=0;
5373   previous_fb.left=0;
5374   previous_fb.right=0;
5375   default_fb.top=0;
5376   default_fb.bottom=0;
5377   default_fb.left=0;
5378   default_fb.right=0;
5379
5380   /* Open image file.  */
5381
5382   assert(image_info != (const ImageInfo *) NULL);
5383   assert(image_info->signature == MagickCoreSignature);
5384   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5385      image_info->filename);
5386   assert(exception != (ExceptionInfo *) NULL);
5387   assert(exception->signature == MagickCoreSignature);
5388   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
5389   image=AcquireImage(image_info,exception);
5390   mng_info=(MngInfo *) NULL;
5391   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5392
5393   if (status == MagickFalse)
5394     return((Image *) NULL);
5395
5396   first_mng_object=MagickFalse;
5397   skipping_loop=(-1);
5398   have_mng_structure=MagickFalse;
5399
5400   /* Allocate a MngInfo structure.  */
5401
5402   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
5403
5404   if (mng_info == (MngInfo *) NULL)
5405     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5406
5407   /* Initialize members of the MngInfo structure.  */
5408
5409   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5410   mng_info->image=image;
5411   have_mng_structure=MagickTrue;
5412
5413   if (LocaleCompare(image_info->magick,"MNG") == 0)
5414     {
5415       char
5416         magic_number[MagickPathExtent];
5417
5418       /* Verify MNG signature.  */
5419       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5420       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5421         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5422
5423       /* Initialize some nonzero members of the MngInfo structure.  */
5424       for (i=0; i < MNG_MAX_OBJECTS; i++)
5425       {
5426         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5427         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5428       }
5429       mng_info->exists[0]=MagickTrue;
5430     }
5431
5432   first_mng_object=MagickTrue;
5433   mng_type=0;
5434 #if defined(MNG_INSERT_LAYERS)
5435   insert_layers=MagickFalse; /* should be False during convert or mogrify */
5436 #endif
5437   default_frame_delay=0;
5438   default_frame_timeout=0;
5439   frame_delay=0;
5440   final_delay=1;
5441   mng_info->ticks_per_second=1UL*image->ticks_per_second;
5442   object_id=0;
5443   skip_to_iend=MagickFalse;
5444   term_chunk_found=MagickFalse;
5445   mng_info->framing_mode=1;
5446 #if defined(MNG_INSERT_LAYERS)
5447   mandatory_back=MagickFalse;
5448 #endif
5449 #if defined(MNG_INSERT_LAYERS)
5450   mng_background_color=image->background_color;
5451 #endif
5452   default_fb=mng_info->frame;
5453   previous_fb=mng_info->frame;
5454   do
5455   {
5456     char
5457       type[MagickPathExtent];
5458
5459     if (LocaleCompare(image_info->magick,"MNG") == 0)
5460       {
5461         unsigned char
5462           *chunk;
5463
5464         /*
5465           Read a new chunk.
5466         */
5467         type[0]='\0';
5468         (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5469         length=ReadBlobMSBLong(image);
5470         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5471
5472         if (logging != MagickFalse)
5473           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5474            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
5475            type[0],type[1],type[2],type[3],(double) length);
5476
5477         if (length > PNG_UINT_31_MAX)
5478           {
5479             status=MagickFalse;
5480             break;
5481           }
5482
5483         if (count == 0)
5484           ThrowReaderException(CorruptImageError,"CorruptImage");
5485
5486         p=NULL;
5487         chunk=(unsigned char *) NULL;
5488
5489         if (length != 0)
5490           {
5491             chunk=(unsigned char *) AcquireQuantumMemory(length,
5492              sizeof(*chunk));
5493
5494             if (chunk == (unsigned char *) NULL)
5495               ThrowReaderException(ResourceLimitError,
5496                 "MemoryAllocationFailed");
5497
5498             for (i=0; i < (ssize_t) length; i++)
5499               chunk[i]=(unsigned char) ReadBlobByte(image);
5500
5501             p=chunk;
5502           }
5503
5504         (void) ReadBlobMSBLong(image);  /* read crc word */
5505
5506 #if !defined(JNG_SUPPORTED)
5507         if (memcmp(type,mng_JHDR,4) == 0)
5508           {
5509             skip_to_iend=MagickTrue;
5510
5511             if (mng_info->jhdr_warning == 0)
5512               (void) ThrowMagickException(exception,GetMagickModule(),
5513                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5514
5515             mng_info->jhdr_warning++;
5516           }
5517 #endif
5518         if (memcmp(type,mng_DHDR,4) == 0)
5519           {
5520             skip_to_iend=MagickTrue;
5521
5522             if (mng_info->dhdr_warning == 0)
5523               (void) ThrowMagickException(exception,GetMagickModule(),
5524                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5525
5526             mng_info->dhdr_warning++;
5527           }
5528         if (memcmp(type,mng_MEND,4) == 0)
5529           break;
5530
5531         if (skip_to_iend)
5532           {
5533             if (memcmp(type,mng_IEND,4) == 0)
5534               skip_to_iend=MagickFalse;
5535
5536             if (length != 0)
5537               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5538
5539             if (logging != MagickFalse)
5540               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5541                 "  Skip to IEND.");
5542
5543             continue;
5544           }
5545
5546         if (memcmp(type,mng_MHDR,4) == 0)
5547           {
5548             if (length != 28)
5549               {
5550                 if (chunk)
5551                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5552                 ThrowReaderException(CorruptImageError,"CorruptImage");
5553               }
5554
5555             mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5556                 (p[2] << 8) | p[3]);
5557
5558             mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5559                 (p[6] << 8) | p[7]);
5560
5561             if (logging != MagickFalse)
5562               {
5563                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5564                   "  MNG width: %.20g",(double) mng_info->mng_width);
5565                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5566                   "  MNG height: %.20g",(double) mng_info->mng_height);
5567               }
5568
5569             p+=8;
5570             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5571
5572             if (mng_info->ticks_per_second == 0)
5573               default_frame_delay=0;
5574
5575             else
5576               default_frame_delay=1UL*image->ticks_per_second/
5577                 mng_info->ticks_per_second;
5578
5579             frame_delay=default_frame_delay;
5580             simplicity=0;
5581
5582             p+=16;
5583             simplicity=(size_t) mng_get_long(p);
5584
5585             mng_type=1;    /* Full MNG */
5586
5587             if ((simplicity != 0) && ((simplicity | 11) == 11))
5588               mng_type=2; /* LC */
5589
5590             if ((simplicity != 0) && ((simplicity | 9) == 9))
5591               mng_type=3; /* VLC */
5592
5593 #if defined(MNG_INSERT_LAYERS)
5594             if (mng_type != 3)
5595               insert_layers=MagickTrue;
5596 #endif
5597             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5598               {
5599                 /* Allocate next image structure.  */
5600                 AcquireNextImage(image_info,image,exception);
5601
5602                 if (GetNextImageInList(image) == (Image *) NULL)
5603                   return((Image *) NULL);
5604
5605                 image=SyncNextImageInList(image);
5606                 mng_info->image=image;
5607               }
5608
5609             if ((mng_info->mng_width > 65535L) ||
5610                 (mng_info->mng_height > 65535L))
5611               ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5612
5613             (void) FormatLocaleString(page_geometry,MagickPathExtent,
5614               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5615               mng_info->mng_height);
5616
5617             mng_info->frame.left=0;
5618             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5619             mng_info->frame.top=0;
5620             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5621             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5622
5623             for (i=0; i < MNG_MAX_OBJECTS; i++)
5624               mng_info->object_clip[i]=mng_info->frame;
5625
5626             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5627             continue;
5628           }
5629
5630         if (memcmp(type,mng_TERM,4) == 0)
5631           {
5632             int
5633               repeat=0;
5634
5635             if (length != 0)
5636               repeat=p[0];
5637
5638             if (repeat == 3)
5639               {
5640                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5641                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5642
5643                 if (mng_iterations == PNG_UINT_31_MAX)
5644                   mng_iterations=0;
5645
5646                 image->iterations=mng_iterations;
5647                 term_chunk_found=MagickTrue;
5648               }
5649
5650             if (logging != MagickFalse)
5651               {
5652                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5653                   "    repeat=%d,  final_delay=%.20g,  iterations=%.20g",
5654                   repeat,(double) final_delay, (double) image->iterations);
5655               }
5656
5657             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5658             continue;
5659           }
5660         if (memcmp(type,mng_DEFI,4) == 0)
5661           {
5662             if (mng_type == 3)
5663               (void) ThrowMagickException(exception,GetMagickModule(),
5664                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5665                 image->filename);
5666
5667             if (length < 2)
5668               {
5669                 if (chunk)
5670                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5671                 ThrowReaderException(CorruptImageError,"CorruptImage");
5672               }
5673
5674             object_id=(p[0] << 8) | p[1];
5675
5676             if (mng_type == 2 && object_id != 0)
5677               (void) ThrowMagickException(exception,GetMagickModule(),
5678                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5679                 image->filename);
5680
5681             if (object_id > MNG_MAX_OBJECTS)
5682               {
5683                 /*
5684                   Instead of using a warning we should allocate a larger
5685                   MngInfo structure and continue.
5686                 */
5687                 (void) ThrowMagickException(exception,GetMagickModule(),
5688                   CoderError,"object id too large","`%s'",image->filename);
5689                 object_id=MNG_MAX_OBJECTS;
5690               }
5691
5692             if (mng_info->exists[object_id])
5693               if (mng_info->frozen[object_id])
5694                 {
5695                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5696                   (void) ThrowMagickException(exception,
5697                     GetMagickModule(),CoderError,
5698                     "DEFI cannot redefine a frozen MNG object","`%s'",
5699                     image->filename);
5700                   continue;
5701                 }
5702
5703             mng_info->exists[object_id]=MagickTrue;
5704
5705             if (length > 2)
5706               mng_info->invisible[object_id]=p[2];
5707
5708             /*
5709               Extract object offset info.
5710             */
5711             if (length > 11)
5712               {
5713                 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5714                     (p[5] << 16) | (p[6] << 8) | p[7]);
5715
5716                 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5717                     (p[9] << 16) | (p[10] << 8) | p[11]);
5718
5719                 if (logging != MagickFalse)
5720                   {
5721                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5722                       "  x_off[%d]: %.20g,  y_off[%d]: %.20g",
5723                       object_id,(double) mng_info->x_off[object_id],
5724                       object_id,(double) mng_info->y_off[object_id]);
5725                   }
5726               }
5727
5728             /*
5729               Extract object clipping info.
5730             */
5731             if (length > 27)
5732               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5733                 &p[12]);
5734
5735             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5736             continue;
5737           }
5738         if (memcmp(type,mng_bKGD,4) == 0)
5739           {
5740             mng_info->have_global_bkgd=MagickFalse;
5741
5742             if (length > 5)
5743               {
5744                 mng_info->mng_global_bkgd.red=
5745                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5746
5747                 mng_info->mng_global_bkgd.green=
5748                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5749
5750                 mng_info->mng_global_bkgd.blue=
5751                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5752
5753                 mng_info->have_global_bkgd=MagickTrue;
5754               }
5755
5756             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5757             continue;
5758           }
5759         if (memcmp(type,mng_BACK,4) == 0)
5760           {
5761 #if defined(MNG_INSERT_LAYERS)
5762             if (length > 6)
5763               mandatory_back=p[6];
5764
5765             else
5766               mandatory_back=0;
5767
5768             if (mandatory_back && length > 5)
5769               {
5770                 mng_background_color.red=
5771                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5772
5773                 mng_background_color.green=
5774                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5775
5776                 mng_background_color.blue=
5777                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5778
5779                 mng_background_color.alpha=OpaqueAlpha;
5780               }
5781
5782 #ifdef MNG_OBJECT_BUFFERS
5783             if (length > 8)
5784               mng_background_object=(p[7] << 8) | p[8];
5785 #endif
5786 #endif
5787             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5788             continue;
5789           }
5790
5791         if (memcmp(type,mng_PLTE,4) == 0)
5792           {
5793             /* Read global PLTE.  */
5794
5795             if (length && (length < 769))
5796               {
5797                 if (mng_info->global_plte == (png_colorp) NULL)
5798                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5799                     sizeof(*mng_info->global_plte));
5800
5801                 for (i=0; i < (ssize_t) (length/3); i++)
5802                 {
5803                   mng_info->global_plte[i].red=p[3*i];
5804                   mng_info->global_plte[i].green=p[3*i+1];
5805                   mng_info->global_plte[i].blue=p[3*i+2];
5806                 }
5807
5808                 mng_info->global_plte_length=(unsigned int) (length/3);
5809               }
5810 #ifdef MNG_LOOSE
5811             for ( ; i < 256; i++)
5812             {
5813               mng_info->global_plte[i].red=i;
5814               mng_info->global_plte[i].green=i;
5815               mng_info->global_plte[i].blue=i;
5816             }
5817
5818             if (length != 0)
5819               mng_info->global_plte_length=256;
5820 #endif
5821             else
5822               mng_info->global_plte_length=0;
5823
5824             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5825             continue;
5826           }
5827
5828         if (memcmp(type,mng_tRNS,4) == 0)
5829           {
5830             /* read global tRNS */
5831
5832             if (length > 0 && length < 257)
5833               for (i=0; i < (ssize_t) length; i++)
5834                 mng_info->global_trns[i]=p[i];
5835
5836 #ifdef MNG_LOOSE
5837             for ( ; i < 256; i++)
5838               mng_info->global_trns[i]=255;
5839 #endif
5840             mng_info->global_trns_length=(unsigned int) length;
5841             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5842             continue;
5843           }
5844         if (memcmp(type,mng_gAMA,4) == 0)
5845           {
5846             if (length == 4)
5847               {
5848                 ssize_t
5849                   igamma;
5850
5851                 igamma=mng_get_long(p);
5852                 mng_info->global_gamma=((float) igamma)*0.00001;
5853                 mng_info->have_global_gama=MagickTrue;
5854               }
5855
5856             else
5857               mng_info->have_global_gama=MagickFalse;
5858
5859             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5860             continue;
5861           }
5862
5863         if (memcmp(type,mng_cHRM,4) == 0)
5864           {
5865             /* Read global cHRM */
5866
5867             if (length == 32)
5868               {
5869                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5870                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5871                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5872                 mng_info->global_chrm.red_primary.y=0.00001*
5873                   mng_get_long(&p[12]);
5874                 mng_info->global_chrm.green_primary.x=0.00001*
5875                   mng_get_long(&p[16]);
5876                 mng_info->global_chrm.green_primary.y=0.00001*
5877                   mng_get_long(&p[20]);
5878                 mng_info->global_chrm.blue_primary.x=0.00001*
5879                   mng_get_long(&p[24]);
5880                 mng_info->global_chrm.blue_primary.y=0.00001*
5881                   mng_get_long(&p[28]);
5882                 mng_info->have_global_chrm=MagickTrue;
5883               }
5884             else
5885               mng_info->have_global_chrm=MagickFalse;
5886
5887             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5888             continue;
5889           }
5890
5891         if (memcmp(type,mng_sRGB,4) == 0)
5892           {
5893             /*
5894               Read global sRGB.
5895             */
5896             if (length != 0)
5897               {
5898                 mng_info->global_srgb_intent=
5899                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5900                 mng_info->have_global_srgb=MagickTrue;
5901               }
5902             else
5903               mng_info->have_global_srgb=MagickFalse;
5904
5905             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5906             continue;
5907           }
5908
5909         if (memcmp(type,mng_iCCP,4) == 0)
5910           {
5911             /* To do: */
5912
5913             /*
5914               Read global iCCP.
5915             */
5916             if (length != 0)
5917               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5918
5919             continue;
5920           }
5921
5922         if (memcmp(type,mng_FRAM,4) == 0)
5923           {
5924             if (mng_type == 3)
5925               (void) ThrowMagickException(exception,GetMagickModule(),
5926                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5927                 image->filename);
5928
5929             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5930               image->delay=frame_delay;
5931
5932             frame_delay=default_frame_delay;
5933             frame_timeout=default_frame_timeout;
5934             fb=default_fb;
5935
5936             if (length != 0)
5937               if (p[0])
5938                 mng_info->framing_mode=p[0];
5939
5940             if (logging != MagickFalse)
5941               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5942                 "    Framing_mode=%d",mng_info->framing_mode);
5943
5944             if (length > 6)
5945               {
5946                 /* Note the delay and frame clipping boundaries.  */
5947
5948                 p++; /* framing mode */
5949
5950                 while (*p && ((p-chunk) < (ssize_t) length))
5951                   p++;  /* frame name */
5952
5953                 p++;  /* frame name terminator */
5954
5955                 if ((p-chunk) < (ssize_t) (length-4))
5956                   {
5957                     int
5958                       change_delay,
5959                       change_timeout,
5960                       change_clipping;
5961
5962                     change_delay=(*p++);
5963                     change_timeout=(*p++);
5964                     change_clipping=(*p++);
5965                     p++; /* change_sync */
5966
5967                     if (change_delay)
5968                       {
5969                         frame_delay=1UL*image->ticks_per_second*
5970                           mng_get_long(p);
5971
5972                         if (mng_info->ticks_per_second != 0)
5973                           frame_delay/=mng_info->ticks_per_second;
5974
5975                         else
5976                           frame_delay=PNG_UINT_31_MAX;
5977
5978                         if (change_delay == 2)
5979                           default_frame_delay=frame_delay;
5980
5981                         p+=4;
5982
5983                         if (logging != MagickFalse)
5984                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5985                             "    Framing_delay=%.20g",(double) frame_delay);
5986                       }
5987
5988                     if (change_timeout)
5989                       {
5990                         frame_timeout=1UL*image->ticks_per_second*
5991                           mng_get_long(p);
5992
5993                         if (mng_info->ticks_per_second != 0)
5994                           frame_timeout/=mng_info->ticks_per_second;
5995
5996                         else
5997                           frame_timeout=PNG_UINT_31_MAX;
5998
5999                         if (change_timeout == 2)
6000                           default_frame_timeout=frame_timeout;
6001
6002                         p+=4;
6003
6004                         if (logging != MagickFalse)
6005                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6006                             "    Framing_timeout=%.20g",(double) frame_timeout);
6007                       }
6008
6009                     if (change_clipping)
6010                       {
6011                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
6012                         p+=17;
6013                         previous_fb=fb;
6014
6015                         if (logging != MagickFalse)
6016                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6017                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
6018                             (double) fb.left,(double) fb.right,(double) fb.top,
6019                             (double) fb.bottom);
6020
6021                         if (change_clipping == 2)
6022                           default_fb=fb;
6023                       }
6024                   }
6025               }
6026             mng_info->clip=fb;
6027             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
6028
6029             subframe_width=(size_t) (mng_info->clip.right
6030                -mng_info->clip.left);
6031
6032             subframe_height=(size_t) (mng_info->clip.bottom
6033                -mng_info->clip.top);
6034             /*
6035               Insert a background layer behind the frame if framing_mode is 4.
6036             */
6037 #if defined(MNG_INSERT_LAYERS)
6038             if (logging != MagickFalse)
6039               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6040                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
6041                 subframe_width,(double) subframe_height);
6042
6043             if (insert_layers && (mng_info->framing_mode == 4) &&
6044                 (subframe_width) && (subframe_height))
6045               {
6046                 /* Allocate next image structure.  */
6047                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6048                   {
6049                     AcquireNextImage(image_info,image,exception);
6050
6051                     if (GetNextImageInList(image) == (Image *) NULL)
6052                       {
6053                         image=DestroyImageList(image);
6054                         MngInfoFreeStruct(mng_info,&have_mng_structure);
6055                         return((Image *) NULL);
6056                       }
6057
6058                     image=SyncNextImageInList(image);
6059                   }
6060
6061                 mng_info->image=image;
6062
6063                 if (term_chunk_found)
6064                   {
6065                     image->start_loop=MagickTrue;
6066                     image->iterations=mng_iterations;
6067                     term_chunk_found=MagickFalse;
6068                   }
6069
6070                 else
6071                     image->start_loop=MagickFalse;
6072
6073                 image->columns=subframe_width;
6074                 image->rows=subframe_height;
6075                 image->page.width=subframe_width;
6076                 image->page.height=subframe_height;
6077                 image->page.x=mng_info->clip.left;
6078                 image->page.y=mng_info->clip.top;
6079                 image->background_color=mng_background_color;
6080                 image->alpha_trait=UndefinedPixelTrait;
6081                 image->delay=0;
6082                 (void) SetImageBackgroundColor(image,exception);
6083
6084                 if (logging != MagickFalse)
6085                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6086                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6087                     (double) mng_info->clip.left,
6088                     (double) mng_info->clip.right,
6089                     (double) mng_info->clip.top,
6090                     (double) mng_info->clip.bottom);
6091               }
6092 #endif
6093             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6094             continue;
6095           }
6096
6097         if (memcmp(type,mng_CLIP,4) == 0)
6098           {
6099             unsigned int
6100               first_object,
6101               last_object;
6102
6103             /*
6104               Read CLIP.
6105             */
6106             if (length > 3)
6107               {
6108                 first_object=(p[0] << 8) | p[1];
6109                 last_object=(p[2] << 8) | p[3];
6110                 p+=4;
6111
6112                 for (i=(int) first_object; i <= (int) last_object; i++)
6113                 {
6114                   if (mng_info->exists[i] && !mng_info->frozen[i])
6115                     {
6116                       MngBox
6117                         box;
6118
6119                       box=mng_info->object_clip[i];
6120                       if ((p-chunk) < (ssize_t) (length-17))
6121                         mng_info->object_clip[i]=
6122                            mng_read_box(box,(char) p[0],&p[1]);
6123                     }
6124                 }
6125
6126               }
6127             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6128             continue;
6129           }
6130
6131         if (memcmp(type,mng_SAVE,4) == 0)
6132           {
6133             for (i=1; i < MNG_MAX_OBJECTS; i++)
6134               if (mng_info->exists[i])
6135                 {
6136                  mng_info->frozen[i]=MagickTrue;
6137 #ifdef MNG_OBJECT_BUFFERS
6138                  if (mng_info->ob[i] != (MngBuffer *) NULL)
6139                     mng_info->ob[i]->frozen=MagickTrue;
6140 #endif
6141                 }
6142
6143             if (length != 0)
6144               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6145
6146             continue;
6147           }
6148
6149         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
6150           {
6151             /* Read DISC or SEEK.  */
6152
6153             if ((length == 0) || !memcmp(type,mng_SEEK,4))
6154               {
6155                 for (i=1; i < MNG_MAX_OBJECTS; i++)
6156                   MngInfoDiscardObject(mng_info,i);
6157               }
6158
6159             else
6160               {
6161                 register ssize_t
6162                   j;
6163
6164                 for (j=1; j < (ssize_t) length; j+=2)
6165                 {
6166                   i=p[j-1] << 8 | p[j];
6167                   MngInfoDiscardObject(mng_info,i);
6168                 }
6169               }
6170
6171             if (length != 0)
6172               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6173
6174             continue;
6175           }
6176
6177         if (memcmp(type,mng_MOVE,4) == 0)
6178           {
6179             size_t
6180               first_object,
6181               last_object;
6182
6183             /* read MOVE */
6184
6185             if (length > 3)
6186             {
6187               first_object=(p[0] << 8) | p[1];
6188               last_object=(p[2] << 8) | p[3];
6189               p+=4;
6190
6191               for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
6192               {
6193                 if (mng_info->exists[i] && !mng_info->frozen[i] &&
6194                     (p-chunk) < (ssize_t) (length-8))
6195                   {
6196                     MngPair
6197                       new_pair;
6198
6199                     MngPair
6200                       old_pair;
6201
6202                     old_pair.a=mng_info->x_off[i];
6203                     old_pair.b=mng_info->y_off[i];
6204                     new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
6205                     mng_info->x_off[i]=new_pair.a;
6206                     mng_info->y_off[i]=new_pair.b;
6207                   }
6208               }
6209             }
6210
6211             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6212             continue;
6213           }
6214
6215         if (memcmp(type,mng_LOOP,4) == 0)
6216           {
6217             ssize_t loop_iters=1;
6218             if (length > 4)
6219               {
6220                 loop_level=chunk[0];
6221                 mng_info->loop_active[loop_level]=1;  /* mark loop active */
6222
6223                 /* Record starting point.  */
6224                 loop_iters=mng_get_long(&chunk[1]);
6225
6226                 if (logging != MagickFalse)
6227                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6228                     "  LOOP level %.20g has %.20g iterations ",
6229                     (double) loop_level, (double) loop_iters);
6230
6231                 if (loop_iters == 0)
6232                   skipping_loop=loop_level;
6233
6234                 else
6235                   {
6236                     mng_info->loop_jump[loop_level]=TellBlob(image);
6237                     mng_info->loop_count[loop_level]=loop_iters;
6238                   }
6239
6240                 mng_info->loop_iteration[loop_level]=0;
6241               }
6242             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6243             continue;
6244           }
6245
6246         if (memcmp(type,mng_ENDL,4) == 0)
6247           {
6248             if (length > 0)
6249               {
6250                 loop_level=chunk[0];
6251
6252                 if (skipping_loop > 0)
6253                   {
6254                     if (skipping_loop == loop_level)
6255                       {
6256                         /*
6257                           Found end of zero-iteration loop.
6258                         */
6259                         skipping_loop=(-1);
6260                         mng_info->loop_active[loop_level]=0;
6261                       }
6262                   }
6263
6264                 else
6265                   {
6266                     if (mng_info->loop_active[loop_level] == 1)
6267                       {
6268                         mng_info->loop_count[loop_level]--;
6269                         mng_info->loop_iteration[loop_level]++;
6270
6271                         if (logging != MagickFalse)
6272                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6273                           "  ENDL: LOOP level %.20g has %.20g remaining iters",
6274                             (double) loop_level,(double)
6275                             mng_info->loop_count[loop_level]);
6276
6277                         if (mng_info->loop_count[loop_level] != 0)
6278                           {
6279                             offset=
6280                               SeekBlob(image,mng_info->loop_jump[loop_level],
6281                               SEEK_SET);
6282
6283                             if (offset < 0)
6284                               ThrowReaderException(CorruptImageError,
6285                                 "ImproperImageHeader");
6286                           }
6287
6288                         else
6289                           {
6290                             short
6291                               last_level;
6292
6293                             /*
6294                               Finished loop.
6295                             */
6296                             mng_info->loop_active[loop_level]=0;
6297                             last_level=(-1);
6298                             for (i=0; i < loop_level; i++)
6299                               if (mng_info->loop_active[i] == 1)
6300                                 last_level=(short) i;
6301                             loop_level=last_level;
6302                           }
6303                       }
6304                   }
6305               }
6306
6307             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6308             continue;
6309           }
6310
6311         if (memcmp(type,mng_CLON,4) == 0)
6312           {
6313             if (mng_info->clon_warning == 0)
6314               (void) ThrowMagickException(exception,GetMagickModule(),
6315                 CoderError,"CLON is not implemented yet","`%s'",
6316                 image->filename);
6317
6318             mng_info->clon_warning++;
6319           }
6320
6321         if (memcmp(type,mng_MAGN,4) == 0)
6322           {
6323             png_uint_16
6324               magn_first,
6325               magn_last,
6326               magn_mb,
6327               magn_ml,
6328               magn_mr,
6329               magn_mt,
6330               magn_mx,
6331               magn_my,
6332               magn_methx,
6333               magn_methy;
6334
6335             if (length > 1)
6336               magn_first=(p[0] << 8) | p[1];
6337
6338             else
6339               magn_first=0;
6340
6341             if (length > 3)
6342               magn_last=(p[2] << 8) | p[3];
6343
6344             else
6345               magn_last=magn_first;
6346 #ifndef MNG_OBJECT_BUFFERS
6347             if (magn_first || magn_last)
6348               if (mng_info->magn_warning == 0)
6349                 {
6350                   (void) ThrowMagickException(exception,
6351                      GetMagickModule(),CoderError,
6352                      "MAGN is not implemented yet for nonzero objects",
6353                      "`%s'",image->filename);
6354
6355                    mng_info->magn_warning++;
6356                 }
6357 #endif
6358             if (length > 4)
6359               magn_methx=p[4];
6360
6361             else
6362               magn_methx=0;
6363
6364             if (length > 6)
6365               magn_mx=(p[5] << 8) | p[6];
6366
6367             else
6368               magn_mx=1;
6369
6370             if (magn_mx == 0)
6371               magn_mx=1;
6372
6373             if (length > 8)
6374               magn_my=(p[7] << 8) | p[8];
6375
6376             else
6377               magn_my=magn_mx;
6378
6379             if (magn_my == 0)
6380               magn_my=1;
6381
6382             if (length > 10)
6383               magn_ml=(p[9] << 8) | p[10];
6384
6385             else
6386               magn_ml=magn_mx;
6387
6388             if (magn_ml == 0)
6389               magn_ml=1;
6390
6391             if (length > 12)
6392               magn_mr=(p[11] << 8) | p[12];
6393
6394             else
6395               magn_mr=magn_mx;
6396
6397             if (magn_mr == 0)
6398               magn_mr=1;
6399
6400             if (length > 14)
6401               magn_mt=(p[13] << 8) | p[14];
6402
6403             else
6404               magn_mt=magn_my;
6405
6406             if (magn_mt == 0)
6407               magn_mt=1;
6408
6409             if (length > 16)
6410               magn_mb=(p[15] << 8) | p[16];
6411
6412             else
6413               magn_mb=magn_my;
6414
6415             if (magn_mb == 0)
6416               magn_mb=1;
6417
6418             if (length > 17)
6419               magn_methy=p[17];
6420
6421             else
6422               magn_methy=magn_methx;
6423
6424
6425             if (magn_methx > 5 || magn_methy > 5)
6426               if (mng_info->magn_warning == 0)
6427                 {
6428                   (void) ThrowMagickException(exception,
6429                      GetMagickModule(),CoderError,
6430                      "Unknown MAGN method in MNG datastream","`%s'",
6431                      image->filename);
6432
6433                    mng_info->magn_warning++;
6434                 }
6435 #ifdef MNG_OBJECT_BUFFERS
6436           /* Magnify existing objects in the range magn_first to magn_last */
6437 #endif
6438             if (magn_first == 0 || magn_last == 0)
6439               {
6440                 /* Save the magnification factors for object 0 */
6441                 mng_info->magn_mb=magn_mb;
6442                 mng_info->magn_ml=magn_ml;
6443                 mng_info->magn_mr=magn_mr;
6444                 mng_info->magn_mt=magn_mt;
6445                 mng_info->magn_mx=magn_mx;
6446                 mng_info->magn_my=magn_my;
6447                 mng_info->magn_methx=magn_methx;
6448                 mng_info->magn_methy=magn_methy;
6449               }
6450           }
6451
6452         if (memcmp(type,mng_PAST,4) == 0)
6453           {
6454             if (mng_info->past_warning == 0)
6455               (void) ThrowMagickException(exception,GetMagickModule(),
6456                 CoderError,"PAST is not implemented yet","`%s'",
6457                 image->filename);
6458
6459             mng_info->past_warning++;
6460           }
6461
6462         if (memcmp(type,mng_SHOW,4) == 0)
6463           {
6464             if (mng_info->show_warning == 0)
6465               (void) ThrowMagickException(exception,GetMagickModule(),
6466                 CoderError,"SHOW is not implemented yet","`%s'",
6467                 image->filename);
6468
6469             mng_info->show_warning++;
6470           }
6471
6472         if (memcmp(type,mng_sBIT,4) == 0)
6473           {
6474             if (length < 4)
6475               mng_info->have_global_sbit=MagickFalse;
6476
6477             else
6478               {
6479                 mng_info->global_sbit.gray=p[0];
6480                 mng_info->global_sbit.red=p[0];
6481                 mng_info->global_sbit.green=p[1];
6482                 mng_info->global_sbit.blue=p[2];
6483                 mng_info->global_sbit.alpha=p[3];
6484                 mng_info->have_global_sbit=MagickTrue;
6485              }
6486           }
6487         if (memcmp(type,mng_pHYs,4) == 0)
6488           {
6489             if (length > 8)
6490               {
6491                 mng_info->global_x_pixels_per_unit=
6492                     (size_t) mng_get_long(p);
6493                 mng_info->global_y_pixels_per_unit=
6494                     (size_t) mng_get_long(&p[4]);
6495                 mng_info->global_phys_unit_type=p[8];
6496                 mng_info->have_global_phys=MagickTrue;
6497               }
6498
6499             else
6500               mng_info->have_global_phys=MagickFalse;
6501           }
6502         if (memcmp(type,mng_pHYg,4) == 0)
6503           {
6504             if (mng_info->phyg_warning == 0)
6505               (void) ThrowMagickException(exception,GetMagickModule(),
6506                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6507
6508             mng_info->phyg_warning++;
6509           }
6510         if (memcmp(type,mng_BASI,4) == 0)
6511           {
6512             skip_to_iend=MagickTrue;
6513
6514             if (mng_info->basi_warning == 0)
6515               (void) ThrowMagickException(exception,GetMagickModule(),
6516                 CoderError,"BASI is not implemented yet","`%s'",
6517                 image->filename);
6518
6519             mng_info->basi_warning++;
6520 #ifdef MNG_BASI_SUPPORTED
6521             basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6522                (p[2] << 8) | p[3]);
6523             basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6524                (p[6] << 8) | p[7]);
6525             basi_color_type=p[8];
6526             basi_compression_method=p[9];
6527             basi_filter_type=p[10];
6528             basi_interlace_method=p[11];
6529             if (length > 11)
6530               basi_red=(p[12] << 8) & p[13];
6531
6532             else
6533               basi_red=0;
6534
6535             if (length > 13)
6536               basi_green=(p[14] << 8) & p[15];
6537
6538             else
6539               basi_green=0;
6540
6541             if (length > 15)
6542               basi_blue=(p[16] << 8) & p[17];
6543
6544             else
6545               basi_blue=0;
6546
6547             if (length > 17)
6548               basi_alpha=(p[18] << 8) & p[19];
6549
6550             else
6551               {
6552                 if (basi_sample_depth == 16)
6553                   basi_alpha=65535L;
6554                 else
6555                   basi_alpha=255;
6556               }
6557
6558             if (length > 19)
6559               basi_viewable=p[20];
6560
6561             else
6562               basi_viewable=0;
6563
6564 #endif
6565             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6566             continue;
6567           }
6568
6569         if (memcmp(type,mng_IHDR,4)
6570 #if defined(JNG_SUPPORTED)
6571             && memcmp(type,mng_JHDR,4)
6572 #endif
6573             )
6574           {
6575             /* Not an IHDR or JHDR chunk */
6576             if (length != 0)
6577               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6578
6579             continue;
6580           }
6581 /* Process IHDR */
6582         if (logging != MagickFalse)
6583           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6584             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6585
6586         mng_info->exists[object_id]=MagickTrue;
6587         mng_info->viewable[object_id]=MagickTrue;
6588
6589         if (mng_info->invisible[object_id])
6590           {
6591             if (logging != MagickFalse)
6592               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6593                 "  Skipping invisible object");
6594
6595             skip_to_iend=MagickTrue;
6596             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6597             continue;
6598           }
6599 #if defined(MNG_INSERT_LAYERS)
6600         if (length < 8)
6601           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6602
6603         image_width=(size_t) mng_get_long(p);
6604         image_height=(size_t) mng_get_long(&p[4]);
6605 #endif
6606         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6607
6608         /*
6609           Insert a transparent background layer behind the entire animation
6610           if it is not full screen.
6611         */
6612 #if defined(MNG_INSERT_LAYERS)
6613         if (insert_layers && mng_type && first_mng_object)
6614           {
6615             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6616                 (image_width < mng_info->mng_width) ||
6617                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6618                 (image_height < mng_info->mng_height) ||
6619                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6620               {
6621                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6622                   {
6623                     /*
6624                       Allocate next image structure.
6625                     */
6626                     AcquireNextImage(image_info,image,exception);
6627
6628                     if (GetNextImageInList(image) == (Image *) NULL)
6629                       {
6630                         image=DestroyImageList(image);
6631                         MngInfoFreeStruct(mng_info,&have_mng_structure);
6632                         return((Image *) NULL);
6633                       }
6634
6635                     image=SyncNextImageInList(image);
6636                   }
6637                 mng_info->image=image;
6638
6639                 if (term_chunk_found)
6640                   {
6641                     image->start_loop=MagickTrue;
6642                     image->iterations=mng_iterations;
6643                     term_chunk_found=MagickFalse;
6644                   }
6645
6646                 else
6647                     image->start_loop=MagickFalse;
6648
6649                 /* Make a background rectangle.  */
6650
6651                 image->delay=0;
6652                 image->columns=mng_info->mng_width;
6653                 image->rows=mng_info->mng_height;
6654                 image->page.width=mng_info->mng_width;
6655                 image->page.height=mng_info->mng_height;
6656                 image->page.x=0;
6657                 image->page.y=0;
6658                 image->background_color=mng_background_color;
6659                 (void) SetImageBackgroundColor(image,exception);
6660                 if (logging != MagickFalse)
6661                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6662                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6663                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6664               }
6665           }
6666         /*
6667           Insert a background layer behind the upcoming image if
6668           framing_mode is 3, and we haven't already inserted one.
6669         */
6670         if (insert_layers && (mng_info->framing_mode == 3) &&
6671                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6672                 (simplicity & 0x08)))
6673           {
6674             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6675             {
6676               /*
6677                 Allocate next image structure.
6678               */
6679               AcquireNextImage(image_info,image,exception);
6680
6681               if (GetNextImageInList(image) == (Image *) NULL)
6682                 {
6683                   image=DestroyImageList(image);
6684                   MngInfoFreeStruct(mng_info,&have_mng_structure);
6685                   return((Image *) NULL);
6686                 }
6687
6688               image=SyncNextImageInList(image);
6689             }
6690
6691             mng_info->image=image;
6692
6693             if (term_chunk_found)
6694               {
6695                 image->start_loop=MagickTrue;
6696                 image->iterations=mng_iterations;
6697                 term_chunk_found=MagickFalse;
6698               }
6699
6700             else
6701                 image->start_loop=MagickFalse;
6702
6703             image->delay=0;
6704             image->columns=subframe_width;
6705             image->rows=subframe_height;
6706             image->page.width=subframe_width;
6707             image->page.height=subframe_height;
6708             image->page.x=mng_info->clip.left;
6709             image->page.y=mng_info->clip.top;
6710             image->background_color=mng_background_color;
6711             image->alpha_trait=UndefinedPixelTrait;
6712             (void) SetImageBackgroundColor(image,exception);
6713
6714             if (logging != MagickFalse)
6715               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6716                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6717                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6718                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6719           }
6720 #endif /* MNG_INSERT_LAYERS */
6721         first_mng_object=MagickFalse;
6722
6723         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6724           {
6725             /*
6726               Allocate next image structure.
6727             */
6728             AcquireNextImage(image_info,image,exception);
6729
6730             if (GetNextImageInList(image) == (Image *) NULL)
6731               {
6732                 image=DestroyImageList(image);
6733                 MngInfoFreeStruct(mng_info,&have_mng_structure);
6734                 return((Image *) NULL);
6735               }
6736
6737             image=SyncNextImageInList(image);
6738           }
6739         mng_info->image=image;
6740         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6741           GetBlobSize(image));
6742
6743         if (status == MagickFalse)
6744           break;
6745
6746         if (term_chunk_found)
6747           {
6748             image->start_loop=MagickTrue;
6749             term_chunk_found=MagickFalse;
6750           }
6751
6752         else
6753             image->start_loop=MagickFalse;
6754
6755         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6756           {
6757             image->delay=frame_delay;
6758             frame_delay=default_frame_delay;
6759           }
6760
6761         else
6762           image->delay=0;
6763
6764         image->page.width=mng_info->mng_width;
6765         image->page.height=mng_info->mng_height;
6766         image->page.x=mng_info->x_off[object_id];
6767         image->page.y=mng_info->y_off[object_id];
6768         image->iterations=mng_iterations;
6769
6770         /*
6771           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6772         */
6773
6774         if (logging != MagickFalse)
6775           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6776             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6777             type[2],type[3]);
6778
6779         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6780
6781         if (offset < 0)
6782           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6783       }
6784
6785     mng_info->image=image;
6786     mng_info->mng_type=mng_type;
6787     mng_info->object_id=object_id;
6788
6789     if (memcmp(type,mng_IHDR,4) == 0)
6790       image=ReadOnePNGImage(mng_info,image_info,exception);
6791
6792 #if defined(JNG_SUPPORTED)
6793     else
6794       image=ReadOneJNGImage(mng_info,image_info,exception);
6795 #endif
6796
6797     if (image == (Image *) NULL)
6798       {
6799         if (logging != MagickFalse)
6800           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6801             "exit ReadJNGImage() with error");
6802
6803         MngInfoFreeStruct(mng_info,&have_mng_structure);
6804         return((Image *) NULL);
6805       }
6806
6807     if (image->columns == 0 || image->rows == 0)
6808       {
6809         (void) CloseBlob(image);
6810         image=DestroyImageList(image);
6811         MngInfoFreeStruct(mng_info,&have_mng_structure);
6812         return((Image *) NULL);
6813       }
6814
6815     mng_info->image=image;
6816
6817     if (mng_type)
6818       {
6819         MngBox
6820           crop_box;
6821
6822         if (mng_info->magn_methx || mng_info->magn_methy)
6823           {
6824             png_uint_32
6825                magnified_height,
6826                magnified_width;
6827
6828             if (logging != MagickFalse)
6829               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6830                 "  Processing MNG MAGN chunk");
6831
6832             if (mng_info->magn_methx == 1)
6833               {
6834                 magnified_width=mng_info->magn_ml;
6835
6836                 if (image->columns > 1)
6837                    magnified_width += mng_info->magn_mr;
6838
6839                 if (image->columns > 2)
6840                    magnified_width += (png_uint_32)
6841                       ((image->columns-2)*(mng_info->magn_mx));
6842               }
6843
6844             else
6845               {
6846                 magnified_width=(png_uint_32) image->columns;
6847
6848                 if (image->columns > 1)
6849                    magnified_width += mng_info->magn_ml-1;
6850
6851                 if (image->columns > 2)
6852                    magnified_width += mng_info->magn_mr-1;
6853
6854                 if (image->columns > 3)
6855                    magnified_width += (png_uint_32)
6856                       ((image->columns-3)*(mng_info->magn_mx-1));
6857               }
6858
6859             if (mng_info->magn_methy == 1)
6860               {
6861                 magnified_height=mng_info->magn_mt;
6862
6863                 if (image->rows > 1)
6864                    magnified_height += mng_info->magn_mb;
6865
6866                 if (image->rows > 2)
6867                    magnified_height += (png_uint_32)
6868                       ((image->rows-2)*(mng_info->magn_my));
6869               }
6870
6871             else
6872               {
6873                 magnified_height=(png_uint_32) image->rows;
6874
6875                 if (image->rows > 1)
6876                    magnified_height += mng_info->magn_mt-1;
6877
6878                 if (image->rows > 2)
6879                    magnified_height += mng_info->magn_mb-1;
6880
6881                 if (image->rows > 3)
6882                    magnified_height += (png_uint_32)
6883                       ((image->rows-3)*(mng_info->magn_my-1));
6884               }
6885
6886             if (magnified_height > image->rows ||
6887                 magnified_width > image->columns)
6888               {
6889                 Image
6890                   *large_image;
6891
6892                 int
6893                   yy;
6894
6895                 Quantum
6896                   *next,
6897                   *prev;
6898
6899                 png_uint_16
6900                   magn_methx,
6901                   magn_methy;
6902
6903                 ssize_t
6904                   m,
6905                   y;
6906
6907                 register Quantum
6908                   *n,
6909                   *q;
6910
6911                 register ssize_t
6912                   x;
6913
6914                 /* Allocate next image structure.  */
6915
6916                 if (logging != MagickFalse)
6917                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6918                     "    Allocate magnified image");
6919
6920                 AcquireNextImage(image_info,image,exception);
6921
6922                 if (GetNextImageInList(image) == (Image *) NULL)
6923                   {
6924                     image=DestroyImageList(image);
6925                     MngInfoFreeStruct(mng_info,&have_mng_structure);
6926                     return((Image *) NULL);
6927                   }
6928
6929                 large_image=SyncNextImageInList(image);
6930
6931                 large_image->columns=magnified_width;
6932                 large_image->rows=magnified_height;
6933
6934                 magn_methx=mng_info->magn_methx;
6935                 magn_methy=mng_info->magn_methy;
6936
6937 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6938 #define QM unsigned short
6939                 if (magn_methx != 1 || magn_methy != 1)
6940                   {
6941                   /*
6942                      Scale pixels to unsigned shorts to prevent
6943                      overflow of intermediate values of interpolations
6944                   */
6945                      for (y=0; y < (ssize_t) image->rows; y++)
6946                      {
6947                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6948                           exception);
6949
6950                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6951                        {
6952                           SetPixelRed(image,ScaleQuantumToShort(
6953                             GetPixelRed(image,q)),q);
6954                           SetPixelGreen(image,ScaleQuantumToShort(
6955                             GetPixelGreen(image,q)),q);
6956                           SetPixelBlue(image,ScaleQuantumToShort(
6957                             GetPixelBlue(image,q)),q);
6958                           SetPixelAlpha(image,ScaleQuantumToShort(
6959                             GetPixelAlpha(image,q)),q);
6960                           q+=GetPixelChannels(image);
6961                        }
6962
6963                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6964                          break;
6965                      }
6966                   }
6967 #else
6968 #define QM Quantum
6969 #endif
6970
6971                 if (image->alpha_trait != UndefinedPixelTrait)
6972                    (void) SetImageBackgroundColor(large_image,exception);
6973
6974                 else
6975                   {
6976                     large_image->background_color.alpha=OpaqueAlpha;
6977                     (void) SetImageBackgroundColor(large_image,exception);
6978
6979                     if (magn_methx == 4)
6980                       magn_methx=2;
6981
6982                     if (magn_methx == 5)
6983                       magn_methx=3;
6984
6985                     if (magn_methy == 4)
6986                       magn_methy=2;
6987
6988                     if (magn_methy == 5)
6989                       magn_methy=3;
6990                   }
6991
6992                 /* magnify the rows into the right side of the large image */
6993
6994                 if (logging != MagickFalse)
6995                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6996                     "    Magnify the rows to %.20g",
6997                     (double) large_image->rows);
6998                 m=(ssize_t) mng_info->magn_mt;
6999                 yy=0;
7000                 length=(size_t) GetPixelChannels(image)*image->columns;
7001                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
7002                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
7003
7004                 if ((prev == (Quantum *) NULL) ||
7005                     (next == (Quantum *) NULL))
7006                   {
7007                      image=DestroyImageList(image);
7008                      MngInfoFreeStruct(mng_info,&have_mng_structure);
7009                      ThrowReaderException(ResourceLimitError,
7010                        "MemoryAllocationFailed");
7011                   }
7012
7013                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
7014                 (void) CopyMagickMemory(next,n,length);
7015
7016                 for (y=0; y < (ssize_t) image->rows; y++)
7017                 {
7018                   if (y == 0)
7019                     m=(ssize_t) mng_info->magn_mt;
7020
7021                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
7022                     m=(ssize_t) mng_info->magn_mb;
7023
7024                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
7025                     m=(ssize_t) mng_info->magn_mb;
7026
7027                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
7028                     m=1;
7029
7030                   else
7031                     m=(ssize_t) mng_info->magn_my;
7032
7033                   n=prev;
7034                   prev=next;
7035                   next=n;
7036
7037                   if (y < (ssize_t) image->rows-1)
7038                     {
7039                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
7040                           exception);
7041                       (void) CopyMagickMemory(next,n,length);
7042                     }
7043
7044                   for (i=0; i < m; i++, yy++)
7045                   {
7046                     register Quantum
7047                       *pixels;
7048
7049                     assert(yy < (ssize_t) large_image->rows);
7050                     pixels=prev;
7051                     n=next;
7052                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
7053                       1,exception);
7054                     q+=(large_image->columns-image->columns)*
7055                       GetPixelChannels(large_image);
7056
7057                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
7058                     {
7059                       /* To do: get color as function of indexes[x] */
7060                       /*
7061                       if (image->storage_class == PseudoClass)
7062                         {
7063                         }
7064                       */
7065
7066                       if (magn_methy <= 1)
7067                         {
7068                           /* replicate previous */
7069                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
7070                           SetPixelGreen(large_image,GetPixelGreen(image,
7071                              pixels),q);
7072                           SetPixelBlue(large_image,GetPixelBlue(image,
7073                              pixels),q);
7074                           SetPixelAlpha(large_image,GetPixelAlpha(image,
7075                              pixels),q);
7076                         }
7077
7078                       else if (magn_methy == 2 || magn_methy == 4)
7079                         {
7080                           if (i == 0)
7081                             {
7082                               SetPixelRed(large_image,GetPixelRed(image,
7083                                  pixels),q);
7084                               SetPixelGreen(large_image,GetPixelGreen(image,
7085                                  pixels),q);
7086                               SetPixelBlue(large_image,GetPixelBlue(image,
7087                                  pixels),q);
7088                               SetPixelAlpha(large_image,GetPixelAlpha(image,
7089                                  pixels),q);
7090                             }
7091
7092                           else
7093                             {
7094                               /* Interpolate */
7095                               SetPixelRed(large_image,((QM) (((ssize_t)
7096                                  (2*i*(GetPixelRed(image,n)
7097                                  -GetPixelRed(image,pixels)+m))/
7098                                  ((ssize_t) (m*2))
7099                                  +GetPixelRed(image,pixels)))),q);
7100                               SetPixelGreen(large_image,((QM) (((ssize_t)
7101                                  (2*i*(GetPixelGreen(image,n)
7102                                  -GetPixelGreen(image,pixels)+m))/
7103                                  ((ssize_t) (m*2))
7104                                  +GetPixelGreen(image,pixels)))),q);
7105                               SetPixelBlue(large_image,((QM) (((ssize_t)
7106                                  (2*i*(GetPixelBlue(image,n)
7107                                  -GetPixelBlue(image,pixels)+m))/
7108                                  ((ssize_t) (m*2))
7109                                  +GetPixelBlue(image,pixels)))),q);
7110
7111                               if (image->alpha_trait != UndefinedPixelTrait)
7112                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
7113                                     (2*i*(GetPixelAlpha(image,n)
7114                                     -GetPixelAlpha(image,pixels)+m))
7115                                     /((ssize_t) (m*2))+
7116                                    GetPixelAlpha(image,pixels)))),q);
7117                             }
7118
7119                           if (magn_methy == 4)
7120                             {
7121                               /* Replicate nearest */
7122                               if (i <= ((m+1) << 1))
7123                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
7124                                     pixels),q);
7125                               else
7126                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
7127                                     n),q);
7128                             }
7129                         }
7130
7131                       else /* if (magn_methy == 3 || magn_methy == 5) */
7132                         {
7133                           /* Replicate nearest */
7134                           if (i <= ((m+1) << 1))
7135                           {
7136                              SetPixelRed(large_image,GetPixelRed(image,
7137                                     pixels),q);
7138                              SetPixelGreen(large_image,GetPixelGreen(image,
7139                                     pixels),q);
7140                              SetPixelBlue(large_image,GetPixelBlue(image,
7141                                     pixels),q);
7142                              SetPixelAlpha(large_image,GetPixelAlpha(image,
7143                                     pixels),q);
7144                           }
7145
7146                           else
7147                           {
7148                              SetPixelRed(large_image,GetPixelRed(image,n),q);
7149                              SetPixelGreen(large_image,GetPixelGreen(image,n),
7150                                     q);
7151                              SetPixelBlue(large_image,GetPixelBlue(image,n),
7152                                     q);
7153                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
7154                                     q);
7155                           }
7156
7157                           if (magn_methy == 5)
7158                             {
7159                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
7160                                  (GetPixelAlpha(image,n)
7161                                  -GetPixelAlpha(image,pixels))
7162                                  +m))/((ssize_t) (m*2))
7163                                  +GetPixelAlpha(image,pixels)),q);
7164                             }
7165                         }
7166                       n+=GetPixelChannels(image);
7167                       q+=GetPixelChannels(large_image);
7168                       pixels+=GetPixelChannels(image);
7169                     } /* x */
7170
7171                     if (SyncAuthenticPixels(large_image,exception) == 0)
7172                       break;
7173
7174                   } /* i */
7175                 } /* y */
7176
7177                 prev=(Quantum *) RelinquishMagickMemory(prev);
7178                 next=(Quantum *) RelinquishMagickMemory(next);
7179
7180                 length=image->columns;
7181
7182                 if (logging != MagickFalse)
7183                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7184                     "    Delete original image");
7185
7186                 DeleteImageFromList(&image);
7187
7188                 image=large_image;
7189
7190                 mng_info->image=image;
7191
7192                 /* magnify the columns */
7193                 if (logging != MagickFalse)
7194                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7195                     "    Magnify the columns to %.20g",
7196                     (double) image->columns);
7197
7198                 for (y=0; y < (ssize_t) image->rows; y++)
7199                 {
7200                   register Quantum
7201                     *pixels;
7202
7203                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7204                   pixels=q+(image->columns-length)*GetPixelChannels(image);
7205                   n=pixels+GetPixelChannels(image);
7206
7207                   for (x=(ssize_t) (image->columns-length);
7208                     x < (ssize_t) image->columns; x++)
7209                   {
7210                     /* To do: Rewrite using Get/Set***PixelChannel() */
7211
7212                     if (x == (ssize_t) (image->columns-length))
7213                       m=(ssize_t) mng_info->magn_ml;
7214
7215                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
7216                       m=(ssize_t) mng_info->magn_mr;
7217
7218                     else if (magn_methx <= 1 &&
7219                         x == (ssize_t) image->columns-1)
7220                       m=(ssize_t) mng_info->magn_mr;
7221
7222                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
7223                       m=1;
7224
7225                     else
7226                       m=(ssize_t) mng_info->magn_mx;
7227
7228                     for (i=0; i < m; i++)
7229                     {
7230                       if (magn_methx <= 1)
7231                         {
7232                           /* replicate previous */
7233                           SetPixelRed(image,GetPixelRed(image,pixels),q);
7234                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7235                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7236                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7237                         }
7238
7239                       else if (magn_methx == 2 || magn_methx == 4)
7240                         {
7241                           if (i == 0)
7242                           {
7243                             SetPixelRed(image,GetPixelRed(image,pixels),q);
7244                             SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7245                             SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7246                             SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7247                           }
7248
7249                           /* To do: Rewrite using Get/Set***PixelChannel() */
7250                           else
7251                             {
7252                               /* Interpolate */
7253                               SetPixelRed(image,(QM) ((2*i*(
7254                                  GetPixelRed(image,n)
7255                                  -GetPixelRed(image,pixels))+m)
7256                                  /((ssize_t) (m*2))+
7257                                  GetPixelRed(image,pixels)),q);
7258
7259                               SetPixelGreen(image,(QM) ((2*i*(
7260                                  GetPixelGreen(image,n)
7261                                  -GetPixelGreen(image,pixels))+m)
7262                                  /((ssize_t) (m*2))+
7263                                  GetPixelGreen(image,pixels)),q);
7264
7265                               SetPixelBlue(image,(QM) ((2*i*(
7266                                  GetPixelBlue(image,n)
7267                                  -GetPixelBlue(image,pixels))+m)
7268                                  /((ssize_t) (m*2))+
7269                                  GetPixelBlue(image,pixels)),q);
7270                               if (image->alpha_trait != UndefinedPixelTrait)
7271                                  SetPixelAlpha(image,(QM) ((2*i*(
7272                                    GetPixelAlpha(image,n)
7273                                    -GetPixelAlpha(image,pixels))+m)
7274                                    /((ssize_t) (m*2))+
7275                                    GetPixelAlpha(image,pixels)),q);
7276                             }
7277
7278                           if (magn_methx == 4)
7279                             {
7280                               /* Replicate nearest */
7281                               if (i <= ((m+1) << 1))
7282                               {
7283                                  SetPixelAlpha(image,
7284                                    GetPixelAlpha(image,pixels)+0,q);
7285                               }
7286                               else
7287                               {
7288                                  SetPixelAlpha(image,
7289                                    GetPixelAlpha(image,n)+0,q);
7290                               }
7291                             }
7292                         }
7293
7294                       else /* if (magn_methx == 3 || magn_methx == 5) */
7295                         {
7296                           /* Replicate nearest */
7297                           if (i <= ((m+1) << 1))
7298                           {
7299                              SetPixelRed(image,GetPixelRed(image,pixels),q);
7300                              SetPixelGreen(image,GetPixelGreen(image,
7301                                  pixels),q);
7302                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7303                              SetPixelAlpha(image,GetPixelAlpha(image,
7304                                  pixels),q);
7305                           }
7306
7307                           else
7308                           {
7309                              SetPixelRed(image,GetPixelRed(image,n),q);
7310                              SetPixelGreen(image,GetPixelGreen(image,n),q);
7311                              SetPixelBlue(image,GetPixelBlue(image,n),q);
7312                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7313                           }
7314
7315                           if (magn_methx == 5)
7316                             {
7317                               /* Interpolate */
7318                               SetPixelAlpha(image,
7319                                  (QM) ((2*i*( GetPixelAlpha(image,n)
7320                                  -GetPixelAlpha(image,pixels))+m)/
7321                                  ((ssize_t) (m*2))
7322                                  +GetPixelAlpha(image,pixels)),q);
7323                             }
7324                         }
7325                       q+=GetPixelChannels(image);
7326                     }
7327                     n+=GetPixelChannels(image);
7328                   }
7329
7330                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
7331                     break;
7332                 }
7333 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7334               if (magn_methx != 1 || magn_methy != 1)
7335                 {
7336                 /*
7337                    Rescale pixels to Quantum
7338                 */
7339                    for (y=0; y < (ssize_t) image->rows; y++)
7340                    {
7341                      q=GetAuthenticPixels(image,0,y,image->columns,1,
7342                        exception);
7343
7344                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
7345                      {
7346                         SetPixelRed(image,ScaleShortToQuantum(
7347                           GetPixelRed(image,q)),q);
7348                         SetPixelGreen(image,ScaleShortToQuantum(
7349                           GetPixelGreen(image,q)),q);
7350                         SetPixelBlue(image,ScaleShortToQuantum(
7351                           GetPixelBlue(image,q)),q);
7352                         SetPixelAlpha(image,ScaleShortToQuantum(
7353                           GetPixelAlpha(image,q)),q);
7354                         q+=GetPixelChannels(image);
7355                      }
7356
7357                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
7358                        break;
7359                    }
7360                 }
7361 #endif
7362                 if (logging != MagickFalse)
7363                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7364                     "  Finished MAGN processing");
7365               }
7366           }
7367
7368         /*
7369           Crop_box is with respect to the upper left corner of the MNG.
7370         */
7371         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7372         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7373         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7374         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7375         crop_box=mng_minimum_box(crop_box,mng_info->clip);
7376         crop_box=mng_minimum_box(crop_box,mng_info->frame);
7377         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7378         if ((crop_box.left != (mng_info->image_box.left
7379             +mng_info->x_off[object_id])) ||
7380             (crop_box.right != (mng_info->image_box.right
7381             +mng_info->x_off[object_id])) ||
7382             (crop_box.top != (mng_info->image_box.top
7383             +mng_info->y_off[object_id])) ||
7384             (crop_box.bottom != (mng_info->image_box.bottom
7385             +mng_info->y_off[object_id])))
7386           {
7387             if (logging != MagickFalse)
7388               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7389                 "  Crop the PNG image");
7390
7391             if ((crop_box.left < crop_box.right) &&
7392                 (crop_box.top < crop_box.bottom))
7393               {
7394                 Image
7395                   *im;
7396
7397                 RectangleInfo
7398                   crop_info;
7399
7400                 /*
7401                   Crop_info is with respect to the upper left corner of
7402                   the image.
7403                 */
7404                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7405                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7406                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7407                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7408                 image->page.width=image->columns;
7409                 image->page.height=image->rows;
7410                 image->page.x=0;
7411                 image->page.y=0;
7412                 im=CropImage(image,&crop_info,exception);
7413
7414                 if (im != (Image *) NULL)
7415                   {
7416                     image->columns=im->columns;
7417                     image->rows=im->rows;
7418                     im=DestroyImage(im);
7419                     image->page.width=image->columns;
7420                     image->page.height=image->rows;
7421                     image->page.x=crop_box.left;
7422                     image->page.y=crop_box.top;
7423                   }
7424               }
7425
7426             else
7427               {
7428                 /*
7429                   No pixels in crop area.  The MNG spec still requires
7430                   a layer, though, so make a single transparent pixel in
7431                   the top left corner.
7432                 */
7433                 image->columns=1;
7434                 image->rows=1;
7435                 image->colors=2;
7436                 (void) SetImageBackgroundColor(image,exception);
7437                 image->page.width=1;
7438                 image->page.height=1;
7439                 image->page.x=0;
7440                 image->page.y=0;
7441               }
7442           }
7443 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7444         image=mng_info->image;
7445 #endif
7446       }
7447
7448 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7449       /* PNG does not handle depths greater than 16 so reduce it even
7450        * if lossy.
7451        */
7452       if (image->depth > 16)
7453          image->depth=16;
7454 #endif
7455
7456 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7457       if (image->depth > 8)
7458         {
7459           /* To do: fill low byte properly */
7460           image->depth=16;
7461         }
7462
7463       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7464          image->depth = 8;
7465 #endif
7466
7467       if (image_info->number_scenes != 0)
7468         {
7469           if (mng_info->scenes_found >
7470              (ssize_t) (image_info->first_scene+image_info->number_scenes))
7471             break;
7472         }
7473
7474       if (logging != MagickFalse)
7475         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7476           "  Finished reading image datastream.");
7477
7478   } while (LocaleCompare(image_info->magick,"MNG") == 0);
7479
7480   (void) CloseBlob(image);
7481
7482   if (logging != MagickFalse)
7483     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7484       "  Finished reading all image datastreams.");
7485
7486 #if defined(MNG_INSERT_LAYERS)
7487   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7488        (mng_info->mng_height))
7489     {
7490       /*
7491         Insert a background layer if nothing else was found.
7492       */
7493       if (logging != MagickFalse)
7494         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7495           "  No images found.  Inserting a background layer.");
7496
7497       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7498         {
7499           /*
7500             Allocate next image structure.
7501           */
7502           AcquireNextImage(image_info,image,exception);
7503           if (GetNextImageInList(image) == (Image *) NULL)
7504             {
7505               image=DestroyImageList(image);
7506               MngInfoFreeStruct(mng_info,&have_mng_structure);
7507
7508               if (logging != MagickFalse)
7509                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7510                   "  Allocation failed, returning NULL.");
7511
7512               return((Image *) NULL);
7513             }
7514           image=SyncNextImageInList(image);
7515         }
7516       image->columns=mng_info->mng_width;
7517       image->rows=mng_info->mng_height;
7518       image->page.width=mng_info->mng_width;
7519       image->page.height=mng_info->mng_height;
7520       image->page.x=0;
7521       image->page.y=0;
7522       image->background_color=mng_background_color;
7523       image->alpha_trait=UndefinedPixelTrait;
7524
7525       if (image_info->ping == MagickFalse)
7526         (void) SetImageBackgroundColor(image,exception);
7527
7528       mng_info->image_found++;
7529     }
7530 #endif
7531   image->iterations=mng_iterations;
7532
7533   if (mng_iterations == 1)
7534     image->start_loop=MagickTrue;
7535
7536   while (GetPreviousImageInList(image) != (Image *) NULL)
7537   {
7538     image_count++;
7539     if (image_count > 10*mng_info->image_found)
7540       {
7541         if (logging != MagickFalse)
7542           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7543
7544         (void) ThrowMagickException(exception,GetMagickModule(),
7545           CoderError,"Linked list is corrupted, beginning of list not found",
7546           "`%s'",image_info->filename);
7547
7548         return((Image *) NULL);
7549       }
7550
7551     image=GetPreviousImageInList(image);
7552
7553     if (GetNextImageInList(image) == (Image *) NULL)
7554       {
7555         if (logging != MagickFalse)
7556           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7557
7558         (void) ThrowMagickException(exception,GetMagickModule(),
7559           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7560           image_info->filename);
7561       }
7562   }
7563
7564   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7565              GetNextImageInList(image) ==
7566      (Image *) NULL)
7567     {
7568       if (logging != MagickFalse)
7569         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7570             "  First image null");
7571
7572       (void) ThrowMagickException(exception,GetMagickModule(),
7573         CoderError,"image->next for first image is NULL but shouldn't be.",
7574         "`%s'",image_info->filename);
7575     }
7576
7577   if (mng_info->image_found == 0)
7578     {
7579       if (logging != MagickFalse)
7580         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7581           "  No visible images found.");
7582
7583       (void) ThrowMagickException(exception,GetMagickModule(),
7584         CoderError,"No visible images in file","`%s'",image_info->filename);
7585
7586       if (image != (Image *) NULL)
7587         image=DestroyImageList(image);
7588
7589       MngInfoFreeStruct(mng_info,&have_mng_structure);
7590       return((Image *) NULL);
7591     }
7592
7593   if (mng_info->ticks_per_second)
7594     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7595             final_delay/mng_info->ticks_per_second;
7596
7597   else
7598     image->start_loop=MagickTrue;
7599
7600   /* Find final nonzero image delay */
7601   final_image_delay=0;
7602
7603   while (GetNextImageInList(image) != (Image *) NULL)
7604     {
7605       if (image->delay)
7606         final_image_delay=image->delay;
7607
7608       image=GetNextImageInList(image);
7609     }
7610
7611   if (final_delay < final_image_delay)
7612     final_delay=final_image_delay;
7613
7614   image->delay=final_delay;
7615
7616   if (logging != MagickFalse)
7617       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7618         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7619         (double) final_delay);
7620
7621   if (logging != MagickFalse)
7622     {
7623       int
7624         scene;
7625
7626       scene=0;
7627       image=GetFirstImageInList(image);
7628
7629       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7630         "  Before coalesce:");
7631
7632       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7633         "    scene 0 delay=%.20g",(double) image->delay);
7634
7635       while (GetNextImageInList(image) != (Image *) NULL)
7636       {
7637         image=GetNextImageInList(image);
7638         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7639           "    scene %.20g delay=%.20g",(double) scene++,
7640           (double) image->delay);
7641       }
7642     }
7643
7644   image=GetFirstImageInList(image);
7645 #ifdef MNG_COALESCE_LAYERS
7646   if (insert_layers)
7647     {
7648       Image
7649         *next_image,
7650         *next;
7651
7652       size_t
7653         scene;
7654
7655       if (logging != MagickFalse)
7656         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7657           "  Coalesce Images");
7658
7659       scene=image->scene;
7660       next_image=CoalesceImages(image,exception);
7661
7662       if (next_image == (Image *) NULL)
7663         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7664
7665       image=DestroyImageList(image);
7666       image=next_image;
7667
7668       for (next=image; next != (Image *) NULL; next=next_image)
7669       {
7670          next->page.width=mng_info->mng_width;
7671          next->page.height=mng_info->mng_height;
7672          next->page.x=0;
7673          next->page.y=0;
7674          next->scene=scene++;
7675          next_image=GetNextImageInList(next);
7676
7677          if (next_image == (Image *) NULL)
7678            break;
7679
7680          if (next->delay == 0)
7681            {
7682              scene--;
7683              next_image->previous=GetPreviousImageInList(next);
7684              if (GetPreviousImageInList(next) == (Image *) NULL)
7685                image=next_image;
7686              else
7687                next->previous->next=next_image;
7688              next=DestroyImage(next);
7689            }
7690       }
7691     }
7692 #endif
7693
7694   while (GetNextImageInList(image) != (Image *) NULL)
7695       image=GetNextImageInList(image);
7696
7697   image->dispose=BackgroundDispose;
7698
7699   if (logging != MagickFalse)
7700     {
7701       int
7702         scene;
7703
7704       scene=0;
7705       image=GetFirstImageInList(image);
7706
7707       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7708         "  After coalesce:");
7709
7710       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7711         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7712         (double) image->dispose);
7713
7714       while (GetNextImageInList(image) != (Image *) NULL)
7715       {
7716         image=GetNextImageInList(image);
7717
7718         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7719           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7720           (double) image->delay,(double) image->dispose);
7721       }
7722    }
7723
7724   image=GetFirstImageInList(image);
7725   MngInfoFreeStruct(mng_info,&have_mng_structure);
7726   have_mng_structure=MagickFalse;
7727
7728   if (logging != MagickFalse)
7729     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7730
7731   return(GetFirstImageInList(image));
7732 }
7733 #else /* PNG_LIBPNG_VER > 10011 */
7734 static Image *ReadPNGImage(const ImageInfo *image_info,
7735    ExceptionInfo *exception)
7736 {
7737   printf("Your PNG library is too old: You have libpng-%s\n",
7738      PNG_LIBPNG_VER_STRING);
7739
7740   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7741     "PNG library is too old","`%s'",image_info->filename);
7742
7743   return(Image *) NULL;
7744 }
7745
7746 static Image *ReadMNGImage(const ImageInfo *image_info,
7747    ExceptionInfo *exception)
7748 {
7749   return(ReadPNGImage(image_info,exception));
7750 }
7751 #endif /* PNG_LIBPNG_VER > 10011 */
7752 #endif
7753 \f
7754 /*
7755 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7756 %                                                                             %
7757 %                                                                             %
7758 %                                                                             %
7759 %   R e g i s t e r P N G I m a g e                                           %
7760 %                                                                             %
7761 %                                                                             %
7762 %                                                                             %
7763 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7764 %
7765 %  RegisterPNGImage() adds properties for the PNG image format to
7766 %  the list of supported formats.  The properties include the image format
7767 %  tag, a method to read and/or write the format, whether the format
7768 %  supports the saving of more than one frame to the same file or blob,
7769 %  whether the format supports native in-memory I/O, and a brief
7770 %  description of the format.
7771 %
7772 %  The format of the RegisterPNGImage method is:
7773 %
7774 %      size_t RegisterPNGImage(void)
7775 %
7776 */
7777 ModuleExport size_t RegisterPNGImage(void)
7778 {
7779   char
7780     version[MagickPathExtent];
7781
7782   MagickInfo
7783     *entry;
7784
7785   static const char
7786     *PNGNote=
7787     {
7788       "See http://www.libpng.org/ for details about the PNG format."
7789     },
7790
7791     *JNGNote=
7792     {
7793       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7794       "format."
7795     },
7796
7797     *MNGNote=
7798     {
7799       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7800       "format."
7801     };
7802
7803   *version='\0';
7804
7805 #if defined(PNG_LIBPNG_VER_STRING)
7806   (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7807   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
7808    MagickPathExtent);
7809
7810   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7811     {
7812       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7813       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7814             MagickPathExtent);
7815     }
7816 #endif
7817
7818   entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7819   entry->flags|=CoderSeekableStreamFlag;  /* To do: eliminate this. */
7820
7821 #if defined(MAGICKCORE_PNG_DELEGATE)
7822   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7823   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7824 #endif
7825
7826   entry->magick=(IsImageFormatHandler *) IsMNG;
7827
7828   if (*version != '\0')
7829     entry->version=ConstantString(version);
7830
7831   entry->mime_type=ConstantString("video/x-mng");
7832   entry->note=ConstantString(MNGNote);
7833   (void) RegisterMagickInfo(entry);
7834
7835   entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
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^=CoderAdjoinFlag;
7844   entry->mime_type=ConstantString("image/png");
7845
7846   if (*version != '\0')
7847     entry->version=ConstantString(version);
7848
7849   entry->note=ConstantString(PNGNote);
7850   (void) RegisterMagickInfo(entry);
7851
7852   entry=AcquireMagickInfo("PNG","PNG8",
7853     "8-bit indexed with optional binary transparency");
7854
7855 #if defined(MAGICKCORE_PNG_DELEGATE)
7856   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7857   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7858 #endif
7859
7860   entry->magick=(IsImageFormatHandler *) IsPNG;
7861   entry->flags^=CoderAdjoinFlag;
7862   entry->mime_type=ConstantString("image/png");
7863   (void) RegisterMagickInfo(entry);
7864
7865   entry=AcquireMagickInfo("PNG","PNG24",
7866     "opaque or binary transparent 24-bit RGB");
7867   *version='\0';
7868
7869 #if defined(ZLIB_VERSION)
7870   (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7871   (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7872
7873   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7874     {
7875       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7876       (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7877     }
7878 #endif
7879
7880   if (*version != '\0')
7881     entry->version=ConstantString(version);
7882
7883 #if defined(MAGICKCORE_PNG_DELEGATE)
7884   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7885   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7886 #endif
7887
7888   entry->magick=(IsImageFormatHandler *) IsPNG;
7889   entry->flags^=CoderAdjoinFlag;
7890   entry->mime_type=ConstantString("image/png");
7891   (void) RegisterMagickInfo(entry);
7892
7893   entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7894
7895 #if defined(MAGICKCORE_PNG_DELEGATE)
7896   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7897   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7898 #endif
7899
7900   entry->magick=(IsImageFormatHandler *) IsPNG;
7901   entry->flags^=CoderAdjoinFlag;
7902   entry->mime_type=ConstantString("image/png");
7903   (void) RegisterMagickInfo(entry);
7904
7905   entry=AcquireMagickInfo("PNG","PNG48",
7906     "opaque or binary transparent 48-bit RGB");
7907
7908 #if defined(MAGICKCORE_PNG_DELEGATE)
7909   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7910   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7911 #endif
7912
7913   entry->magick=(IsImageFormatHandler *) IsPNG;
7914   entry->flags^=CoderAdjoinFlag;
7915   entry->mime_type=ConstantString("image/png");
7916   (void) RegisterMagickInfo(entry);
7917
7918   entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7919
7920 #if defined(MAGICKCORE_PNG_DELEGATE)
7921   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7922   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7923 #endif
7924
7925   entry->magick=(IsImageFormatHandler *) IsPNG;
7926   entry->flags^=CoderAdjoinFlag;
7927   entry->mime_type=ConstantString("image/png");
7928   (void) RegisterMagickInfo(entry);
7929
7930   entry=AcquireMagickInfo("PNG","PNG00",
7931     "PNG inheriting bit-depth, color-type from original, if possible");
7932
7933 #if defined(MAGICKCORE_PNG_DELEGATE)
7934   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7935   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7936 #endif
7937
7938   entry->magick=(IsImageFormatHandler *) IsPNG;
7939   entry->flags^=CoderAdjoinFlag;
7940   entry->mime_type=ConstantString("image/png");
7941   (void) RegisterMagickInfo(entry);
7942
7943   entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7944
7945 #if defined(JNG_SUPPORTED)
7946 #if defined(MAGICKCORE_PNG_DELEGATE)
7947   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7948   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7949 #endif
7950 #endif
7951
7952   entry->magick=(IsImageFormatHandler *) IsJNG;
7953   entry->flags^=CoderAdjoinFlag;
7954   entry->mime_type=ConstantString("image/x-jng");
7955   entry->note=ConstantString(JNGNote);
7956   (void) RegisterMagickInfo(entry);
7957
7958 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7959   ping_semaphore=AcquireSemaphoreInfo();
7960 #endif
7961
7962   return(MagickImageCoderSignature);
7963 }
7964 \f
7965 /*
7966 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7967 %                                                                             %
7968 %                                                                             %
7969 %                                                                             %
7970 %   U n r e g i s t e r P N G I m a g e                                       %
7971 %                                                                             %
7972 %                                                                             %
7973 %                                                                             %
7974 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7975 %
7976 %  UnregisterPNGImage() removes format registrations made by the
7977 %  PNG module from the list of supported formats.
7978 %
7979 %  The format of the UnregisterPNGImage method is:
7980 %
7981 %      UnregisterPNGImage(void)
7982 %
7983 */
7984 ModuleExport void UnregisterPNGImage(void)
7985 {
7986   (void) UnregisterMagickInfo("MNG");
7987   (void) UnregisterMagickInfo("PNG");
7988   (void) UnregisterMagickInfo("PNG8");
7989   (void) UnregisterMagickInfo("PNG24");
7990   (void) UnregisterMagickInfo("PNG32");
7991   (void) UnregisterMagickInfo("PNG48");
7992   (void) UnregisterMagickInfo("PNG64");
7993   (void) UnregisterMagickInfo("PNG00");
7994   (void) UnregisterMagickInfo("JNG");
7995
7996 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7997   if (ping_semaphore != (SemaphoreInfo *) NULL)
7998     RelinquishSemaphoreInfo(&ping_semaphore);
7999 #endif
8000 }
8001 \f
8002 #if defined(MAGICKCORE_PNG_DELEGATE)
8003 #if PNG_LIBPNG_VER > 10011
8004 /*
8005 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8006 %                                                                             %
8007 %                                                                             %
8008 %                                                                             %
8009 %   W r i t e M N G I m a g e                                                 %
8010 %                                                                             %
8011 %                                                                             %
8012 %                                                                             %
8013 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8014 %
8015 %  WriteMNGImage() writes an image in the Portable Network Graphics
8016 %  Group's "Multiple-image Network Graphics" encoded image format.
8017 %
8018 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
8019 %
8020 %  The format of the WriteMNGImage method is:
8021 %
8022 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
8023 %        Image *image,ExceptionInfo *exception)
8024 %
8025 %  A description of each parameter follows.
8026 %
8027 %    o image_info: the image info.
8028 %
8029 %    o image:  The image.
8030 %
8031 %    o exception: return any errors or warnings in this structure.
8032 %
8033 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
8034 %    "To do" under ReadPNGImage):
8035 %
8036 %    Preserve all unknown and not-yet-handled known chunks found in input
8037 %    PNG file and copy them  into output PNG files according to the PNG
8038 %    copying rules.
8039 %
8040 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
8041 %
8042 %    Improve selection of color type (use indexed-colour or indexed-colour
8043 %    with tRNS when 256 or fewer unique RGBA values are present).
8044 %
8045 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
8046 %    This will be complicated if we limit ourselves to generating MNG-LC
8047 %    files.  For now we ignore disposal method 3 and simply overlay the next
8048 %    image on it.
8049 %
8050 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
8051 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
8052 %    [mostly done 15 June 1999 but still need to take care of tRNS]
8053 %
8054 %    Check for identical sRGB and replace with a global sRGB (and remove
8055 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
8056 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
8057 %    local gAMA/cHRM with local sRGB if appropriate).
8058 %
8059 %    Check for identical sBIT chunks and write global ones.
8060 %
8061 %    Provide option to skip writing the signature tEXt chunks.
8062 %
8063 %    Use signatures to detect identical objects and reuse the first
8064 %    instance of such objects instead of writing duplicate objects.
8065 %
8066 %    Use a smaller-than-32k value of compression window size when
8067 %    appropriate.
8068 %
8069 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
8070 %    ancillary text chunks and save profiles.
8071 %
8072 %    Provide an option to force LC files (to ensure exact framing rate)
8073 %    instead of VLC.
8074 %
8075 %    Provide an option to force VLC files instead of LC, even when offsets
8076 %    are present.  This will involve expanding the embedded images with a
8077 %    transparent region at the top and/or left.
8078 */
8079
8080 static void
8081 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
8082    png_info *ping_info, unsigned char *profile_type, unsigned char
8083    *profile_description, unsigned char *profile_data, png_uint_32 length)
8084 {
8085    png_textp
8086      text;
8087
8088    register ssize_t
8089      i;
8090
8091    unsigned char
8092      *sp;
8093
8094    png_charp
8095      dp;
8096
8097    png_uint_32
8098      allocated_length,
8099      description_length;
8100
8101    unsigned char
8102      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
8103
8104    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
8105       return;
8106
8107    if (image_info->verbose)
8108      {
8109        (void) printf("writing raw profile: type=%s, length=%.20g\n",
8110          (char *) profile_type, (double) length);
8111      }
8112
8113 #if PNG_LIBPNG_VER >= 10400
8114    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
8115 #else
8116    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
8117 #endif
8118    description_length=(png_uint_32) strlen((const char *) profile_description);
8119    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
8120       + description_length);
8121 #if PNG_LIBPNG_VER >= 10400
8122    text[0].text=(png_charp) png_malloc(ping,
8123       (png_alloc_size_t) allocated_length);
8124    text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
8125 #else
8126    text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
8127    text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
8128 #endif
8129    text[0].key[0]='\0';
8130    (void) ConcatenateMagickString(text[0].key,
8131       "Raw profile type ",MagickPathExtent);
8132    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
8133    sp=profile_data;
8134    dp=text[0].text;
8135    *dp++='\n';
8136    (void) CopyMagickString(dp,(const char *) profile_description,
8137      allocated_length);
8138    dp+=description_length;
8139    *dp++='\n';
8140    (void) FormatLocaleString(dp,allocated_length-
8141      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
8142    dp+=8;
8143
8144    for (i=0; i < (ssize_t) length; i++)
8145    {
8146      if (i%36 == 0)
8147        *dp++='\n';
8148      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
8149      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
8150    }
8151
8152    *dp++='\n';
8153    *dp='\0';
8154    text[0].text_length=(png_size_t) (dp-text[0].text);
8155    text[0].compression=image_info->compression == NoCompression ||
8156      (image_info->compression == UndefinedCompression &&
8157      text[0].text_length < 128) ? -1 : 0;
8158
8159    if (text[0].text_length <= allocated_length)
8160      png_set_text(ping,ping_info,text,1);
8161
8162    png_free(ping,text[0].text);
8163    png_free(ping,text[0].key);
8164    png_free(ping,text);
8165 }
8166
8167 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
8168   const char *string, MagickBooleanType logging)
8169 {
8170   char
8171     *name;
8172
8173   const StringInfo
8174     *profile;
8175
8176   unsigned char
8177     *data;
8178
8179   png_uint_32 length;
8180
8181   ResetImageProfileIterator(image);
8182
8183   for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8184   {
8185     profile=GetImageProfile(image,name);
8186
8187     if (profile != (const StringInfo *) NULL)
8188       {
8189         StringInfo
8190           *ping_profile;
8191
8192         if (LocaleNCompare(name,string,11) == 0)
8193           {
8194             if (logging != MagickFalse)
8195                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8196                    "  Found %s profile",name);
8197
8198             ping_profile=CloneStringInfo(profile);
8199             data=GetStringInfoDatum(ping_profile),
8200             length=(png_uint_32) GetStringInfoLength(ping_profile);
8201             data[4]=data[3];
8202             data[3]=data[2];
8203             data[2]=data[1];
8204             data[1]=data[0];
8205             (void) WriteBlobMSBULong(image,length-5);  /* data length */
8206             (void) WriteBlob(image,length-1,data+1);
8207             (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
8208             ping_profile=DestroyStringInfo(ping_profile);
8209           }
8210       }
8211
8212       name=GetNextImageProfile(image);
8213    }
8214
8215    return(MagickTrue);
8216 }
8217
8218 static inline MagickBooleanType Magick_png_color_equal(const Image *image,
8219   const Quantum *p, const PixelInfo *q)
8220 {
8221   MagickRealType
8222     value;
8223
8224   value=(MagickRealType) p[image->channel_map[RedPixelChannel].offset];
8225   if (AbsolutePixelValue(value-q->red) >= MagickEpsilon)
8226     return(MagickFalse);
8227   value=(MagickRealType) p[image->channel_map[GreenPixelChannel].offset];
8228   if (AbsolutePixelValue(value-q->green) >= MagickEpsilon)
8229     return(MagickFalse);
8230   value=(MagickRealType) p[image->channel_map[BluePixelChannel].offset];
8231   if (AbsolutePixelValue(value-q->blue) >= MagickEpsilon)
8232     return(MagickFalse);
8233
8234   return(MagickTrue);
8235 }
8236
8237 #if defined(PNG_tIME_SUPPORTED)
8238 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
8239   const char *date,ExceptionInfo *exception)
8240 {
8241   unsigned int
8242     day,
8243     hour,
8244     minute,
8245     month,
8246     second,
8247     year;
8248
8249   png_time
8250     ptime;
8251
8252   time_t
8253     ttime;
8254
8255   if (date != (const char *) NULL)
8256     {
8257       if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
8258           &second) != 6)
8259         {
8260           (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8261             "Invalid date format specified for png:tIME","`%s'",
8262             image->filename);
8263           return;
8264         }
8265       ptime.year=(png_uint_16) year;
8266       ptime.month=(png_byte) month;
8267       ptime.day=(png_byte) day;
8268       ptime.hour=(png_byte) hour;
8269       ptime.minute=(png_byte) minute;
8270       ptime.second=(png_byte) second;
8271     }
8272   else
8273   {
8274     time(&ttime);
8275     png_convert_from_time_t(&ptime,ttime);
8276   }
8277   png_set_tIME(ping,info,&ptime);
8278 }
8279 #endif
8280
8281 /* Write one PNG image */
8282 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
8283   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
8284 {
8285   char
8286     im_vers[32],
8287     libpng_runv[32],
8288     libpng_vers[32],
8289     zlib_runv[32],
8290     zlib_vers[32];
8291
8292   Image
8293     *image;
8294
8295   ImageInfo
8296     *image_info;
8297
8298   char
8299     s[2];
8300
8301   const char
8302     *name,
8303     *property,
8304     *value;
8305
8306   const StringInfo
8307     *profile;
8308
8309   int
8310     num_passes,
8311     pass,
8312     ping_wrote_caNv;
8313
8314   png_byte
8315      ping_trans_alpha[256];
8316
8317   png_color
8318      palette[257];
8319
8320   png_color_16
8321     ping_background,
8322     ping_trans_color;
8323
8324   png_info
8325     *ping_info;
8326
8327   png_struct
8328     *ping;
8329
8330   png_uint_32
8331     ping_height,
8332     ping_width;
8333
8334   ssize_t
8335     y;
8336
8337   MagickBooleanType
8338     image_matte,
8339     logging,
8340     matte,
8341
8342     ping_have_blob,
8343     ping_have_cheap_transparency,
8344     ping_have_color,
8345     ping_have_non_bw,
8346     ping_have_PLTE,
8347     ping_have_bKGD,
8348     ping_have_eXIf,
8349     ping_have_iCCP,
8350     ping_have_pHYs,
8351     ping_have_sRGB,
8352     ping_have_tRNS,
8353
8354     ping_exclude_bKGD,
8355     ping_exclude_cHRM,
8356     ping_exclude_date,
8357     /* ping_exclude_EXIF, */
8358     ping_exclude_eXIf,
8359     ping_exclude_gAMA,
8360     ping_exclude_iCCP,
8361     /* ping_exclude_iTXt, */
8362     ping_exclude_oFFs,
8363     ping_exclude_pHYs,
8364     ping_exclude_sRGB,
8365     ping_exclude_tEXt,
8366     ping_exclude_tIME,
8367     /* ping_exclude_tRNS, */
8368     ping_exclude_vpAg,
8369     ping_exclude_caNv,
8370     ping_exclude_zCCP, /* hex-encoded iCCP */
8371     ping_exclude_zTXt,
8372
8373     ping_preserve_colormap,
8374     ping_preserve_iCCP,
8375     ping_need_colortype_warning,
8376
8377     status,
8378     tried_332,
8379     tried_333,
8380     tried_444;
8381
8382   MemoryInfo
8383     *volatile pixel_info;
8384
8385   QuantumInfo
8386     *quantum_info;
8387
8388   PNGErrorInfo
8389     error_info;
8390
8391   register ssize_t
8392     i,
8393     x;
8394
8395   unsigned char
8396     *ping_pixels;
8397
8398   volatile int
8399     image_colors,
8400     ping_bit_depth,
8401     ping_color_type,
8402     ping_interlace_method,
8403     ping_compression_method,
8404     ping_filter_method,
8405     ping_num_trans;
8406
8407   volatile size_t
8408     image_depth,
8409     old_bit_depth;
8410
8411   size_t
8412     quality,
8413     rowbytes,
8414     save_image_depth;
8415
8416   int
8417     j,
8418     number_colors,
8419     number_opaque,
8420     number_semitransparent,
8421     number_transparent,
8422     ping_pHYs_unit_type;
8423
8424   png_uint_32
8425     ping_pHYs_x_resolution,
8426     ping_pHYs_y_resolution;
8427
8428   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8429     "  Enter WriteOnePNGImage()");
8430
8431   image = CloneImage(IMimage,0,0,MagickFalse,exception);
8432   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8433   if (image_info == (ImageInfo *) NULL)
8434      ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8435
8436   /* Define these outside of the following "if logging()" block so they will
8437    * show in debuggers.
8438    */
8439   *im_vers='\0';
8440   (void) ConcatenateMagickString(im_vers,
8441          MagickLibVersionText,MagickPathExtent);
8442   (void) ConcatenateMagickString(im_vers,
8443          MagickLibAddendum,MagickPathExtent);
8444
8445   *libpng_vers='\0';
8446   (void) ConcatenateMagickString(libpng_vers,
8447          PNG_LIBPNG_VER_STRING,32);
8448   *libpng_runv='\0';
8449   (void) ConcatenateMagickString(libpng_runv,
8450          png_get_libpng_ver(NULL),32);
8451
8452   *zlib_vers='\0';
8453   (void) ConcatenateMagickString(zlib_vers,
8454          ZLIB_VERSION,32);
8455   *zlib_runv='\0';
8456   (void) ConcatenateMagickString(zlib_runv,
8457          zlib_version,32);
8458
8459   if (logging != MagickFalse)
8460     {
8461        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"    IM version     = %s",
8462            im_vers);
8463        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"    Libpng version = %s",
8464            libpng_vers);
8465        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8466        {
8467        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8468            libpng_runv);
8469        }
8470        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
8471            zlib_vers);
8472        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8473        {
8474        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8475            zlib_runv);
8476        }
8477     }
8478
8479   /* Initialize some stuff */
8480   ping_bit_depth=0,
8481   ping_color_type=0,
8482   ping_interlace_method=0,
8483   ping_compression_method=0,
8484   ping_filter_method=0,
8485   ping_num_trans = 0;
8486
8487   ping_background.red = 0;
8488   ping_background.green = 0;
8489   ping_background.blue = 0;
8490   ping_background.gray = 0;
8491   ping_background.index = 0;
8492
8493   ping_trans_color.red=0;
8494   ping_trans_color.green=0;
8495   ping_trans_color.blue=0;
8496   ping_trans_color.gray=0;
8497
8498   ping_pHYs_unit_type = 0;
8499   ping_pHYs_x_resolution = 0;
8500   ping_pHYs_y_resolution = 0;
8501
8502   ping_have_blob=MagickFalse;
8503   ping_have_cheap_transparency=MagickFalse;
8504   ping_have_color=MagickTrue;
8505   ping_have_non_bw=MagickTrue;
8506   ping_have_PLTE=MagickFalse;
8507   ping_have_bKGD=MagickFalse;
8508   ping_have_eXIf=MagickTrue;
8509   ping_have_iCCP=MagickFalse;
8510   ping_have_pHYs=MagickFalse;
8511   ping_have_sRGB=MagickFalse;
8512   ping_have_tRNS=MagickFalse;
8513
8514   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8515   ping_exclude_caNv=mng_info->ping_exclude_caNv;
8516   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8517   ping_exclude_date=mng_info->ping_exclude_date;
8518   /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8519   ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
8520   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8521   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8522   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8523   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8524   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8525   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8526   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8527   ping_exclude_tIME=mng_info->ping_exclude_tIME;
8528   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8529   ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8530   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8531   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8532
8533   ping_preserve_colormap = mng_info->ping_preserve_colormap;
8534   ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8535   ping_need_colortype_warning = MagickFalse;
8536
8537   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8538    * i.e., eliminate the ICC profile and set image->rendering_intent.
8539    * Note that this will not involve any changes to the actual pixels
8540    * but merely passes information to applications that read the resulting
8541    * PNG image.
8542    *
8543    * To do: recognize other variants of the sRGB profile, using the CRC to
8544    * verify all recognized variants including the 7 already known.
8545    *
8546    * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8547    *
8548    * Use something other than image->rendering_intent to record the fact
8549    * that the sRGB profile was found.
8550    *
8551    * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8552    * profile.  Record the Blackpoint Compensation, if any.
8553    */
8554    if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8555    {
8556       char
8557         *name;
8558
8559       const StringInfo
8560         *profile;
8561
8562       ResetImageProfileIterator(image);
8563       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8564       {
8565         profile=GetImageProfile(image,name);
8566
8567         if (profile != (StringInfo *) NULL)
8568           {
8569             if ((LocaleCompare(name,"ICC") == 0) ||
8570                 (LocaleCompare(name,"ICM") == 0))
8571
8572              {
8573                  int
8574                    icheck,
8575                    got_crc=0;
8576
8577
8578                  png_uint_32
8579                    length,
8580                    profile_crc=0;
8581
8582                  unsigned char
8583                    *data;
8584
8585                  length=(png_uint_32) GetStringInfoLength(profile);
8586
8587                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8588                  {
8589                    if (length == sRGB_info[icheck].len)
8590                    {
8591                      if (got_crc == 0)
8592                      {
8593                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8594                          "    Got a %lu-byte ICC profile (potentially sRGB)",
8595                          (unsigned long) length);
8596
8597                        data=GetStringInfoDatum(profile);
8598                        profile_crc=crc32(0,data,length);
8599
8600                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8601                            "      with crc=%8x",(unsigned int) profile_crc);
8602                        got_crc++;
8603                      }
8604
8605                      if (profile_crc == sRGB_info[icheck].crc)
8606                      {
8607                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8608                             "      It is sRGB with rendering intent = %s",
8609                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
8610                              sRGB_info[icheck].intent));
8611                         if (image->rendering_intent==UndefinedIntent)
8612                         {
8613                           image->rendering_intent=
8614                           Magick_RenderingIntent_from_PNG_RenderingIntent(
8615                              sRGB_info[icheck].intent);
8616                         }
8617                         ping_exclude_iCCP = MagickTrue;
8618                         ping_exclude_zCCP = MagickTrue;
8619                         ping_have_sRGB = MagickTrue;
8620                         break;
8621                      }
8622                    }
8623                  }
8624                  if (sRGB_info[icheck].len == 0)
8625                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8626                         "    Got %lu-byte ICC profile not recognized as sRGB",
8627                         (unsigned long) length);
8628               }
8629           }
8630         name=GetNextImageProfile(image);
8631       }
8632   }
8633
8634   number_opaque = 0;
8635   number_semitransparent = 0;
8636   number_transparent = 0;
8637
8638   if (logging != MagickFalse)
8639     {
8640       if (image->storage_class == UndefinedClass)
8641           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8642           "    image->storage_class=UndefinedClass");
8643       if (image->storage_class == DirectClass)
8644           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8645           "    image->storage_class=DirectClass");
8646       if (image->storage_class == PseudoClass)
8647           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8648           "    image->storage_class=PseudoClass");
8649       (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8650           "    image->taint=MagickTrue":
8651           "    image->taint=MagickFalse");
8652     }
8653
8654   if (image->storage_class == PseudoClass &&
8655      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8656      mng_info->write_png48 || mng_info->write_png64 ||
8657      (mng_info->write_png_colortype != 1 &&
8658      mng_info->write_png_colortype != 5)))
8659     {
8660       (void) SyncImage(image,exception);
8661       image->storage_class = DirectClass;
8662     }
8663
8664   if (ping_preserve_colormap == MagickFalse)
8665     {
8666       if (image->storage_class != PseudoClass && image->colormap != NULL)
8667         {
8668           /* Free the bogus colormap; it can cause trouble later */
8669            if (logging != MagickFalse)
8670               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8671               "    Freeing bogus colormap");
8672            (void) RelinquishMagickMemory(image->colormap);
8673            image->colormap=NULL;
8674         }
8675     }
8676
8677   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8678     (void) TransformImageColorspace(image,sRGBColorspace,exception);
8679
8680   /*
8681     Sometimes we get PseudoClass images whose RGB values don't match
8682     the colors in the colormap.  This code syncs the RGB values.
8683   */
8684   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8685      (void) SyncImage(image,exception);
8686
8687 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8688   if (image->depth > 8)
8689     {
8690       if (logging != MagickFalse)
8691         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8692           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8693
8694       image->depth=8;
8695     }
8696 #endif
8697
8698   /* Respect the -depth option */
8699   if (image->depth < 4)
8700     {
8701        register Quantum
8702          *r;
8703
8704        if (image->depth > 2)
8705          {
8706            /* Scale to 4-bit */
8707            LBR04PacketRGBO(image->background_color);
8708
8709            for (y=0; y < (ssize_t) image->rows; y++)
8710            {
8711              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8712
8713              if (r == (Quantum *) NULL)
8714                break;
8715
8716              for (x=0; x < (ssize_t) image->columns; x++)
8717              {
8718                 LBR04PixelRGBA(r);
8719                 r+=GetPixelChannels(image);
8720              }
8721
8722              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8723                 break;
8724            }
8725
8726            if (image->storage_class == PseudoClass && image->colormap != NULL)
8727            {
8728              for (i=0; i < (ssize_t) image->colors; i++)
8729              {
8730                LBR04PacketRGBO(image->colormap[i]);
8731              }
8732            }
8733          }
8734        else if (image->depth > 1)
8735          {
8736            /* Scale to 2-bit */
8737            LBR02PacketRGBO(image->background_color);
8738
8739            for (y=0; y < (ssize_t) image->rows; y++)
8740            {
8741              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8742
8743              if (r == (Quantum *) NULL)
8744                break;
8745
8746              for (x=0; x < (ssize_t) image->columns; x++)
8747              {
8748                 LBR02PixelRGBA(r);
8749                 r+=GetPixelChannels(image);
8750              }
8751
8752              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8753                 break;
8754            }
8755
8756            if (image->storage_class == PseudoClass && image->colormap != NULL)
8757            {
8758              for (i=0; i < (ssize_t) image->colors; i++)
8759              {
8760                LBR02PacketRGBO(image->colormap[i]);
8761              }
8762            }
8763          }
8764        else
8765          {
8766            /* Scale to 1-bit */
8767            LBR01PacketRGBO(image->background_color);
8768
8769            for (y=0; y < (ssize_t) image->rows; y++)
8770            {
8771              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8772
8773              if (r == (Quantum *) NULL)
8774                break;
8775
8776              for (x=0; x < (ssize_t) image->columns; x++)
8777              {
8778                 LBR01PixelRGBA(r);
8779                 r+=GetPixelChannels(image);
8780              }
8781
8782              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8783                 break;
8784            }
8785
8786            if (image->storage_class == PseudoClass && image->colormap != NULL)
8787            {
8788              for (i=0; i < (ssize_t) image->colors; i++)
8789              {
8790                LBR01PacketRGBO(image->colormap[i]);
8791              }
8792            }
8793          }
8794     }
8795
8796   /* To do: set to next higher multiple of 8 */
8797   if (image->depth < 8)
8798      image->depth=8;
8799
8800 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8801   /* PNG does not handle depths greater than 16 so reduce it even
8802    * if lossy
8803    */
8804   if (image->depth > 8)
8805       image->depth=16;
8806 #endif
8807
8808 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8809   if (image->depth > 8)
8810     {
8811       /* To do: fill low byte properly */
8812       image->depth=16;
8813     }
8814
8815   if (image->depth == 16 && mng_info->write_png_depth != 16)
8816     if (mng_info->write_png8 ||
8817         LosslessReduceDepthOK(image,exception) != MagickFalse)
8818       image->depth = 8;
8819 #endif
8820
8821   image_colors = (int) image->colors;
8822   number_opaque = (int) image->colors;
8823   number_transparent = 0;
8824   number_semitransparent = 0;
8825
8826   if (mng_info->write_png_colortype &&
8827      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8828      mng_info->write_png_colortype < 4 &&
8829      image->alpha_trait == UndefinedPixelTrait)))
8830   {
8831      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8832       * are not going to need the result.
8833       */
8834      if (mng_info->write_png_colortype == 1 ||
8835         mng_info->write_png_colortype == 5)
8836        ping_have_color=MagickFalse;
8837
8838      if (image->alpha_trait != UndefinedPixelTrait)
8839        {
8840          number_transparent = 2;
8841          number_semitransparent = 1;
8842        }
8843   }
8844
8845   if (mng_info->write_png_colortype < 7)
8846   {
8847   /* BUILD_PALETTE
8848    *
8849    * Normally we run this just once, but in the case of writing PNG8
8850    * we reduce the transparency to binary and run again, then if there
8851    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8852    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8853    * palette.  Then (To do) we take care of a final reduction that is only
8854    * needed if there are still 256 colors present and one of them has both
8855    * transparent and opaque instances.
8856    */
8857
8858   tried_332 = MagickFalse;
8859   tried_333 = MagickFalse;
8860   tried_444 = MagickFalse;
8861
8862   for (j=0; j<6; j++)
8863   {
8864     /*
8865      * Sometimes we get DirectClass images that have 256 colors or fewer.
8866      * This code will build a colormap.
8867      *
8868      * Also, sometimes we get PseudoClass images with an out-of-date
8869      * colormap.  This code will replace the colormap with a new one.
8870      * Sometimes we get PseudoClass images that have more than 256 colors.
8871      * This code will delete the colormap and change the image to
8872      * DirectClass.
8873      *
8874      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8875      * even though it sometimes contains left-over non-opaque values.
8876      *
8877      * Also we gather some information (number of opaque, transparent,
8878      * and semitransparent pixels, and whether the image has any non-gray
8879      * pixels or only black-and-white pixels) that we might need later.
8880      *
8881      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8882      * we need to check for bogus non-opaque values, at least.
8883      */
8884
8885    int
8886      n;
8887
8888    PixelInfo
8889      opaque[260],
8890      semitransparent[260],
8891      transparent[260];
8892
8893    register const Quantum
8894      *s;
8895
8896    register Quantum
8897      *q,
8898      *r;
8899
8900    if (logging != MagickFalse)
8901      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8902          "    Enter BUILD_PALETTE:");
8903
8904    if (logging != MagickFalse)
8905      {
8906        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8907              "      image->columns=%.20g",(double) image->columns);
8908        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8909              "      image->rows=%.20g",(double) image->rows);
8910        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8911              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
8912        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8913              "      image->depth=%.20g",(double) image->depth);
8914
8915        if (image->storage_class == PseudoClass && image->colormap != NULL)
8916        {
8917          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8918              "      Original colormap:");
8919          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8920              "        i    (red,green,blue,alpha)");
8921
8922          for (i=0; i < 256; i++)
8923          {
8924                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8925                    "        %d    (%d,%d,%d,%d)",
8926                     (int) i,
8927                     (int) image->colormap[i].red,
8928                     (int) image->colormap[i].green,
8929                     (int) image->colormap[i].blue,
8930                     (int) image->colormap[i].alpha);
8931          }
8932
8933          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8934          {
8935            if (i > 255)
8936              {
8937                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8938                    "        %d    (%d,%d,%d,%d)",
8939                     (int) i,
8940                     (int) image->colormap[i].red,
8941                     (int) image->colormap[i].green,
8942                     (int) image->colormap[i].blue,
8943                     (int) image->colormap[i].alpha);
8944              }
8945          }
8946        }
8947
8948        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8949            "      image->colors=%d",(int) image->colors);
8950
8951        if (image->colors == 0)
8952          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8953              "        (zero means unknown)");
8954
8955        if (ping_preserve_colormap == MagickFalse)
8956          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8957               "      Regenerate the colormap");
8958      }
8959
8960      image_colors=0;
8961      number_opaque = 0;
8962      number_semitransparent = 0;
8963      number_transparent = 0;
8964
8965      for (y=0; y < (ssize_t) image->rows; y++)
8966      {
8967        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8968
8969        if (q == (Quantum *) NULL)
8970          break;
8971
8972        for (x=0; x < (ssize_t) image->columns; x++)
8973        {
8974            if (image->alpha_trait == UndefinedPixelTrait ||
8975               GetPixelAlpha(image,q) == OpaqueAlpha)
8976              {
8977                if (number_opaque < 259)
8978                  {
8979                    if (number_opaque == 0)
8980                      {
8981                        GetPixelInfoPixel(image, q, opaque);
8982                        opaque[0].alpha=OpaqueAlpha;
8983                        number_opaque=1;
8984                      }
8985
8986                    for (i=0; i< (ssize_t) number_opaque; i++)
8987                      {
8988                        if (Magick_png_color_equal(image,q,opaque+i))
8989                          break;
8990                      }
8991
8992                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
8993                      {
8994                        number_opaque++;
8995                        GetPixelInfoPixel(image, q, opaque+i);
8996                        opaque[i].alpha=OpaqueAlpha;
8997                      }
8998                  }
8999              }
9000            else if (GetPixelAlpha(image,q) == TransparentAlpha)
9001              {
9002                if (number_transparent < 259)
9003                  {
9004                    if (number_transparent == 0)
9005                      {
9006                        GetPixelInfoPixel(image, q, transparent);
9007                        ping_trans_color.red=(unsigned short)
9008                          GetPixelRed(image,q);
9009                        ping_trans_color.green=(unsigned short)
9010                          GetPixelGreen(image,q);
9011                        ping_trans_color.blue=(unsigned short)
9012                          GetPixelBlue(image,q);
9013                        ping_trans_color.gray=(unsigned short)
9014                          GetPixelGray(image,q);
9015                        number_transparent = 1;
9016                      }
9017
9018                    for (i=0; i< (ssize_t) number_transparent; i++)
9019                      {
9020                        if (Magick_png_color_equal(image,q,transparent+i))
9021                          break;
9022                      }
9023
9024                    if (i ==  (ssize_t) number_transparent &&
9025                        number_transparent < 259)
9026                      {
9027                        number_transparent++;
9028                        GetPixelInfoPixel(image,q,transparent+i);
9029                      }
9030                  }
9031              }
9032            else
9033              {
9034                if (number_semitransparent < 259)
9035                  {
9036                    if (number_semitransparent == 0)
9037                      {
9038                        GetPixelInfoPixel(image,q,semitransparent);
9039                        number_semitransparent = 1;
9040                      }
9041
9042                    for (i=0; i< (ssize_t) number_semitransparent; i++)
9043                      {
9044                        if (Magick_png_color_equal(image,q,semitransparent+i)
9045                            && GetPixelAlpha(image,q) ==
9046                            semitransparent[i].alpha)
9047                          break;
9048                      }
9049
9050                    if (i ==  (ssize_t) number_semitransparent &&
9051                        number_semitransparent < 259)
9052                      {
9053                        number_semitransparent++;
9054                        GetPixelInfoPixel(image, q, semitransparent+i);
9055                      }
9056                  }
9057              }
9058            q+=GetPixelChannels(image);
9059         }
9060      }
9061
9062      if (mng_info->write_png8 == MagickFalse &&
9063          ping_exclude_bKGD == MagickFalse)
9064        {
9065          /* Add the background color to the palette, if it
9066           * isn't already there.
9067           */
9068           if (logging != MagickFalse)
9069             {
9070               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9071                   "      Check colormap for background (%d,%d,%d)",
9072                   (int) image->background_color.red,
9073                   (int) image->background_color.green,
9074                   (int) image->background_color.blue);
9075             }
9076           for (i=0; i<number_opaque; i++)
9077           {
9078              if (opaque[i].red == image->background_color.red &&
9079                  opaque[i].green == image->background_color.green &&
9080                  opaque[i].blue == image->background_color.blue)
9081                break;
9082           }
9083           if (number_opaque < 259 && i == number_opaque)
9084             {
9085                opaque[i] = image->background_color;
9086                ping_background.index = i;
9087                number_opaque++;
9088                if (logging != MagickFalse)
9089                  {
9090                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9091                        "      background_color index is %d",(int) i);
9092                  }
9093
9094             }
9095           else if (logging != MagickFalse)
9096               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9097                   "      No room in the colormap to add background color");
9098        }
9099
9100      image_colors=number_opaque+number_transparent+number_semitransparent;
9101
9102      if (logging != MagickFalse)
9103        {
9104          if (image_colors > 256)
9105             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9106                   "      image has more than 256 colors");
9107
9108          else
9109             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9110                   "      image has %d colors",image_colors);
9111        }
9112
9113      if (ping_preserve_colormap != MagickFalse)
9114        break;
9115
9116      if (mng_info->write_png_colortype != 7) /* We won't need this info */
9117        {
9118          ping_have_color=MagickFalse;
9119          ping_have_non_bw=MagickFalse;
9120
9121          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
9122          {
9123            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9124               "incompatible colorspace");
9125            ping_have_color=MagickTrue;
9126            ping_have_non_bw=MagickTrue;
9127          }
9128
9129          if(image_colors > 256)
9130            {
9131              for (y=0; y < (ssize_t) image->rows; y++)
9132              {
9133                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9134
9135                if (q == (Quantum *) NULL)
9136                  break;
9137
9138                s=q;
9139                for (x=0; x < (ssize_t) image->columns; x++)
9140                {
9141                  if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
9142                      GetPixelRed(image,s) != GetPixelBlue(image,s))
9143                    {
9144                       ping_have_color=MagickTrue;
9145                       ping_have_non_bw=MagickTrue;
9146                       break;
9147                    }
9148                  s+=GetPixelChannels(image);
9149                }
9150
9151                if (ping_have_color != MagickFalse)
9152                  break;
9153
9154                /* Worst case is black-and-white; we are looking at every
9155                 * pixel twice.
9156                 */
9157
9158                if (ping_have_non_bw == MagickFalse)
9159                  {
9160                    s=q;
9161                    for (x=0; x < (ssize_t) image->columns; x++)
9162                    {
9163                      if (GetPixelRed(image,s) != 0 &&
9164                          GetPixelRed(image,s) != QuantumRange)
9165                        {
9166                          ping_have_non_bw=MagickTrue;
9167                          break;
9168                        }
9169                      s+=GetPixelChannels(image);
9170                    }
9171                }
9172              }
9173            }
9174        }
9175
9176      if (image_colors < 257)
9177        {
9178          PixelInfo
9179            colormap[260];
9180
9181          /*
9182           * Initialize image colormap.
9183           */
9184
9185          if (logging != MagickFalse)
9186             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9187                   "      Sort the new colormap");
9188
9189         /* Sort palette, transparent first */;
9190
9191          n = 0;
9192
9193          for (i=0; i<number_transparent; i++)
9194             colormap[n++] = transparent[i];
9195
9196          for (i=0; i<number_semitransparent; i++)
9197             colormap[n++] = semitransparent[i];
9198
9199          for (i=0; i<number_opaque; i++)
9200             colormap[n++] = opaque[i];
9201
9202          ping_background.index +=
9203            (number_transparent + number_semitransparent);
9204
9205          /* image_colors < 257; search the colormap instead of the pixels
9206           * to get ping_have_color and ping_have_non_bw
9207           */
9208          for (i=0; i<n; i++)
9209          {
9210            if (ping_have_color == MagickFalse)
9211              {
9212                 if (colormap[i].red != colormap[i].green ||
9213                     colormap[i].red != colormap[i].blue)
9214                   {
9215                      ping_have_color=MagickTrue;
9216                      ping_have_non_bw=MagickTrue;
9217                      break;
9218                   }
9219               }
9220
9221            if (ping_have_non_bw == MagickFalse)
9222              {
9223                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
9224                    ping_have_non_bw=MagickTrue;
9225              }
9226           }
9227
9228         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
9229             (number_transparent == 0 && number_semitransparent == 0)) &&
9230             (((mng_info->write_png_colortype-1) ==
9231             PNG_COLOR_TYPE_PALETTE) ||
9232             (mng_info->write_png_colortype == 0)))
9233           {
9234             if (logging != MagickFalse)
9235               {
9236                 if (n !=  (ssize_t) image_colors)
9237                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9238                    "   image_colors (%d) and n (%d)  don't match",
9239                    image_colors, n);
9240
9241                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9242                    "      AcquireImageColormap");
9243               }
9244
9245             image->colors = image_colors;
9246
9247             if (AcquireImageColormap(image,image_colors,exception) ==
9248                 MagickFalse)
9249                ThrowWriterException(ResourceLimitError,
9250                    "MemoryAllocationFailed");
9251
9252             for (i=0; i< (ssize_t) image_colors; i++)
9253                image->colormap[i] = colormap[i];
9254
9255             if (logging != MagickFalse)
9256               {
9257                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9258                       "      image->colors=%d (%d)",
9259                       (int) image->colors, image_colors);
9260
9261                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9262                       "      Update the pixel indexes");
9263               }
9264
9265             /* Sync the pixel indices with the new colormap */
9266
9267             for (y=0; y < (ssize_t) image->rows; y++)
9268             {
9269               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9270
9271               if (q == (Quantum *) NULL)
9272                 break;
9273
9274               for (x=0; x < (ssize_t) image->columns; x++)
9275               {
9276                 for (i=0; i< (ssize_t) image_colors; i++)
9277                 {
9278                   if ((image->alpha_trait == UndefinedPixelTrait ||
9279                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
9280                       image->colormap[i].red == GetPixelRed(image,q) &&
9281                       image->colormap[i].green == GetPixelGreen(image,q) &&
9282                       image->colormap[i].blue == GetPixelBlue(image,q))
9283                   {
9284                     SetPixelIndex(image,i,q);
9285                     break;
9286                   }
9287                 }
9288                 q+=GetPixelChannels(image);
9289               }
9290
9291               if (SyncAuthenticPixels(image,exception) == MagickFalse)
9292                  break;
9293             }
9294           }
9295        }
9296
9297      if (logging != MagickFalse)
9298        {
9299          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9300             "      image->colors=%d", (int) image->colors);
9301
9302          if (image->colormap != NULL)
9303            {
9304              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9305                  "       i     (red,green,blue,alpha)");
9306
9307              for (i=0; i < (ssize_t) image->colors; i++)
9308              {
9309                if (i < 300 || i >= (ssize_t) image->colors - 10)
9310                  {
9311                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9312                        "       %d     (%d,%d,%d,%d)",
9313                         (int) i,
9314                         (int) image->colormap[i].red,
9315                         (int) image->colormap[i].green,
9316                         (int) image->colormap[i].blue,
9317                         (int) image->colormap[i].alpha);
9318                  }
9319              }
9320            }
9321
9322            if (number_transparent < 257)
9323              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9324                    "      number_transparent     = %d",
9325                    number_transparent);
9326            else
9327
9328              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9329                    "      number_transparent     > 256");
9330
9331            if (number_opaque < 257)
9332              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9333                    "      number_opaque          = %d",
9334                    number_opaque);
9335
9336            else
9337              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9338                    "      number_opaque          > 256");
9339
9340            if (number_semitransparent < 257)
9341              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9342                    "      number_semitransparent = %d",
9343                    number_semitransparent);
9344
9345            else
9346              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9347                    "      number_semitransparent > 256");
9348
9349            if (ping_have_non_bw == MagickFalse)
9350               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9351                     "      All pixels and the background are black or white");
9352
9353            else if (ping_have_color == MagickFalse)
9354               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9355                     "      All pixels and the background are gray");
9356
9357            else
9358               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9359                     "      At least one pixel or the background is non-gray");
9360
9361            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9362                "    Exit BUILD_PALETTE:");
9363        }
9364
9365    if (mng_info->write_png8 == MagickFalse)
9366       break;
9367
9368    /* Make any reductions necessary for the PNG8 format */
9369     if (image_colors <= 256 &&
9370         image_colors != 0 && image->colormap != NULL &&
9371         number_semitransparent == 0 &&
9372         number_transparent <= 1)
9373       break;
9374
9375     /* PNG8 can't have semitransparent colors so we threshold the
9376      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9377      * transparent color so if more than one is transparent we merge
9378      * them into image->background_color.
9379      */
9380     if (number_semitransparent != 0 || number_transparent > 1)
9381       {
9382         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9383             "    Thresholding the alpha channel to binary");
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/2)
9395                 {
9396                   SetPixelViaPixelInfo(image,&image->background_color,r);
9397                   SetPixelAlpha(image,TransparentAlpha,r);
9398                 }
9399               else
9400                   SetPixelAlpha(image,OpaqueAlpha,r);
9401               r+=GetPixelChannels(image);
9402           }
9403
9404           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9405              break;
9406
9407           if (image_colors != 0 && image_colors <= 256 &&
9408              image->colormap != NULL)
9409             for (i=0; i<image_colors; i++)
9410                 image->colormap[i].alpha =
9411                     (image->colormap[i].alpha > TransparentAlpha/2 ?
9412                     TransparentAlpha : OpaqueAlpha);
9413         }
9414       continue;
9415     }
9416
9417     /* PNG8 can't have more than 256 colors so we quantize the pixels and
9418      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
9419      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9420      * colors or less.
9421      */
9422     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9423       {
9424         if (logging != MagickFalse)
9425            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9426                "    Quantizing the background color to 4-4-4");
9427
9428         tried_444 = MagickTrue;
9429
9430         LBR04PacketRGB(image->background_color);
9431
9432         if (logging != MagickFalse)
9433           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9434               "    Quantizing the pixel colors to 4-4-4");
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                   LBR04PixelRGB(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 4-4-4");
9463
9464           for (i=0; i<image_colors; i++)
9465           {
9466             LBR04PacketRGB(image->colormap[i]);
9467           }
9468         }
9469         continue;
9470       }
9471
9472     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9473       {
9474         if (logging != MagickFalse)
9475            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9476                "    Quantizing the background color to 3-3-3");
9477
9478         tried_333 = MagickTrue;
9479
9480         LBR03PacketRGB(image->background_color);
9481
9482         if (logging != MagickFalse)
9483           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9484               "    Quantizing the pixel colors to 3-3-3-1");
9485
9486         if (image->colormap == NULL)
9487         {
9488           for (y=0; y < (ssize_t) image->rows; y++)
9489           {
9490             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9491
9492             if (r == (Quantum *) NULL)
9493               break;
9494
9495             for (x=0; x < (ssize_t) image->columns; x++)
9496             {
9497               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9498                   LBR03RGB(r);
9499               r+=GetPixelChannels(image);
9500             }
9501
9502             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9503                break;
9504           }
9505         }
9506
9507         else /* Should not reach this; colormap already exists and
9508                 must be <= 256 */
9509         {
9510           if (logging != MagickFalse)
9511               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9512               "    Quantizing the colormap to 3-3-3-1");
9513           for (i=0; i<image_colors; i++)
9514           {
9515               LBR03PacketRGB(image->colormap[i]);
9516           }
9517         }
9518         continue;
9519       }
9520
9521     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9522       {
9523         if (logging != MagickFalse)
9524            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9525                "    Quantizing the background color to 3-3-2");
9526
9527         tried_332 = MagickTrue;
9528
9529         /* Red and green were already done so we only quantize the blue
9530          * channel
9531          */
9532
9533         LBR02PacketBlue(image->background_color);
9534
9535         if (logging != MagickFalse)
9536           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9537               "    Quantizing the pixel colors to 3-3-2-1");
9538
9539         if (image->colormap == NULL)
9540         {
9541           for (y=0; y < (ssize_t) image->rows; y++)
9542           {
9543             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9544
9545             if (r == (Quantum *) NULL)
9546               break;
9547
9548             for (x=0; x < (ssize_t) image->columns; x++)
9549             {
9550               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9551                   LBR02PixelBlue(r);
9552               r+=GetPixelChannels(image);
9553             }
9554
9555             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9556                break;
9557           }
9558         }
9559
9560         else /* Should not reach this; colormap already exists and
9561                 must be <= 256 */
9562         {
9563           if (logging != MagickFalse)
9564               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9565               "    Quantizing the colormap to 3-3-2-1");
9566           for (i=0; i<image_colors; i++)
9567           {
9568               LBR02PacketBlue(image->colormap[i]);
9569           }
9570       }
9571       continue;
9572     }
9573
9574     if (image_colors == 0 || image_colors > 256)
9575     {
9576       /* Take care of special case with 256 opaque colors + 1 transparent
9577        * color.  We don't need to quantize to 2-3-2-1; we only need to
9578        * eliminate one color, so we'll merge the two darkest red
9579        * colors (0x49, 0, 0) -> (0x24, 0, 0).
9580        */
9581       if (logging != MagickFalse)
9582         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9583             "    Merging two dark red background colors to 3-3-2-1");
9584
9585       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9586           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9587           ScaleQuantumToChar(image->background_color.blue) == 0x00)
9588       {
9589          image->background_color.red=ScaleCharToQuantum(0x24);
9590       }
9591
9592       if (logging != MagickFalse)
9593         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9594             "    Merging two dark red pixel colors to 3-3-2-1");
9595
9596       if (image->colormap == NULL)
9597       {
9598         for (y=0; y < (ssize_t) image->rows; y++)
9599         {
9600           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9601
9602           if (r == (Quantum *) NULL)
9603             break;
9604
9605           for (x=0; x < (ssize_t) image->columns; x++)
9606           {
9607             if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9608                 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9609                 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9610                 GetPixelAlpha(image,r) == OpaqueAlpha)
9611               {
9612                 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9613               }
9614             r+=GetPixelChannels(image);
9615           }
9616
9617           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9618              break;
9619
9620         }
9621       }
9622
9623       else
9624       {
9625          for (i=0; i<image_colors; i++)
9626          {
9627             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9628                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9629                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9630             {
9631                image->colormap[i].red=ScaleCharToQuantum(0x24);
9632             }
9633          }
9634       }
9635     }
9636   }
9637   }
9638   /* END OF BUILD_PALETTE */
9639
9640   /* If we are excluding the tRNS chunk and there is transparency,
9641    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9642    * PNG.
9643    */
9644   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9645      (number_transparent != 0 || number_semitransparent != 0))
9646     {
9647       unsigned int colortype=mng_info->write_png_colortype;
9648
9649       if (ping_have_color == MagickFalse)
9650         mng_info->write_png_colortype = 5;
9651
9652       else
9653         mng_info->write_png_colortype = 7;
9654
9655       if (colortype != 0 &&
9656          mng_info->write_png_colortype != colortype)
9657         ping_need_colortype_warning=MagickTrue;
9658
9659     }
9660
9661   /* See if cheap transparency is possible.  It is only possible
9662    * when there is a single transparent color, no semitransparent
9663    * color, and no opaque color that has the same RGB components
9664    * as the transparent color.  We only need this information if
9665    * we are writing a PNG with colortype 0 or 2, and we have not
9666    * excluded the tRNS chunk.
9667    */
9668   if (number_transparent == 1 &&
9669       mng_info->write_png_colortype < 4)
9670     {
9671        ping_have_cheap_transparency = MagickTrue;
9672
9673        if (number_semitransparent != 0)
9674          ping_have_cheap_transparency = MagickFalse;
9675
9676        else if (image_colors == 0 || image_colors > 256 ||
9677            image->colormap == NULL)
9678          {
9679            register const Quantum
9680              *q;
9681
9682            for (y=0; y < (ssize_t) image->rows; y++)
9683            {
9684              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9685
9686              if (q == (Quantum *) NULL)
9687                break;
9688
9689              for (x=0; x < (ssize_t) image->columns; x++)
9690              {
9691                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9692                      (unsigned short) GetPixelRed(image,q) ==
9693                                      ping_trans_color.red &&
9694                      (unsigned short) GetPixelGreen(image,q) ==
9695                                      ping_trans_color.green &&
9696                      (unsigned short) GetPixelBlue(image,q) ==
9697                                      ping_trans_color.blue)
9698                    {
9699                      ping_have_cheap_transparency = MagickFalse;
9700                      break;
9701                    }
9702
9703                  q+=GetPixelChannels(image);
9704              }
9705
9706              if (ping_have_cheap_transparency == MagickFalse)
9707                 break;
9708            }
9709          }
9710        else
9711          {
9712             /* Assuming that image->colormap[0] is the one transparent color
9713              * and that all others are opaque.
9714              */
9715             if (image_colors > 1)
9716               for (i=1; i<image_colors; i++)
9717                 if (image->colormap[i].red == image->colormap[0].red &&
9718                     image->colormap[i].green == image->colormap[0].green &&
9719                     image->colormap[i].blue == image->colormap[0].blue)
9720                   {
9721                      ping_have_cheap_transparency = MagickFalse;
9722                      break;
9723                   }
9724          }
9725
9726        if (logging != MagickFalse)
9727          {
9728            if (ping_have_cheap_transparency == MagickFalse)
9729              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9730                  "   Cheap transparency is not possible.");
9731
9732            else
9733              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9734                  "   Cheap transparency is possible.");
9735          }
9736      }
9737   else
9738     ping_have_cheap_transparency = MagickFalse;
9739
9740   image_depth=image->depth;
9741
9742   quantum_info = (QuantumInfo *) NULL;
9743   number_colors=0;
9744   image_colors=(int) image->colors;
9745   image_matte=image->alpha_trait !=
9746         UndefinedPixelTrait ? MagickTrue : MagickFalse;
9747
9748   if (mng_info->write_png_colortype < 5)
9749     mng_info->IsPalette=image->storage_class == PseudoClass &&
9750       image_colors <= 256 && image->colormap != NULL;
9751   else
9752     mng_info->IsPalette = MagickFalse;
9753
9754   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9755      (image->colors == 0 || image->colormap == NULL))
9756     {
9757       image_info=DestroyImageInfo(image_info);
9758       image=DestroyImage(image);
9759       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9760           "Cannot write PNG8 or color-type 3; colormap is NULL",
9761           "`%s'",IMimage->filename);
9762       return(MagickFalse);
9763     }
9764
9765   /*
9766     Allocate the PNG structures
9767   */
9768 #ifdef PNG_USER_MEM_SUPPORTED
9769  error_info.image=image;
9770  error_info.exception=exception;
9771   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9772     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9773     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9774
9775 #else
9776   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9777     MagickPNGErrorHandler,MagickPNGWarningHandler);
9778
9779 #endif
9780   if (ping == (png_struct *) NULL)
9781     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9782
9783   ping_info=png_create_info_struct(ping);
9784
9785   if (ping_info == (png_info *) NULL)
9786     {
9787       png_destroy_write_struct(&ping,(png_info **) NULL);
9788       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9789     }
9790
9791   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9792   pixel_info=(MemoryInfo *) NULL;
9793
9794   if (setjmp(png_jmpbuf(ping)))
9795     {
9796       /*
9797         PNG write failed.
9798       */
9799 #ifdef PNG_DEBUG
9800      if (image_info->verbose)
9801         (void) printf("PNG write has failed.\n");
9802 #endif
9803       png_destroy_write_struct(&ping,&ping_info);
9804 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9805       UnlockSemaphoreInfo(ping_semaphore);
9806 #endif
9807
9808       if (pixel_info != (MemoryInfo *) NULL)
9809         pixel_info=RelinquishVirtualMemory(pixel_info);
9810
9811       if (quantum_info != (QuantumInfo *) NULL)
9812         quantum_info=DestroyQuantumInfo(quantum_info);
9813
9814       if (ping_have_blob != MagickFalse)
9815           (void) CloseBlob(image);
9816       image_info=DestroyImageInfo(image_info);
9817       image=DestroyImage(image);
9818       return(MagickFalse);
9819     }
9820
9821   /* {  For navigation to end of SETJMP-protected block.  Within this
9822    *    block, use png_error() instead of Throwing an Exception, to ensure
9823    *    that libpng is able to clean up, and that the semaphore is unlocked.
9824    */
9825
9826 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9827   LockSemaphoreInfo(ping_semaphore);
9828 #endif
9829
9830 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9831   /* Allow benign errors */
9832   png_set_benign_errors(ping, 1);
9833 #endif
9834
9835 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9836   /* Reject images with too many rows or columns */
9837   png_set_user_limits(ping,
9838     (png_uint_32) MagickMin(0x7fffffffL,
9839         GetMagickResourceLimit(WidthResource)),
9840     (png_uint_32) MagickMin(0x7fffffffL,
9841         GetMagickResourceLimit(HeightResource)));
9842 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9843
9844   /*
9845     Prepare PNG for writing.
9846   */
9847
9848 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9849   if (mng_info->write_mng)
9850   {
9851      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9852 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9853      /* Disable new libpng-1.5.10 feature when writing a MNG because
9854       * zero-length PLTE is OK
9855       */
9856      png_set_check_for_invalid_index (ping, 0);
9857 # endif
9858   }
9859
9860 #else
9861 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9862   if (mng_info->write_mng)
9863      png_permit_empty_plte(ping,MagickTrue);
9864
9865 # endif
9866 #endif
9867
9868   x=0;
9869
9870   ping_width=(png_uint_32) image->columns;
9871   ping_height=(png_uint_32) image->rows;
9872
9873   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9874      image_depth=8;
9875
9876   if (mng_info->write_png48 || mng_info->write_png64)
9877      image_depth=16;
9878
9879   if (mng_info->write_png_depth != 0)
9880      image_depth=mng_info->write_png_depth;
9881
9882   /* Adjust requested depth to next higher valid depth if necessary */
9883   if (image_depth > 8)
9884      image_depth=16;
9885
9886   if ((image_depth > 4) && (image_depth < 8))
9887      image_depth=8;
9888
9889   if (image_depth == 3)
9890      image_depth=4;
9891
9892   if (logging != MagickFalse)
9893     {
9894      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9895         "    width=%.20g",(double) ping_width);
9896      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9897         "    height=%.20g",(double) ping_height);
9898      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9899         "    image_matte=%.20g",(double) image->alpha_trait);
9900      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9901         "    image->depth=%.20g",(double) image->depth);
9902      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9903         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
9904     }
9905
9906   save_image_depth=image_depth;
9907   ping_bit_depth=(png_byte) save_image_depth;
9908
9909
9910 #if defined(PNG_pHYs_SUPPORTED)
9911   if (ping_exclude_pHYs == MagickFalse)
9912   {
9913   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9914       (!mng_info->write_mng || !mng_info->equal_physs))
9915     {
9916       if (logging != MagickFalse)
9917         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9918             "    Setting up pHYs chunk");
9919
9920       if (image->units == PixelsPerInchResolution)
9921         {
9922           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9923           ping_pHYs_x_resolution=
9924              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9925           ping_pHYs_y_resolution=
9926              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9927         }
9928
9929       else if (image->units == PixelsPerCentimeterResolution)
9930         {
9931           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9932           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9933           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9934         }
9935
9936       else
9937         {
9938           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9939           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9940           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9941         }
9942
9943       if (logging != MagickFalse)
9944         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9945           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9946           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9947           (int) ping_pHYs_unit_type);
9948        ping_have_pHYs = MagickTrue;
9949     }
9950   }
9951 #endif
9952
9953   if (ping_exclude_bKGD == MagickFalse)
9954   {
9955   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9956     {
9957        unsigned int
9958          mask;
9959
9960        mask=0xffff;
9961        if (ping_bit_depth == 8)
9962           mask=0x00ff;
9963
9964        if (ping_bit_depth == 4)
9965           mask=0x000f;
9966
9967        if (ping_bit_depth == 2)
9968           mask=0x0003;
9969
9970        if (ping_bit_depth == 1)
9971           mask=0x0001;
9972
9973        ping_background.red=(png_uint_16)
9974          (ScaleQuantumToShort(image->background_color.red) & mask);
9975
9976        ping_background.green=(png_uint_16)
9977          (ScaleQuantumToShort(image->background_color.green) & mask);
9978
9979        ping_background.blue=(png_uint_16)
9980          (ScaleQuantumToShort(image->background_color.blue) & mask);
9981
9982        ping_background.gray=(png_uint_16) ping_background.green;
9983     }
9984
9985   if (logging != MagickFalse)
9986     {
9987       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9988           "    Setting up bKGD chunk (1)");
9989       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9990           "      background_color index is %d",
9991           (int) ping_background.index);
9992
9993       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9994           "    ping_bit_depth=%d",ping_bit_depth);
9995     }
9996
9997   ping_have_bKGD = MagickTrue;
9998   }
9999
10000   /*
10001     Select the color type.
10002   */
10003   matte=image_matte;
10004   old_bit_depth=0;
10005
10006   if (mng_info->IsPalette && mng_info->write_png8)
10007     {
10008       /* To do: make this a function cause it's used twice, except
10009          for reducing the sample depth from 8. */
10010
10011       number_colors=image_colors;
10012
10013       ping_have_tRNS=MagickFalse;
10014
10015       /*
10016         Set image palette.
10017       */
10018       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10019
10020       if (logging != MagickFalse)
10021         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10022             "  Setting up PLTE chunk with %d colors (%d)",
10023             number_colors, image_colors);
10024
10025       for (i=0; i < (ssize_t) number_colors; i++)
10026       {
10027         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10028         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10029         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10030         if (logging != MagickFalse)
10031           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10032 #if MAGICKCORE_QUANTUM_DEPTH == 8
10033             "    %3ld (%3d,%3d,%3d)",
10034 #else
10035             "    %5ld (%5d,%5d,%5d)",
10036 #endif
10037             (long) i,palette[i].red,palette[i].green,palette[i].blue);
10038
10039       }
10040
10041       ping_have_PLTE=MagickTrue;
10042       image_depth=ping_bit_depth;
10043       ping_num_trans=0;
10044
10045       if (matte != MagickFalse)
10046       {
10047           /*
10048             Identify which colormap entry is transparent.
10049           */
10050           assert(number_colors <= 256);
10051           assert(image->colormap != NULL);
10052
10053           for (i=0; i < (ssize_t) number_transparent; i++)
10054              ping_trans_alpha[i]=0;
10055
10056
10057           ping_num_trans=(unsigned short) (number_transparent +
10058              number_semitransparent);
10059
10060           if (ping_num_trans == 0)
10061              ping_have_tRNS=MagickFalse;
10062
10063           else
10064              ping_have_tRNS=MagickTrue;
10065       }
10066
10067       if (ping_exclude_bKGD == MagickFalse)
10068       {
10069        /*
10070         * Identify which colormap entry is the background color.
10071         */
10072
10073         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
10074           if (IsPNGColorEqual(ping_background,image->colormap[i]))
10075             break;
10076
10077         ping_background.index=(png_byte) i;
10078
10079         if (logging != MagickFalse)
10080           {
10081             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10082                  "      background_color index is %d",
10083                  (int) ping_background.index);
10084           }
10085       }
10086     } /* end of write_png8 */
10087
10088   else if (mng_info->write_png_colortype == 1)
10089     {
10090       image_matte=MagickFalse;
10091       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10092     }
10093
10094   else if (mng_info->write_png24 || mng_info->write_png48 ||
10095       mng_info->write_png_colortype == 3)
10096     {
10097       image_matte=MagickFalse;
10098       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10099     }
10100
10101   else if (mng_info->write_png32 || mng_info->write_png64 ||
10102       mng_info->write_png_colortype == 7)
10103     {
10104       image_matte=MagickTrue;
10105       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10106     }
10107
10108   else /* mng_info->write_pngNN not specified */
10109     {
10110       image_depth=ping_bit_depth;
10111
10112       if (mng_info->write_png_colortype != 0)
10113         {
10114           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
10115
10116           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10117               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10118             image_matte=MagickTrue;
10119
10120           else
10121             image_matte=MagickFalse;
10122
10123           if (logging != MagickFalse)
10124              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10125              "   PNG colortype %d was specified:",(int) ping_color_type);
10126         }
10127
10128       else /* write_png_colortype not specified */
10129         {
10130           if (logging != MagickFalse)
10131              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10132              "  Selecting PNG colortype:");
10133
10134           ping_color_type=(png_byte) ((matte != MagickFalse)?
10135             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
10136
10137           if (image_info->type == TrueColorType)
10138             {
10139               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10140               image_matte=MagickFalse;
10141             }
10142
10143           if (image_info->type == TrueColorAlphaType)
10144             {
10145               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10146               image_matte=MagickTrue;
10147             }
10148
10149           if (image_info->type == PaletteType ||
10150               image_info->type == PaletteAlphaType)
10151             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10152
10153           if (mng_info->write_png_colortype == 0 &&
10154              image_info->type == UndefinedType)
10155             {
10156               if (ping_have_color == MagickFalse)
10157                 {
10158                   if (image_matte == MagickFalse)
10159                     {
10160                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10161                       image_matte=MagickFalse;
10162                     }
10163
10164                   else
10165                     {
10166                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
10167                       image_matte=MagickTrue;
10168                     }
10169                 }
10170               else
10171                 {
10172                   if (image_matte == MagickFalse)
10173                     {
10174                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10175                       image_matte=MagickFalse;
10176                     }
10177
10178                   else
10179                     {
10180                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
10181                       image_matte=MagickTrue;
10182                     }
10183                  }
10184             }
10185
10186         }
10187
10188       if (logging != MagickFalse)
10189          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10190          "    Selected PNG colortype=%d",ping_color_type);
10191
10192       if (ping_bit_depth < 8)
10193         {
10194           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10195               ping_color_type == PNG_COLOR_TYPE_RGB ||
10196               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10197             ping_bit_depth=8;
10198         }
10199
10200       old_bit_depth=ping_bit_depth;
10201
10202       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10203         {
10204           if (image->alpha_trait == UndefinedPixelTrait &&
10205                ping_have_non_bw == MagickFalse)
10206              ping_bit_depth=1;
10207         }
10208
10209       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
10210         {
10211            size_t one = 1;
10212            ping_bit_depth=1;
10213
10214            if (image->colors == 0)
10215            {
10216               /* DO SOMETHING */
10217                 png_error(ping,"image has 0 colors");
10218            }
10219
10220            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
10221              ping_bit_depth <<= 1;
10222         }
10223
10224       if (logging != MagickFalse)
10225          {
10226            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10227             "    Number of colors: %.20g",(double) image_colors);
10228
10229            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10230             "    Tentative PNG bit depth: %d",ping_bit_depth);
10231          }
10232
10233       if (ping_bit_depth < (int) mng_info->write_png_depth)
10234          ping_bit_depth = mng_info->write_png_depth;
10235     }
10236
10237   image_depth=ping_bit_depth;
10238
10239   if (logging != MagickFalse)
10240     {
10241       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10242         "    Tentative PNG color type: %s (%.20g)",
10243         PngColorTypeToString(ping_color_type),
10244         (double) ping_color_type);
10245
10246       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10247         "    image_info->type: %.20g",(double) image_info->type);
10248
10249       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10250         "    image_depth: %.20g",(double) image_depth);
10251
10252       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10253
10254         "    image->depth: %.20g",(double) image->depth);
10255
10256       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10257         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
10258     }
10259
10260   if (matte != MagickFalse)
10261     {
10262       if (mng_info->IsPalette)
10263         {
10264           if (mng_info->write_png_colortype == 0)
10265             {
10266               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10267
10268               if (ping_have_color != MagickFalse)
10269                  ping_color_type=PNG_COLOR_TYPE_RGBA;
10270             }
10271
10272           /*
10273            * Determine if there is any transparent color.
10274           */
10275           if (number_transparent + number_semitransparent == 0)
10276             {
10277               /*
10278                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
10279               */
10280
10281               image_matte=MagickFalse;
10282
10283               if (mng_info->write_png_colortype == 0)
10284                 ping_color_type&=0x03;
10285             }
10286
10287           else
10288             {
10289               unsigned int
10290                 mask;
10291
10292               mask=0xffff;
10293
10294               if (ping_bit_depth == 8)
10295                  mask=0x00ff;
10296
10297               if (ping_bit_depth == 4)
10298                  mask=0x000f;
10299
10300               if (ping_bit_depth == 2)
10301                  mask=0x0003;
10302
10303               if (ping_bit_depth == 1)
10304                  mask=0x0001;
10305
10306               ping_trans_color.red=(png_uint_16)
10307                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10308
10309               ping_trans_color.green=(png_uint_16)
10310                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10311
10312               ping_trans_color.blue=(png_uint_16)
10313                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10314
10315               ping_trans_color.gray=(png_uint_16)
10316                 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
10317                    image->colormap)) & mask);
10318
10319               ping_trans_color.index=(png_byte) 0;
10320
10321               ping_have_tRNS=MagickTrue;
10322             }
10323
10324           if (ping_have_tRNS != MagickFalse)
10325             {
10326               /*
10327                * Determine if there is one and only one transparent color
10328                * and if so if it is fully transparent.
10329                */
10330               if (ping_have_cheap_transparency == MagickFalse)
10331                 ping_have_tRNS=MagickFalse;
10332             }
10333
10334           if (ping_have_tRNS != MagickFalse)
10335             {
10336               if (mng_info->write_png_colortype == 0)
10337                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
10338
10339               if (image_depth == 8)
10340                 {
10341                   ping_trans_color.red&=0xff;
10342                   ping_trans_color.green&=0xff;
10343                   ping_trans_color.blue&=0xff;
10344                   ping_trans_color.gray&=0xff;
10345                 }
10346             }
10347         }
10348       else
10349         {
10350           if (image_depth == 8)
10351             {
10352               ping_trans_color.red&=0xff;
10353               ping_trans_color.green&=0xff;
10354               ping_trans_color.blue&=0xff;
10355               ping_trans_color.gray&=0xff;
10356             }
10357         }
10358     }
10359
10360     matte=image_matte;
10361
10362     if (ping_have_tRNS != MagickFalse)
10363       image_matte=MagickFalse;
10364
10365     if ((mng_info->IsPalette) &&
10366         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10367         ping_have_color == MagickFalse &&
10368         (image_matte == MagickFalse || image_depth >= 8))
10369       {
10370         size_t one=1;
10371
10372         if (image_matte != MagickFalse)
10373           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10374
10375         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10376           {
10377             ping_color_type=PNG_COLOR_TYPE_GRAY;
10378
10379             if (save_image_depth == 16 && image_depth == 8)
10380               {
10381                 if (logging != MagickFalse)
10382                   {
10383                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10384                         "  Scaling ping_trans_color (0)");
10385                   }
10386                     ping_trans_color.gray*=0x0101;
10387               }
10388           }
10389
10390         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10391           image_depth=MAGICKCORE_QUANTUM_DEPTH;
10392
10393         if ((image_colors == 0) ||
10394              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10395           image_colors=(int) (one << image_depth);
10396
10397         if (image_depth > 8)
10398           ping_bit_depth=16;
10399
10400         else
10401           {
10402             ping_bit_depth=8;
10403             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10404               {
10405                 if(!mng_info->write_png_depth)
10406                   {
10407                     ping_bit_depth=1;
10408
10409                     while ((int) (one << ping_bit_depth)
10410                         < (ssize_t) image_colors)
10411                       ping_bit_depth <<= 1;
10412                   }
10413               }
10414
10415             else if (ping_color_type ==
10416                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10417                 mng_info->IsPalette)
10418               {
10419               /* Check if grayscale is reducible */
10420
10421                 int
10422                   depth_4_ok=MagickTrue,
10423                   depth_2_ok=MagickTrue,
10424                   depth_1_ok=MagickTrue;
10425
10426                 for (i=0; i < (ssize_t) image_colors; i++)
10427                 {
10428                    unsigned char
10429                      intensity;
10430
10431                    intensity=ScaleQuantumToChar(image->colormap[i].red);
10432
10433                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10434                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10435                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10436                      depth_2_ok=depth_1_ok=MagickFalse;
10437                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10438                      depth_1_ok=MagickFalse;
10439                 }
10440
10441                 if (depth_1_ok && mng_info->write_png_depth <= 1)
10442                   ping_bit_depth=1;
10443
10444                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10445                   ping_bit_depth=2;
10446
10447                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10448                   ping_bit_depth=4;
10449               }
10450           }
10451
10452           image_depth=ping_bit_depth;
10453       }
10454
10455     else
10456
10457       if (mng_info->IsPalette)
10458       {
10459         number_colors=image_colors;
10460
10461         if (image_depth <= 8)
10462           {
10463             /*
10464               Set image palette.
10465             */
10466             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10467
10468             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10469               {
10470                 for (i=0; i < (ssize_t) number_colors; i++)
10471                 {
10472                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10473                   palette[i].green=
10474                     ScaleQuantumToChar(image->colormap[i].green);
10475                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10476                 }
10477
10478                 if (logging != MagickFalse)
10479                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10480                     "  Setting up PLTE chunk with %d colors",
10481                     number_colors);
10482
10483                 ping_have_PLTE=MagickTrue;
10484               }
10485
10486             /* color_type is PNG_COLOR_TYPE_PALETTE */
10487             if (mng_info->write_png_depth == 0)
10488               {
10489                 size_t
10490                   one;
10491
10492                 ping_bit_depth=1;
10493                 one=1;
10494
10495                 while ((one << ping_bit_depth) < (size_t) number_colors)
10496                   ping_bit_depth <<= 1;
10497               }
10498
10499             ping_num_trans=0;
10500
10501             if (matte != MagickFalse)
10502               {
10503                 /*
10504                  * Set up trans_colors array.
10505                  */
10506                 assert(number_colors <= 256);
10507
10508                 ping_num_trans=(unsigned short) (number_transparent +
10509                   number_semitransparent);
10510
10511                 if (ping_num_trans == 0)
10512                   ping_have_tRNS=MagickFalse;
10513
10514                 else
10515                   {
10516                     if (logging != MagickFalse)
10517                       {
10518                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10519                           "  Scaling ping_trans_color (1)");
10520                       }
10521                     ping_have_tRNS=MagickTrue;
10522
10523                     for (i=0; i < ping_num_trans; i++)
10524                     {
10525                        ping_trans_alpha[i]= (png_byte)
10526                          ScaleQuantumToChar(image->colormap[i].alpha);
10527                     }
10528                   }
10529               }
10530           }
10531       }
10532
10533     else
10534       {
10535
10536         if (image_depth < 8)
10537           image_depth=8;
10538
10539         if ((save_image_depth == 16) && (image_depth == 8))
10540           {
10541             if (logging != MagickFalse)
10542               {
10543                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10544                   "    Scaling ping_trans_color from (%d,%d,%d)",
10545                   (int) ping_trans_color.red,
10546                   (int) ping_trans_color.green,
10547                   (int) ping_trans_color.blue);
10548               }
10549
10550             ping_trans_color.red*=0x0101;
10551             ping_trans_color.green*=0x0101;
10552             ping_trans_color.blue*=0x0101;
10553             ping_trans_color.gray*=0x0101;
10554
10555             if (logging != MagickFalse)
10556               {
10557                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10558                   "    to (%d,%d,%d)",
10559                   (int) ping_trans_color.red,
10560                   (int) ping_trans_color.green,
10561                   (int) ping_trans_color.blue);
10562               }
10563           }
10564       }
10565
10566     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
10567          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
10568
10569     /*
10570       Adjust background and transparency samples in sub-8-bit grayscale files.
10571     */
10572     if (ping_bit_depth < 8 && ping_color_type ==
10573         PNG_COLOR_TYPE_GRAY)
10574       {
10575          png_uint_16
10576            maxval;
10577
10578          size_t
10579            one=1;
10580
10581          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10582
10583          if (ping_exclude_bKGD == MagickFalse)
10584          {
10585
10586          ping_background.gray=(png_uint_16) ((maxval/65535.)*
10587            (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10588            &image->background_color))) +.5)));
10589
10590          if (logging != MagickFalse)
10591            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10592              "  Setting up bKGD chunk (2)");
10593          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10594              "      background_color index is %d",
10595              (int) ping_background.index);
10596
10597          ping_have_bKGD = MagickTrue;
10598          }
10599
10600          if (logging != MagickFalse)
10601            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10602              "  Scaling ping_trans_color.gray from %d",
10603              (int)ping_trans_color.gray);
10604
10605          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10606            ping_trans_color.gray)+.5);
10607
10608          if (logging != MagickFalse)
10609            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10610              "      to %d", (int)ping_trans_color.gray);
10611       }
10612
10613   if (ping_exclude_bKGD == MagickFalse)
10614   {
10615     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10616       {
10617         /*
10618            Identify which colormap entry is the background color.
10619         */
10620
10621         number_colors=image_colors;
10622
10623         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10624           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10625             break;
10626
10627         ping_background.index=(png_byte) i;
10628
10629         if (logging != MagickFalse)
10630           {
10631             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10632               "  Setting up bKGD chunk with index=%d",(int) i);
10633           }
10634
10635         if (i < (ssize_t) number_colors)
10636           {
10637             ping_have_bKGD = MagickTrue;
10638
10639             if (logging != MagickFalse)
10640               {
10641                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10642                   "     background   =(%d,%d,%d)",
10643                         (int) ping_background.red,
10644                         (int) ping_background.green,
10645                         (int) ping_background.blue);
10646               }
10647           }
10648
10649         else  /* Can't happen */
10650           {
10651             if (logging != MagickFalse)
10652               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10653                   "      No room in PLTE to add bKGD color");
10654             ping_have_bKGD = MagickFalse;
10655           }
10656       }
10657   }
10658
10659   if (logging != MagickFalse)
10660     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10661       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10662       ping_color_type);
10663   /*
10664     Initialize compression level and filtering.
10665   */
10666   if (logging != MagickFalse)
10667     {
10668       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10669         "  Setting up deflate compression");
10670
10671       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10672         "    Compression buffer size: 32768");
10673     }
10674
10675   png_set_compression_buffer_size(ping,32768L);
10676
10677   if (logging != MagickFalse)
10678     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10679       "    Compression mem level: 9");
10680
10681   png_set_compression_mem_level(ping, 9);
10682
10683   /* Untangle the "-quality" setting:
10684
10685      Undefined is 0; the default is used.
10686      Default is 75
10687
10688      10's digit:
10689
10690         0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10691            zlib default compression level
10692
10693         1-9: the zlib compression level
10694
10695      1's digit:
10696
10697         0-4: the PNG filter method
10698
10699         5:   libpng adaptive filtering if compression level > 5
10700              libpng filter type "none" if compression level <= 5
10701                 or if image is grayscale or palette
10702
10703         6:   libpng adaptive filtering
10704
10705         7:   "LOCO" filtering (intrapixel differing) if writing
10706              a MNG, otherwise "none".  Did not work in IM-6.7.0-9
10707              and earlier because of a missing "else".
10708
10709         8:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10710              filtering. Unused prior to IM-6.7.0-10, was same as 6
10711
10712         9:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10713              Unused prior to IM-6.7.0-10, was same as 6
10714
10715     Note that using the -quality option, not all combinations of
10716     PNG filter type, zlib compression level, and zlib compression
10717     strategy are possible.  This will be addressed soon in a
10718     release that accomodates "-define png:compression-strategy", etc.
10719
10720    */
10721
10722   quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10723      image_info->quality;
10724
10725   if (quality <= 9)
10726     {
10727       if (mng_info->write_png_compression_strategy == 0)
10728         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10729     }
10730
10731   else if (mng_info->write_png_compression_level == 0)
10732     {
10733       int
10734         level;
10735
10736       level=(int) MagickMin((ssize_t) quality/10,9);
10737
10738       mng_info->write_png_compression_level = level+1;
10739     }
10740
10741   if (mng_info->write_png_compression_strategy == 0)
10742     {
10743         if ((quality %10) == 8 || (quality %10) == 9)
10744 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10745           mng_info->write_png_compression_strategy=Z_RLE+1;
10746 #else
10747           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10748 #endif
10749     }
10750
10751   if (mng_info->write_png_compression_filter == 0)
10752         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10753
10754   if (logging != MagickFalse)
10755     {
10756         if (mng_info->write_png_compression_level)
10757           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10758             "    Compression level:    %d",
10759             (int) mng_info->write_png_compression_level-1);
10760
10761         if (mng_info->write_png_compression_strategy)
10762           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10763             "    Compression strategy: %d",
10764             (int) mng_info->write_png_compression_strategy-1);
10765
10766         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10767           "  Setting up filtering");
10768
10769         if (mng_info->write_png_compression_filter == 6)
10770           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10771             "    Base filter method: ADAPTIVE");
10772         else if (mng_info->write_png_compression_filter == 0 ||
10773                  mng_info->write_png_compression_filter == 1)
10774           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10775             "    Base filter method: NONE");
10776         else
10777           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10778             "    Base filter method: %d",
10779             (int) mng_info->write_png_compression_filter-1);
10780     }
10781
10782   if (mng_info->write_png_compression_level != 0)
10783     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10784
10785   if (mng_info->write_png_compression_filter == 6)
10786     {
10787       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10788          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10789          (quality < 50))
10790         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10791       else
10792         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10793      }
10794   else if (mng_info->write_png_compression_filter == 7 ||
10795       mng_info->write_png_compression_filter == 10)
10796     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10797
10798   else if (mng_info->write_png_compression_filter == 8)
10799     {
10800 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10801       if (mng_info->write_mng)
10802       {
10803          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10804              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10805         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10806       }
10807 #endif
10808       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10809     }
10810
10811   else if (mng_info->write_png_compression_filter == 9)
10812     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10813
10814   else if (mng_info->write_png_compression_filter != 0)
10815     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10816        mng_info->write_png_compression_filter-1);
10817
10818   if (mng_info->write_png_compression_strategy != 0)
10819     png_set_compression_strategy(ping,
10820        mng_info->write_png_compression_strategy-1);
10821
10822   ping_interlace_method=image_info->interlace != NoInterlace;
10823
10824   if (mng_info->write_mng)
10825     png_set_sig_bytes(ping,8);
10826
10827   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10828
10829   if (mng_info->write_png_colortype != 0)
10830     {
10831      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10832        if (ping_have_color != MagickFalse)
10833          {
10834            ping_color_type = PNG_COLOR_TYPE_RGB;
10835
10836            if (ping_bit_depth < 8)
10837              ping_bit_depth=8;
10838          }
10839
10840      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10841        if (ping_have_color != MagickFalse)
10842          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10843     }
10844
10845   if (ping_need_colortype_warning != MagickFalse ||
10846      ((mng_info->write_png_depth &&
10847      (int) mng_info->write_png_depth != ping_bit_depth) ||
10848      (mng_info->write_png_colortype &&
10849      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10850       mng_info->write_png_colortype != 7 &&
10851       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10852     {
10853       if (logging != MagickFalse)
10854         {
10855           if (ping_need_colortype_warning != MagickFalse)
10856             {
10857               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10858                  "  Image has transparency but tRNS chunk was excluded");
10859             }
10860
10861           if (mng_info->write_png_depth)
10862             {
10863               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10864                   "  Defined png:bit-depth=%u, Computed depth=%u",
10865                   mng_info->write_png_depth,
10866                   ping_bit_depth);
10867             }
10868
10869           if (mng_info->write_png_colortype)
10870             {
10871               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10872                   "  Defined png:color-type=%u, Computed color type=%u",
10873                   mng_info->write_png_colortype-1,
10874                   ping_color_type);
10875             }
10876         }
10877
10878       png_warning(ping,
10879         "Cannot write image with defined png:bit-depth or png:color-type.");
10880     }
10881
10882   if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10883     {
10884       /* Add an opaque matte channel */
10885       image->alpha_trait = BlendPixelTrait;
10886       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10887
10888       if (logging != MagickFalse)
10889         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10890           "  Added an opaque matte channel");
10891     }
10892
10893   if (number_transparent != 0 || number_semitransparent != 0)
10894     {
10895       if (ping_color_type < 4)
10896         {
10897            ping_have_tRNS=MagickTrue;
10898            if (logging != MagickFalse)
10899              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10900                "  Setting ping_have_tRNS=MagickTrue.");
10901         }
10902     }
10903
10904   if (logging != MagickFalse)
10905     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10906       "  Writing PNG header chunks");
10907
10908   png_set_IHDR(ping,ping_info,ping_width,ping_height,
10909                ping_bit_depth,ping_color_type,
10910                ping_interlace_method,ping_compression_method,
10911                ping_filter_method);
10912
10913   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10914     {
10915       png_set_PLTE(ping,ping_info,palette,number_colors);
10916
10917       if (logging != MagickFalse)
10918         {
10919           for (i=0; i< (ssize_t) number_colors; i++)
10920           {
10921             if (i < ping_num_trans)
10922               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10923                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10924                       (int) i,
10925                       (int) palette[i].red,
10926                       (int) palette[i].green,
10927                       (int) palette[i].blue,
10928                       (int) i,
10929                       (int) ping_trans_alpha[i]);
10930              else
10931               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10932                 "     PLTE[%d] = (%d,%d,%d)",
10933                       (int) i,
10934                       (int) palette[i].red,
10935                       (int) palette[i].green,
10936                       (int) palette[i].blue);
10937            }
10938          }
10939     }
10940
10941   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10942   if (ping_exclude_sRGB != MagickFalse ||
10943      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10944   {
10945     if ((ping_exclude_tEXt == MagickFalse ||
10946        ping_exclude_zTXt == MagickFalse) &&
10947        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10948     {
10949       ResetImageProfileIterator(image);
10950       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10951       {
10952         profile=GetImageProfile(image,name);
10953
10954         if (profile != (StringInfo *) NULL)
10955           {
10956 #ifdef PNG_WRITE_iCCP_SUPPORTED
10957             if ((LocaleCompare(name,"ICC") == 0) ||
10958                 (LocaleCompare(name,"ICM") == 0))
10959               {
10960                 ping_have_iCCP = MagickTrue;
10961                 if (ping_exclude_iCCP == MagickFalse)
10962                   {
10963                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10964                         "  Setting up iCCP chunk");
10965
10966                     png_set_iCCP(ping,ping_info,(png_charp) name,0,
10967 #if (PNG_LIBPNG_VER < 10500)
10968                     (png_charp) GetStringInfoDatum(profile),
10969 #else
10970                     (const png_byte *) GetStringInfoDatum(profile),
10971 #endif
10972                     (png_uint_32) GetStringInfoLength(profile));
10973                   }
10974                 else
10975                   {
10976                     /* Do not write hex-encoded ICC chunk */
10977                        name=GetNextImageProfile(image);
10978                        continue;
10979                   }
10980               }
10981 #endif /* WRITE_iCCP */
10982
10983 #if defined(eXIf_SUPPORTED) || defined(exIf_SUPPORTED) || \
10984     defined(zxIf_SUPPORTED)
10985             if (LocaleCompare(name,"exif") == 0)
10986               {
10987                    /* Do not write hex-encoded ICC chunk; we will
10988                       write it later as an eXIf or zxIf chunk */
10989                    name=GetNextImageProfile(image);
10990                    continue;
10991               }
10992 #endif
10993
10994               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10995                  "  Setting up zTXt chunk with uuencoded %s profile",
10996                  name);
10997               Magick_png_write_raw_profile(image_info,ping,ping_info,
10998                 (unsigned char *) name,(unsigned char *) name,
10999                 GetStringInfoDatum(profile),
11000                 (png_uint_32) GetStringInfoLength(profile));
11001           }
11002         name=GetNextImageProfile(image);
11003       }
11004     }
11005   }
11006
11007 #if defined(PNG_WRITE_sRGB_SUPPORTED)
11008   if ((mng_info->have_write_global_srgb == 0) &&
11009       ping_have_iCCP != MagickTrue &&
11010       (ping_have_sRGB != MagickFalse ||
11011       png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11012     {
11013       if (ping_exclude_sRGB == MagickFalse)
11014         {
11015           /*
11016             Note image rendering intent.
11017           */
11018           if (logging != MagickFalse)
11019             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11020                 "  Setting up sRGB chunk");
11021
11022           (void) png_set_sRGB(ping,ping_info,(
11023             Magick_RenderingIntent_to_PNG_RenderingIntent(
11024               image->rendering_intent)));
11025
11026           ping_have_sRGB = MagickTrue;
11027         }
11028     }
11029
11030   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11031 #endif
11032     {
11033       if (ping_exclude_gAMA == MagickFalse &&
11034           ping_have_iCCP == MagickFalse &&
11035           ping_have_sRGB == MagickFalse &&
11036           (ping_exclude_sRGB == MagickFalse ||
11037           (image->gamma < .45 || image->gamma > .46)))
11038       {
11039       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
11040         {
11041           /*
11042             Note image gamma.
11043             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11044           */
11045           if (logging != MagickFalse)
11046             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11047               "  Setting up gAMA chunk");
11048
11049           png_set_gAMA(ping,ping_info,image->gamma);
11050         }
11051       }
11052
11053       if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
11054         {
11055           if ((mng_info->have_write_global_chrm == 0) &&
11056               (image->chromaticity.red_primary.x != 0.0))
11057             {
11058               /*
11059                 Note image chromaticity.
11060                 Note: if cHRM+gAMA == sRGB write sRGB instead.
11061               */
11062                PrimaryInfo
11063                  bp,
11064                  gp,
11065                  rp,
11066                  wp;
11067
11068                wp=image->chromaticity.white_point;
11069                rp=image->chromaticity.red_primary;
11070                gp=image->chromaticity.green_primary;
11071                bp=image->chromaticity.blue_primary;
11072
11073                if (logging != MagickFalse)
11074                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11075                    "  Setting up cHRM chunk");
11076
11077                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
11078                    bp.x,bp.y);
11079            }
11080         }
11081     }
11082
11083   if (ping_exclude_bKGD == MagickFalse)
11084     {
11085       if (ping_have_bKGD != MagickFalse)
11086         {
11087           png_set_bKGD(ping,ping_info,&ping_background);
11088           if (logging != MagickFalse)
11089             {
11090               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11091                    "    Setting up bKGD chunk");
11092               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11093                    "      background color = (%d,%d,%d)",
11094                         (int) ping_background.red,
11095                         (int) ping_background.green,
11096                         (int) ping_background.blue);
11097               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11098                    "      index = %d, gray=%d",
11099                         (int) ping_background.index,
11100                         (int) ping_background.gray);
11101             }
11102          }
11103     }
11104
11105   if (ping_exclude_pHYs == MagickFalse)
11106     {
11107       if (ping_have_pHYs != MagickFalse)
11108         {
11109           png_set_pHYs(ping,ping_info,
11110              ping_pHYs_x_resolution,
11111              ping_pHYs_y_resolution,
11112              ping_pHYs_unit_type);
11113
11114           if (logging != MagickFalse)
11115             {
11116               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11117                    "    Setting up pHYs chunk");
11118               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11119                    "      x_resolution=%lu",
11120                    (unsigned long) ping_pHYs_x_resolution);
11121               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11122                    "      y_resolution=%lu",
11123                    (unsigned long) ping_pHYs_y_resolution);
11124               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11125                    "      unit_type=%lu",
11126                    (unsigned long) ping_pHYs_unit_type);
11127             }
11128         }
11129     }
11130
11131 #if defined(PNG_tIME_SUPPORTED)
11132   if (ping_exclude_tIME == MagickFalse)
11133     {
11134       const char
11135         *timestamp;
11136
11137       if (image->taint == MagickFalse)
11138         {
11139           timestamp=GetImageOption(image_info,"png:tIME");
11140
11141           if (timestamp == (const char *) NULL)
11142             timestamp=GetImageProperty(image,"png:tIME",exception);
11143         }
11144
11145       else
11146         {
11147           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11148              "  Reset tIME in tainted image");
11149
11150           timestamp=GetImageProperty(image,"date:modify",exception);
11151         }
11152
11153       if (timestamp != (const char *) NULL)
11154           write_tIME_chunk(image,ping,ping_info,timestamp,exception);
11155     }
11156 #endif
11157
11158   if (mng_info->need_blob != MagickFalse)
11159   {
11160     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
11161        MagickFalse)
11162        png_error(ping,"WriteBlob Failed");
11163
11164      ping_have_blob=MagickTrue;
11165   }
11166
11167   png_write_info_before_PLTE(ping, ping_info);
11168
11169   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
11170     {
11171       if (logging != MagickFalse)
11172         {
11173           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11174               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
11175         }
11176
11177       if (ping_color_type == 3)
11178          (void) png_set_tRNS(ping, ping_info,
11179                 ping_trans_alpha,
11180                 ping_num_trans,
11181                 NULL);
11182
11183       else
11184         {
11185            (void) png_set_tRNS(ping, ping_info,
11186                   NULL,
11187                   0,
11188                   &ping_trans_color);
11189
11190            if (logging != MagickFalse)
11191              {
11192                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11193                  "     tRNS color   =(%d,%d,%d)",
11194                        (int) ping_trans_color.red,
11195                        (int) ping_trans_color.green,
11196                        (int) ping_trans_color.blue);
11197              }
11198          }
11199     }
11200
11201   /* write any png-chunk-b profiles */
11202   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
11203
11204   png_write_info(ping,ping_info);
11205
11206   /* write any PNG-chunk-m profiles */
11207   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
11208
11209   ping_wrote_caNv = MagickFalse;
11210
11211   /* write caNv chunk */
11212   if (ping_exclude_caNv == MagickFalse)
11213     {
11214       if ((image->page.width != 0 && image->page.width != image->columns) ||
11215           (image->page.height != 0 && image->page.height != image->rows) ||
11216           image->page.x != 0 || image->page.y != 0)
11217         {
11218           unsigned char
11219             chunk[20];
11220
11221           (void) WriteBlobMSBULong(image,16L);  /* data length=8 */
11222           PNGType(chunk,mng_caNv);
11223           LogPNGChunk(logging,mng_caNv,16L);
11224           PNGLong(chunk+4,(png_uint_32) image->page.width);
11225           PNGLong(chunk+8,(png_uint_32) image->page.height);
11226           PNGsLong(chunk+12,(png_int_32) image->page.x);
11227           PNGsLong(chunk+16,(png_int_32) image->page.y);
11228           (void) WriteBlob(image,20,chunk);
11229           (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11230           ping_wrote_caNv = MagickTrue;
11231         }
11232     }
11233
11234 #if defined(PNG_oFFs_SUPPORTED)
11235   if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
11236     {
11237       if (image->page.x || image->page.y)
11238         {
11239            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
11240               (png_int_32) image->page.y, 0);
11241
11242            if (logging != MagickFalse)
11243              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11244                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
11245                  (int) image->page.x, (int) image->page.y);
11246         }
11247     }
11248 #endif
11249
11250   /* write vpAg chunk (deprecated, replaced by caNv) */
11251   if (ping_exclude_vpAg == MagickFalse && ping_wrote_caNv == MagickFalse)
11252     {
11253       if ((image->page.width != 0 && image->page.width != image->columns) ||
11254           (image->page.height != 0 && image->page.height != image->rows))
11255         {
11256           unsigned char
11257             chunk[14];
11258
11259           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
11260           PNGType(chunk,mng_vpAg);
11261           LogPNGChunk(logging,mng_vpAg,9L);
11262           PNGLong(chunk+4,(png_uint_32) image->page.width);
11263           PNGLong(chunk+8,(png_uint_32) image->page.height);
11264           chunk[12]=0;   /* unit = pixels */
11265           (void) WriteBlob(image,13,chunk);
11266           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11267         }
11268     }
11269
11270 #if (PNG_LIBPNG_VER == 10206)
11271     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
11272 #define PNG_HAVE_IDAT               0x04
11273     ping->mode |= PNG_HAVE_IDAT;
11274 #undef PNG_HAVE_IDAT
11275 #endif
11276
11277   png_set_packing(ping);
11278   /*
11279     Allocate memory.
11280   */
11281   rowbytes=image->columns;
11282   if (image_depth > 8)
11283     rowbytes*=2;
11284   switch (ping_color_type)
11285     {
11286       case PNG_COLOR_TYPE_RGB:
11287         rowbytes*=3;
11288         break;
11289
11290       case PNG_COLOR_TYPE_GRAY_ALPHA:
11291         rowbytes*=2;
11292         break;
11293
11294       case PNG_COLOR_TYPE_RGBA:
11295         rowbytes*=4;
11296         break;
11297
11298       default:
11299         break;
11300     }
11301
11302   if (logging != MagickFalse)
11303     {
11304       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11305         "  Writing PNG image data");
11306
11307       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11308         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
11309     }
11310   pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
11311   if (pixel_info == (MemoryInfo *) NULL)
11312     png_error(ping,"Allocation of memory for pixels failed");
11313   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
11314
11315   /*
11316     Initialize image scanlines.
11317   */
11318   quantum_info=AcquireQuantumInfo(image_info,image);
11319   if (quantum_info == (QuantumInfo *) NULL)
11320     png_error(ping,"Memory allocation for quantum_info failed");
11321   quantum_info->format=UndefinedQuantumFormat;
11322   SetQuantumDepth(image,quantum_info,image_depth);
11323   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
11324   num_passes=png_set_interlace_handling(ping);
11325
11326   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11327        !mng_info->write_png48 && !mng_info->write_png64 &&
11328        !mng_info->write_png32) &&
11329        (mng_info->IsPalette ||
11330        (image_info->type == BilevelType)) &&
11331        image_matte == MagickFalse &&
11332        ping_have_non_bw == MagickFalse)
11333     {
11334       /* Palette, Bilevel, or Opaque Monochrome */
11335       register const Quantum
11336         *p;
11337
11338       SetQuantumDepth(image,quantum_info,8);
11339       for (pass=0; pass < num_passes; pass++)
11340       {
11341         /*
11342           Convert PseudoClass image to a PNG monochrome image.
11343         */
11344         for (y=0; y < (ssize_t) image->rows; y++)
11345         {
11346           if (logging != MagickFalse && y == 0)
11347              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11348                  "    Writing row of pixels (0)");
11349
11350           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11351
11352           if (p == (const Quantum *) NULL)
11353             break;
11354
11355           if (mng_info->IsPalette)
11356             {
11357               (void) ExportQuantumPixels(image,(CacheView *) NULL,
11358                 quantum_info,GrayQuantum,ping_pixels,exception);
11359               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
11360                   mng_info->write_png_depth &&
11361                   mng_info->write_png_depth != old_bit_depth)
11362                 {
11363                   /* Undo pixel scaling */
11364                   for (i=0; i < (ssize_t) image->columns; i++)
11365                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
11366                      >> (8-old_bit_depth));
11367                 }
11368             }
11369
11370           else
11371             {
11372               (void) ExportQuantumPixels(image,(CacheView *) NULL,
11373                 quantum_info,RedQuantum,ping_pixels,exception);
11374             }
11375
11376           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11377             for (i=0; i < (ssize_t) image->columns; i++)
11378                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11379                       255 : 0);
11380
11381           if (logging != MagickFalse && y == 0)
11382             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11383                 "    Writing row of pixels (1)");
11384
11385           png_write_row(ping,ping_pixels);
11386
11387           status=SetImageProgress(image,SaveImageTag,
11388               (MagickOffsetType) (pass * image->rows + y),
11389               num_passes * image->rows);
11390
11391           if (status == MagickFalse)
11392             break;
11393         }
11394       }
11395     }
11396
11397   else   /* Not Palette, Bilevel, or Opaque Monochrome */
11398     {
11399       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11400           !mng_info->write_png48 && !mng_info->write_png64 &&
11401           !mng_info->write_png32) && (image_matte != MagickFalse ||
11402           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11403           (mng_info->IsPalette) && ping_have_color == MagickFalse)
11404         {
11405           register const Quantum
11406             *p;
11407
11408           for (pass=0; pass < num_passes; pass++)
11409           {
11410
11411           for (y=0; y < (ssize_t) image->rows; y++)
11412           {
11413             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11414
11415             if (p == (const Quantum *) NULL)
11416               break;
11417
11418             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11419               {
11420                 if (mng_info->IsPalette)
11421                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11422                     quantum_info,GrayQuantum,ping_pixels,exception);
11423
11424                 else
11425                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11426                     quantum_info,RedQuantum,ping_pixels,exception);
11427
11428                 if (logging != MagickFalse && y == 0)
11429                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11430                        "    Writing GRAY PNG pixels (2)");
11431               }
11432
11433             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11434               {
11435                 if (logging != MagickFalse && y == 0)
11436                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11437                          "    Writing GRAY_ALPHA PNG pixels (2)");
11438
11439                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11440                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11441               }
11442
11443             if (logging != MagickFalse && y == 0)
11444               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11445                   "    Writing row of pixels (2)");
11446
11447             png_write_row(ping,ping_pixels);
11448
11449             status=SetImageProgress(image,SaveImageTag,
11450               (MagickOffsetType) (pass * image->rows + y),
11451               num_passes * image->rows);
11452
11453             if (status == MagickFalse)
11454               break;
11455             }
11456           }
11457         }
11458
11459       else
11460         {
11461           register const Quantum
11462             *p;
11463
11464           for (pass=0; pass < num_passes; pass++)
11465           {
11466             if ((image_depth > 8) ||
11467                 mng_info->write_png24 ||
11468                 mng_info->write_png32 ||
11469                 mng_info->write_png48 ||
11470                 mng_info->write_png64 ||
11471                 (!mng_info->write_png8 && !mng_info->IsPalette))
11472             {
11473               for (y=0; y < (ssize_t) image->rows; y++)
11474               {
11475                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11476
11477                 if (p == (const Quantum *) NULL)
11478                   break;
11479
11480                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11481                   {
11482                     if (image->storage_class == DirectClass)
11483                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11484                         quantum_info,RedQuantum,ping_pixels,exception);
11485
11486                     else
11487                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11488                         quantum_info,GrayQuantum,ping_pixels,exception);
11489                   }
11490
11491                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11492                   {
11493                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11494                       quantum_info,GrayAlphaQuantum,ping_pixels,
11495                       exception);
11496
11497                     if (logging != MagickFalse && y == 0)
11498                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11499                            "    Writing GRAY_ALPHA PNG pixels (3)");
11500                   }
11501
11502                 else if (image_matte != MagickFalse)
11503                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11504                     quantum_info,RGBAQuantum,ping_pixels,exception);
11505
11506                 else
11507                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11508                     quantum_info,RGBQuantum,ping_pixels,exception);
11509
11510                 if (logging != MagickFalse && y == 0)
11511                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11512                       "    Writing row of pixels (3)");
11513
11514                 png_write_row(ping,ping_pixels);
11515
11516                 status=SetImageProgress(image,SaveImageTag,
11517                   (MagickOffsetType) (pass * image->rows + y),
11518                   num_passes * image->rows);
11519
11520                 if (status == MagickFalse)
11521                   break;
11522               }
11523             }
11524
11525           else
11526             /* not ((image_depth > 8) ||
11527                 mng_info->write_png24 || mng_info->write_png32 ||
11528                 mng_info->write_png48 || mng_info->write_png64 ||
11529                 (!mng_info->write_png8 && !mng_info->IsPalette))
11530              */
11531             {
11532               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11533                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11534                 {
11535                   if (logging != MagickFalse)
11536                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11537                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11538
11539                   SetQuantumDepth(image,quantum_info,8);
11540                   image_depth=8;
11541                 }
11542
11543               for (y=0; y < (ssize_t) image->rows; y++)
11544               {
11545                 if (logging != MagickFalse && y == 0)
11546                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11547                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
11548                     pass);
11549
11550                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11551
11552                 if (p == (const Quantum *) NULL)
11553                   break;
11554
11555                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11556                   {
11557                     SetQuantumDepth(image,quantum_info,image->depth);
11558
11559                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11560                        quantum_info,GrayQuantum,ping_pixels,exception);
11561                   }
11562
11563                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11564                   {
11565                     if (logging != MagickFalse && y == 0)
11566                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11567                            "  Writing GRAY_ALPHA PNG pixels (4)");
11568
11569                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11570                          quantum_info,GrayAlphaQuantum,ping_pixels,
11571                          exception);
11572                   }
11573
11574                 else
11575                   {
11576                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11577                       quantum_info,IndexQuantum,ping_pixels,exception);
11578
11579                     if (logging != MagickFalse && y <= 2)
11580                     {
11581                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11582                           "  Writing row of non-gray pixels (4)");
11583
11584                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11585                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
11586                           (int)ping_pixels[0],(int)ping_pixels[1]);
11587                     }
11588                   }
11589                 png_write_row(ping,ping_pixels);
11590
11591                 status=SetImageProgress(image,SaveImageTag,
11592                   (MagickOffsetType) (pass * image->rows + y),
11593                   num_passes * image->rows);
11594
11595                 if (status == MagickFalse)
11596                   break;
11597               }
11598             }
11599           }
11600         }
11601     }
11602
11603   if (quantum_info != (QuantumInfo *) NULL)
11604     quantum_info=DestroyQuantumInfo(quantum_info);
11605
11606   if (logging != MagickFalse)
11607     {
11608       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11609         "  Wrote PNG image data");
11610
11611       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11612         "    Width: %.20g",(double) ping_width);
11613
11614       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11615         "    Height: %.20g",(double) ping_height);
11616
11617       if (mng_info->write_png_depth)
11618         {
11619           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11620             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
11621         }
11622
11623       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11624         "    PNG bit-depth written: %d",ping_bit_depth);
11625
11626       if (mng_info->write_png_colortype)
11627         {
11628           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11629             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
11630         }
11631
11632       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11633         "    PNG color-type written: %d",ping_color_type);
11634
11635       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11636         "    PNG Interlace method: %d",ping_interlace_method);
11637     }
11638   /*
11639     Generate text chunks after IDAT.
11640   */
11641   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11642   {
11643     ResetImagePropertyIterator(image);
11644     property=GetNextImageProperty(image);
11645     while (property != (const char *) NULL)
11646     {
11647       png_textp
11648         text;
11649
11650       value=GetImageProperty(image,property,exception);
11651
11652       /* Don't write any "png:" or "jpeg:" properties; those are just for
11653        * "identify" or for passing through to another JPEG
11654        */
11655       if ((LocaleNCompare(property,"png:",4) != 0 &&
11656            LocaleNCompare(property,"jpeg:",5) != 0) &&
11657
11658
11659           /* Suppress density and units if we wrote a pHYs chunk */
11660           (ping_exclude_pHYs != MagickFalse      ||
11661           LocaleCompare(property,"density") != 0 ||
11662           LocaleCompare(property,"units") != 0) &&
11663
11664           /* Suppress the IM-generated Date:create and Date:modify */
11665           (ping_exclude_date == MagickFalse      ||
11666           LocaleNCompare(property, "Date:",5) != 0))
11667         {
11668         if (value != (const char *) NULL)
11669           {
11670
11671 #if PNG_LIBPNG_VER >= 10400
11672             text=(png_textp) png_malloc(ping,
11673                  (png_alloc_size_t) sizeof(png_text));
11674 #else
11675             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11676 #endif
11677             text[0].key=(char *) property;
11678             text[0].text=(char *) value;
11679             text[0].text_length=strlen(value);
11680
11681             if (ping_exclude_tEXt != MagickFalse)
11682                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11683
11684             else if (ping_exclude_zTXt != MagickFalse)
11685                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11686
11687             else
11688             {
11689                text[0].compression=image_info->compression == NoCompression ||
11690                  (image_info->compression == UndefinedCompression &&
11691                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11692                  PNG_TEXT_COMPRESSION_zTXt ;
11693             }
11694
11695             if (logging != MagickFalse)
11696               {
11697                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11698                   "  Setting up text chunk");
11699
11700                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11701                   "    keyword: '%s'",text[0].key);
11702               }
11703
11704             png_set_text(ping,ping_info,text,1);
11705             png_free(ping,text);
11706           }
11707         }
11708       property=GetNextImageProperty(image);
11709     }
11710   }
11711
11712   /* write any PNG-chunk-e profiles */
11713   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11714
11715 #if defined(exIf_SUPPORTED) || defined(zxIf_SUPPORTED)
11716   /* write exIf profile */
11717 #ifdef IM
11718   if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
11719 #endif
11720   {
11721 #ifdef GM
11722  ImageProfileIterator
11723    *profile_iterator;
11724
11725  profile_iterator=AllocateImageProfileIterator(image);
11726  if (profile_iterator)
11727    {
11728      const char
11729        *profile_name;
11730
11731      const unsigned char
11732        *profile_info;
11733
11734      size_t
11735        profile_length;
11736
11737      while (NextImageProfile(profile_iterator,&profile_name,&profile_info,
11738                              &profile_length) != MagickFail)
11739        {
11740          if (LocaleCompare(profile_name,"exif") == 0)
11741          {
11742 #else /* IM */
11743       char
11744         *name;
11745
11746       ResetImageProfileIterator(image);
11747
11748       for (name=GetNextImageProfile(image);
11749            name != (const char *) NULL; )
11750         {
11751           if (LocaleCompare(name,"exif") == 0)
11752             {
11753               const StringInfo
11754                 *profile;
11755
11756               profile=GetImageProfile(image,name);
11757
11758               if (profile != (StringInfo *) NULL)
11759                 {
11760 #endif /* GM */
11761                png_uint_32
11762                  length;
11763                unsigned char
11764                  chunk[4],
11765                  *data;
11766
11767 #ifdef IM
11768                StringInfo
11769                  *ping_profile;
11770 #endif
11771                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11772                   "  Have eXIf profile");
11773
11774 #ifdef GM
11775                 data=profile_info;
11776                 length=(png_uint_32) profile_length;
11777 #else /* IM */
11778                ping_profile=CloneStringInfo(profile);
11779                data=GetStringInfoDatum(ping_profile),
11780                length=(png_uint_32) GetStringInfoLength(ping_profile);
11781 #endif /* GM */
11782
11783 #if defined(zxIf_write_SUPPORTED)
11784                /* For now, write uncompressed exIf/eXIf chunk */
11785 #endif
11786
11787 #if 0 /* eXIf chunk is registered */
11788                PNGType(chunk,mng_eXIf);
11789 #else /* eXIf chunk not yet registered; write exIf instead */
11790                PNGType(chunk,mng_exIf);
11791 #endif
11792                if (length < 7)
11793                  break;  /* othewise crashes */
11794
11795                /* skip the "Exif\0\0" JFIF Exif Header ID */
11796                length -= 6;
11797
11798                LogPNGChunk(logging,chunk,length);
11799                (void) WriteBlobMSBULong(image,length);
11800                (void) WriteBlob(image,4,chunk);
11801                (void) WriteBlob(image,length,data+6);
11802                (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),
11803                  data+6, (uInt) length));
11804                break;
11805 #ifdef IM
11806    name=GetNextImageProfile(image);
11807 #endif
11808              }
11809          }
11810      }
11811   }
11812 #endif /* exIf_SUPPORTED) || zxIf_SUPPORTED */
11813
11814   if (logging != MagickFalse)
11815     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11816       "  Writing PNG end info");
11817
11818   png_write_end(ping,ping_info);
11819
11820   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11821     {
11822       if (mng_info->page.x || mng_info->page.y ||
11823           (ping_width != mng_info->page.width) ||
11824           (ping_height != mng_info->page.height))
11825         {
11826           unsigned char
11827             chunk[32];
11828
11829           /*
11830             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11831           */
11832           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11833           PNGType(chunk,mng_FRAM);
11834           LogPNGChunk(logging,mng_FRAM,27L);
11835           chunk[4]=4;
11836           chunk[5]=0;  /* frame name separator (no name) */
11837           chunk[6]=1;  /* flag for changing delay, for next frame only */
11838           chunk[7]=0;  /* flag for changing frame timeout */
11839           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11840           chunk[9]=0;  /* flag for changing frame sync_id */
11841           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11842           chunk[14]=0; /* clipping boundaries delta type */
11843           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11844           PNGLong(chunk+19,
11845              (png_uint_32) (mng_info->page.x + ping_width));
11846           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11847           PNGLong(chunk+27,
11848              (png_uint_32) (mng_info->page.y + ping_height));
11849           (void) WriteBlob(image,31,chunk);
11850           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11851           mng_info->old_framing_mode=4;
11852           mng_info->framing_mode=1;
11853         }
11854
11855       else
11856         mng_info->framing_mode=3;
11857     }
11858   if (mng_info->write_mng && !mng_info->need_fram &&
11859       ((int) image->dispose == 3))
11860      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11861
11862   /*
11863     Free PNG resources.
11864   */
11865
11866   png_destroy_write_struct(&ping,&ping_info);
11867
11868   pixel_info=RelinquishVirtualMemory(pixel_info);
11869
11870   if (ping_have_blob != MagickFalse)
11871      (void) CloseBlob(image);
11872
11873   image_info=DestroyImageInfo(image_info);
11874   image=DestroyImage(image);
11875
11876   /* Store bit depth actually written */
11877   s[0]=(char) ping_bit_depth;
11878   s[1]='\0';
11879
11880   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11881
11882   if (logging != MagickFalse)
11883     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11884       "  exit WriteOnePNGImage()");
11885
11886 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11887   UnlockSemaphoreInfo(ping_semaphore);
11888 #endif
11889
11890    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11891     *    Throwing an Exception when an error occurs.
11892     */
11893
11894   return(MagickTrue);
11895 /*  End write one PNG image */
11896
11897 }
11898
11899 /*
11900 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11901 %                                                                             %
11902 %                                                                             %
11903 %                                                                             %
11904 %   W r i t e P N G I m a g e                                                 %
11905 %                                                                             %
11906 %                                                                             %
11907 %                                                                             %
11908 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11909 %
11910 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11911 %  Multiple-image Network Graphics (MNG) image file.
11912 %
11913 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11914 %
11915 %  The format of the WritePNGImage method is:
11916 %
11917 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11918 %        Image *image,ExceptionInfo *exception)
11919 %
11920 %  A description of each parameter follows:
11921 %
11922 %    o image_info: the image info.
11923 %
11924 %    o image:  The image.
11925 %
11926 %    o exception: return any errors or warnings in this structure.
11927 %
11928 %  Returns MagickTrue on success, MagickFalse on failure.
11929 %
11930 %  Communicating with the PNG encoder:
11931 %
11932 %  While the datastream written is always in PNG format and normally would
11933 %  be given the "png" file extension, this method also writes the following
11934 %  pseudo-formats which are subsets of png:
11935 %
11936 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11937 %               a depth greater than 8, the depth is reduced. If transparency
11938 %               is present, the tRNS chunk must only have values 0 and 255
11939 %               (i.e., transparency is binary: fully opaque or fully
11940 %               transparent).  If other values are present they will be
11941 %               50%-thresholded to binary transparency.  If more than 256
11942 %               colors are present, they will be quantized to the 4-4-4-1,
11943 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11944 %               of any resulting fully-transparent pixels is changed to
11945 %               the image's background color.
11946 %
11947 %               If you want better quantization or dithering of the colors
11948 %               or alpha than that, you need to do it before calling the
11949 %               PNG encoder. The pixels contain 8-bit indices even if
11950 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11951 %               images will be written as indexed PNG files even though the
11952 %               PNG grayscale type might be slightly more efficient.  Please
11953 %               note that writing to the PNG8 format may result in loss
11954 %               of color and alpha data.
11955 %
11956 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11957 %               chunk can be present to convey binary transparency by naming
11958 %               one of the colors as transparent.  The only loss incurred
11959 %               is reduction of sample depth to 8.  If the image has more
11960 %               than one transparent color, has semitransparent pixels, or
11961 %               has an opaque pixel with the same RGB components as the
11962 %               transparent color, an image is not written.
11963 %
11964 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
11965 %               transparency is permitted, i.e., the alpha sample for
11966 %               each pixel can have any value from 0 to 255. The alpha
11967 %               channel is present even if the image is fully opaque.
11968 %               The only loss in data is the reduction of the sample depth
11969 %               to 8.
11970 %
11971 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
11972 %               chunk can be present to convey binary transparency by naming
11973 %               one of the colors as transparent.  If the image has more
11974 %               than one transparent color, has semitransparent pixels, or
11975 %               has an opaque pixel with the same RGB components as the
11976 %               transparent color, an image is not written.
11977 %
11978 %    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
11979 %               transparency is permitted, i.e., the alpha sample for
11980 %               each pixel can have any value from 0 to 65535. The alpha
11981 %               channel is present even if the image is fully opaque.
11982 %
11983 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
11984 %               image, if the input was a PNG, is written.  If these values
11985 %               cannot be found, or if the pixels have been changed in a way
11986 %               that makes this impossible, then "PNG00" falls back to the
11987 %               regular "PNG" format.
11988 %
11989 %    o -define: For more precise control of the PNG output, you can use the
11990 %               Image options "png:bit-depth" and "png:color-type".  These
11991 %               can be set from the commandline with "-define" and also
11992 %               from the application programming interfaces.  The options
11993 %               are case-independent and are converted to lowercase before
11994 %               being passed to this encoder.
11995 %
11996 %               png:color-type can be 0, 2, 3, 4, or 6.
11997 %
11998 %               When png:color-type is 0 (Grayscale), png:bit-depth can
11999 %               be 1, 2, 4, 8, or 16.
12000 %
12001 %               When png:color-type is 2 (RGB), png:bit-depth can
12002 %               be 8 or 16.
12003 %
12004 %               When png:color-type is 3 (Indexed), png:bit-depth can
12005 %               be 1, 2, 4, or 8.  This refers to the number of bits
12006 %               used to store the index.  The color samples always have
12007 %               bit-depth 8 in indexed PNG files.
12008 %
12009 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
12010 %               png:bit-depth can be 8 or 16.
12011 %
12012 %               If the image cannot be written without loss with the
12013 %               requested bit-depth and color-type, a PNG file will not
12014 %               be written, a warning will be issued, and the encoder will
12015 %               return MagickFalse.
12016 %
12017 %  Since image encoders should not be responsible for the "heavy lifting",
12018 %  the user should make sure that ImageMagick has already reduced the
12019 %  image depth and number of colors and limit transparency to binary
12020 %  transparency prior to attempting to write the image with depth, color,
12021 %  or transparency limitations.
12022 %
12023 %  Note that another definition, "png:bit-depth-written" exists, but it
12024 %  is not intended for external use.  It is only used internally by the
12025 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
12026 %
12027 %  It is possible to request that the PNG encoder write previously-formatted
12028 %  ancillary chunks in the output PNG file, using the "-profile" commandline
12029 %  option as shown below or by setting the profile via a programming
12030 %  interface:
12031 %
12032 %     -profile PNG-chunk-x:<file>
12033 %
12034 %  where x is a location flag and <file> is a file containing the chunk
12035 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
12036 %  This encoder will compute the chunk length and CRC, so those must not
12037 %  be included in the file.
12038 %
12039 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
12040 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
12041 %  of the same type, then add a short unique string after the "x" to prevent
12042 %  subsequent profiles from overwriting the preceding ones, e.g.,
12043 %
12044 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
12045 %
12046 %  As of version 6.6.6 the following optimizations are always done:
12047 %
12048 %   o  32-bit depth is reduced to 16.
12049 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
12050 %      high byte and low byte are identical.
12051 %   o  Palette is sorted to remove unused entries and to put a
12052 %      transparent color first, if BUILD_PNG_PALETTE is defined.
12053 %   o  Opaque matte channel is removed when writing an indexed PNG.
12054 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
12055 %      this can be done without loss and a larger bit depth N was not
12056 %      requested via the "-define png:bit-depth=N" option.
12057 %   o  If matte channel is present but only one transparent color is
12058 %      present, RGB+tRNS is written instead of RGBA
12059 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
12060 %      was requested when converting an opaque image).
12061 %
12062 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12063 */
12064 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
12065   Image *image,ExceptionInfo *exception)
12066 {
12067   MagickBooleanType
12068     excluding,
12069     logging,
12070     have_mng_structure,
12071     status;
12072
12073   MngInfo
12074     *mng_info;
12075
12076   const char
12077     *value;
12078
12079   int
12080     source;
12081
12082   /*
12083     Open image file.
12084   */
12085   assert(image_info != (const ImageInfo *) NULL);
12086   assert(image_info->signature == MagickCoreSignature);
12087   assert(image != (Image *) NULL);
12088   assert(image->signature == MagickCoreSignature);
12089   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12090   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
12091   /*
12092     Allocate a MngInfo structure.
12093   */
12094   have_mng_structure=MagickFalse;
12095   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12096
12097   if (mng_info == (MngInfo *) NULL)
12098     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12099
12100   /*
12101     Initialize members of the MngInfo structure.
12102   */
12103   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12104   mng_info->image=image;
12105   mng_info->equal_backgrounds=MagickTrue;
12106   have_mng_structure=MagickTrue;
12107
12108   /* See if user has requested a specific PNG subformat */
12109
12110   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12111   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12112   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12113   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
12114   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
12115
12116   value=GetImageOption(image_info,"png:format");
12117
12118   if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
12119     {
12120       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12121          "  Format=%s",value);
12122
12123       mng_info->write_png8 = MagickFalse;
12124       mng_info->write_png24 = MagickFalse;
12125       mng_info->write_png32 = MagickFalse;
12126       mng_info->write_png48 = MagickFalse;
12127       mng_info->write_png64 = MagickFalse;
12128
12129       if (LocaleCompare(value,"png8") == 0)
12130         mng_info->write_png8 = MagickTrue;
12131
12132       else if (LocaleCompare(value,"png24") == 0)
12133         mng_info->write_png24 = MagickTrue;
12134
12135       else if (LocaleCompare(value,"png32") == 0)
12136         mng_info->write_png32 = MagickTrue;
12137
12138       else if (LocaleCompare(value,"png48") == 0)
12139         mng_info->write_png48 = MagickTrue;
12140
12141       else if (LocaleCompare(value,"png64") == 0)
12142         mng_info->write_png64 = MagickTrue;
12143
12144       else if ((LocaleCompare(value,"png00") == 0) ||
12145          LocaleCompare(image_info->magick,"PNG00") == 0)
12146         {
12147           /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
12148           value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
12149
12150           if (value != (char *) NULL)
12151             {
12152               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12153                  "  png00 inherited bit depth=%s",value);
12154
12155               if (LocaleCompare(value,"1") == 0)
12156                 mng_info->write_png_depth = 1;
12157
12158               else if (LocaleCompare(value,"2") == 0)
12159                 mng_info->write_png_depth = 2;
12160
12161               else if (LocaleCompare(value,"4") == 0)
12162                 mng_info->write_png_depth = 4;
12163
12164               else if (LocaleCompare(value,"8") == 0)
12165                 mng_info->write_png_depth = 8;
12166
12167               else if (LocaleCompare(value,"16") == 0)
12168                 mng_info->write_png_depth = 16;
12169             }
12170
12171           value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
12172
12173           if (value != (char *) NULL)
12174             {
12175               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12176                  "  png00 inherited color type=%s",value);
12177
12178               if (LocaleCompare(value,"0") == 0)
12179                 mng_info->write_png_colortype = 1;
12180
12181               else if (LocaleCompare(value,"2") == 0)
12182                 mng_info->write_png_colortype = 3;
12183
12184               else if (LocaleCompare(value,"3") == 0)
12185                 mng_info->write_png_colortype = 4;
12186
12187               else if (LocaleCompare(value,"4") == 0)
12188                 mng_info->write_png_colortype = 5;
12189
12190               else if (LocaleCompare(value,"6") == 0)
12191                 mng_info->write_png_colortype = 7;
12192             }
12193         }
12194     }
12195
12196   if (mng_info->write_png8)
12197     {
12198       mng_info->write_png_colortype = /* 3 */ 4;
12199       mng_info->write_png_depth = 8;
12200       image->depth = 8;
12201     }
12202
12203   if (mng_info->write_png24)
12204     {
12205       mng_info->write_png_colortype = /* 2 */ 3;
12206       mng_info->write_png_depth = 8;
12207       image->depth = 8;
12208
12209       if (image->alpha_trait != UndefinedPixelTrait)
12210         (void) SetImageType(image,TrueColorAlphaType,exception);
12211
12212       else
12213         (void) SetImageType(image,TrueColorType,exception);
12214
12215       (void) SyncImage(image,exception);
12216     }
12217
12218   if (mng_info->write_png32)
12219     {
12220       mng_info->write_png_colortype = /* 6 */  7;
12221       mng_info->write_png_depth = 8;
12222       image->depth = 8;
12223       image->alpha_trait = BlendPixelTrait;
12224
12225       (void) SetImageType(image,TrueColorAlphaType,exception);
12226       (void) SyncImage(image,exception);
12227     }
12228
12229   if (mng_info->write_png48)
12230     {
12231       mng_info->write_png_colortype = /* 2 */ 3;
12232       mng_info->write_png_depth = 16;
12233       image->depth = 16;
12234
12235       if (image->alpha_trait != UndefinedPixelTrait)
12236         (void) SetImageType(image,TrueColorAlphaType,exception);
12237
12238       else
12239         (void) SetImageType(image,TrueColorType,exception);
12240
12241       (void) SyncImage(image,exception);
12242     }
12243
12244   if (mng_info->write_png64)
12245     {
12246       mng_info->write_png_colortype = /* 6 */  7;
12247       mng_info->write_png_depth = 16;
12248       image->depth = 16;
12249       image->alpha_trait = BlendPixelTrait;
12250
12251       (void) SetImageType(image,TrueColorAlphaType,exception);
12252       (void) SyncImage(image,exception);
12253     }
12254
12255   value=GetImageOption(image_info,"png:bit-depth");
12256
12257   if (value != (char *) NULL)
12258     {
12259       if (LocaleCompare(value,"1") == 0)
12260         mng_info->write_png_depth = 1;
12261
12262       else if (LocaleCompare(value,"2") == 0)
12263         mng_info->write_png_depth = 2;
12264
12265       else if (LocaleCompare(value,"4") == 0)
12266         mng_info->write_png_depth = 4;
12267
12268       else if (LocaleCompare(value,"8") == 0)
12269         mng_info->write_png_depth = 8;
12270
12271       else if (LocaleCompare(value,"16") == 0)
12272         mng_info->write_png_depth = 16;
12273
12274       else
12275         (void) ThrowMagickException(exception,
12276              GetMagickModule(),CoderWarning,
12277              "ignoring invalid defined png:bit-depth",
12278              "=%s",value);
12279
12280       if (logging != MagickFalse)
12281         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12282           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
12283     }
12284
12285   value=GetImageOption(image_info,"png:color-type");
12286
12287   if (value != (char *) NULL)
12288     {
12289       /* We must store colortype+1 because 0 is a valid colortype */
12290       if (LocaleCompare(value,"0") == 0)
12291         mng_info->write_png_colortype = 1;
12292
12293       else if (LocaleCompare(value,"1") == 0)
12294         mng_info->write_png_colortype = 2;
12295
12296       else if (LocaleCompare(value,"2") == 0)
12297         mng_info->write_png_colortype = 3;
12298
12299       else if (LocaleCompare(value,"3") == 0)
12300         mng_info->write_png_colortype = 4;
12301
12302       else if (LocaleCompare(value,"4") == 0)
12303         mng_info->write_png_colortype = 5;
12304
12305       else if (LocaleCompare(value,"6") == 0)
12306         mng_info->write_png_colortype = 7;
12307
12308       else
12309         (void) ThrowMagickException(exception,
12310              GetMagickModule(),CoderWarning,
12311              "ignoring invalid defined png:color-type",
12312              "=%s",value);
12313
12314       if (logging != MagickFalse)
12315         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12316           "  png:color-type=%d was defined.\n",
12317           mng_info->write_png_colortype-1);
12318     }
12319
12320   /* Check for chunks to be excluded:
12321    *
12322    * The default is to not exclude any known chunks except for any
12323    * listed in the "unused_chunks" array, above.
12324    *
12325    * Chunks can be listed for exclusion via a "png:exclude-chunk"
12326    * define (in the image properties or in the image artifacts)
12327    * or via a mng_info member.  For convenience, in addition
12328    * to or instead of a comma-separated list of chunks, the
12329    * "exclude-chunk" string can be simply "all" or "none".
12330    *
12331    * The exclude-chunk define takes priority over the mng_info.
12332    *
12333    * A "png:include-chunk" define takes  priority over both the
12334    * mng_info and the "png:exclude-chunk" define.  Like the
12335    * "exclude-chunk" string, it can define "all" or "none" as
12336    * well as a comma-separated list.  Chunks that are unknown to
12337    * ImageMagick are always excluded, regardless of their "copy-safe"
12338    * status according to the PNG specification, and even if they
12339    * appear in the "include-chunk" list. Such defines appearing among
12340    * the image options take priority over those found among the image
12341    * artifacts.
12342    *
12343    * Finally, all chunks listed in the "unused_chunks" array are
12344    * automatically excluded, regardless of the other instructions
12345    * or lack thereof.
12346    *
12347    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
12348    * will not be written and the gAMA chunk will only be written if it
12349    * is not between .45 and .46, or approximately (1.0/2.2).
12350    *
12351    * If you exclude tRNS and the image has transparency, the colortype
12352    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
12353    *
12354    * The -strip option causes StripImage() to set the png:include-chunk
12355    * artifact to "none,trns,gama".
12356    */
12357
12358   mng_info->ping_exclude_bKGD=MagickFalse;
12359   mng_info->ping_exclude_caNv=MagickFalse;
12360   mng_info->ping_exclude_cHRM=MagickFalse;
12361   mng_info->ping_exclude_date=MagickFalse;
12362   mng_info->ping_exclude_eXIf=MagickFalse;
12363   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
12364   mng_info->ping_exclude_gAMA=MagickFalse;
12365   mng_info->ping_exclude_iCCP=MagickFalse;
12366   /* mng_info->ping_exclude_iTXt=MagickFalse; */
12367   mng_info->ping_exclude_oFFs=MagickFalse;
12368   mng_info->ping_exclude_pHYs=MagickFalse;
12369   mng_info->ping_exclude_sRGB=MagickFalse;
12370   mng_info->ping_exclude_tEXt=MagickFalse;
12371   mng_info->ping_exclude_tIME=MagickFalse;
12372   mng_info->ping_exclude_tRNS=MagickFalse;
12373   mng_info->ping_exclude_vpAg=MagickFalse;
12374   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
12375   mng_info->ping_exclude_zTXt=MagickFalse;
12376
12377   mng_info->ping_preserve_colormap=MagickFalse;
12378
12379   value=GetImageOption(image_info,"png:preserve-colormap");
12380   if (value == NULL)
12381      value=GetImageArtifact(image,"png:preserve-colormap");
12382   if (value != NULL)
12383      mng_info->ping_preserve_colormap=MagickTrue;
12384
12385   mng_info->ping_preserve_iCCP=MagickFalse;
12386
12387   value=GetImageOption(image_info,"png:preserve-iCCP");
12388   if (value == NULL)
12389      value=GetImageArtifact(image,"png:preserve-iCCP");
12390   if (value != NULL)
12391      mng_info->ping_preserve_iCCP=MagickTrue;
12392
12393   /* These compression-level, compression-strategy, and compression-filter
12394    * defines take precedence over values from the -quality option.
12395    */
12396   value=GetImageOption(image_info,"png:compression-level");
12397   if (value == NULL)
12398      value=GetImageArtifact(image,"png:compression-level");
12399   if (value != NULL)
12400   {
12401       /* We have to add 1 to everything because 0 is a valid input,
12402        * and we want to use 0 (the default) to mean undefined.
12403        */
12404       if (LocaleCompare(value,"0") == 0)
12405         mng_info->write_png_compression_level = 1;
12406
12407       else if (LocaleCompare(value,"1") == 0)
12408         mng_info->write_png_compression_level = 2;
12409
12410       else if (LocaleCompare(value,"2") == 0)
12411         mng_info->write_png_compression_level = 3;
12412
12413       else if (LocaleCompare(value,"3") == 0)
12414         mng_info->write_png_compression_level = 4;
12415
12416       else if (LocaleCompare(value,"4") == 0)
12417         mng_info->write_png_compression_level = 5;
12418
12419       else if (LocaleCompare(value,"5") == 0)
12420         mng_info->write_png_compression_level = 6;
12421
12422       else if (LocaleCompare(value,"6") == 0)
12423         mng_info->write_png_compression_level = 7;
12424
12425       else if (LocaleCompare(value,"7") == 0)
12426         mng_info->write_png_compression_level = 8;
12427
12428       else if (LocaleCompare(value,"8") == 0)
12429         mng_info->write_png_compression_level = 9;
12430
12431       else if (LocaleCompare(value,"9") == 0)
12432         mng_info->write_png_compression_level = 10;
12433
12434       else
12435         (void) ThrowMagickException(exception,
12436              GetMagickModule(),CoderWarning,
12437              "ignoring invalid defined png:compression-level",
12438              "=%s",value);
12439     }
12440
12441   value=GetImageOption(image_info,"png:compression-strategy");
12442   if (value == NULL)
12443      value=GetImageArtifact(image,"png:compression-strategy");
12444   if (value != NULL)
12445   {
12446       if (LocaleCompare(value,"0") == 0)
12447         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12448
12449       else if (LocaleCompare(value,"1") == 0)
12450         mng_info->write_png_compression_strategy = Z_FILTERED+1;
12451
12452       else if (LocaleCompare(value,"2") == 0)
12453         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
12454
12455       else if (LocaleCompare(value,"3") == 0)
12456 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
12457         mng_info->write_png_compression_strategy = Z_RLE+1;
12458 #else
12459         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12460 #endif
12461
12462       else if (LocaleCompare(value,"4") == 0)
12463 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
12464         mng_info->write_png_compression_strategy = Z_FIXED+1;
12465 #else
12466         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12467 #endif
12468
12469       else
12470         (void) ThrowMagickException(exception,
12471              GetMagickModule(),CoderWarning,
12472              "ignoring invalid defined png:compression-strategy",
12473              "=%s",value);
12474     }
12475
12476   value=GetImageOption(image_info,"png:compression-filter");
12477   if (value == NULL)
12478      value=GetImageArtifact(image,"png:compression-filter");
12479   if (value != NULL)
12480   {
12481       /* To do: combinations of filters allowed by libpng
12482        * masks 0x08 through 0xf8
12483        *
12484        * Implement this as a comma-separated list of 0,1,2,3,4,5
12485        * where 5 is a special case meaning PNG_ALL_FILTERS.
12486        */
12487
12488       if (LocaleCompare(value,"0") == 0)
12489         mng_info->write_png_compression_filter = 1;
12490
12491       else if (LocaleCompare(value,"1") == 0)
12492         mng_info->write_png_compression_filter = 2;
12493
12494       else if (LocaleCompare(value,"2") == 0)
12495         mng_info->write_png_compression_filter = 3;
12496
12497       else if (LocaleCompare(value,"3") == 0)
12498         mng_info->write_png_compression_filter = 4;
12499
12500       else if (LocaleCompare(value,"4") == 0)
12501         mng_info->write_png_compression_filter = 5;
12502
12503       else if (LocaleCompare(value,"5") == 0)
12504         mng_info->write_png_compression_filter = 6;
12505
12506       else
12507         (void) ThrowMagickException(exception,
12508              GetMagickModule(),CoderWarning,
12509              "ignoring invalid defined png:compression-filter",
12510              "=%s",value);
12511   }
12512
12513   for (source=0; source<8; source++)
12514   {
12515     value = NULL;
12516
12517     if (source == 0)
12518       value=GetImageOption(image_info,"png:exclude-chunks");
12519
12520     if (source == 1)
12521       value=GetImageArtifact(image,"png:exclude-chunks");
12522
12523     if (source == 2)
12524       value=GetImageOption(image_info,"png:exclude-chunk");
12525
12526     if (source == 3)
12527       value=GetImageArtifact(image,"png:exclude-chunk");
12528
12529     if (source == 4)
12530       value=GetImageOption(image_info,"png:include-chunks");
12531
12532     if (source == 5)
12533       value=GetImageArtifact(image,"png:include-chunks");
12534
12535     if (source == 6)
12536       value=GetImageOption(image_info,"png:include-chunk");
12537
12538     if (source == 7)
12539       value=GetImageArtifact(image,"png:include-chunk");
12540
12541     if (value == NULL)
12542        continue;
12543
12544     if (source < 4)
12545       excluding = MagickTrue;
12546     else
12547       excluding = MagickFalse;
12548
12549     if (logging != MagickFalse)
12550       {
12551         if (source == 0 || source == 2)
12552            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12553               "  png:exclude-chunk=%s found in image options.\n", value);
12554         else if (source == 1 || source == 3)
12555            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12556               "  png:exclude-chunk=%s found in image artifacts.\n", value);
12557         else if (source == 4 || source == 6)
12558            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12559               "  png:include-chunk=%s found in image options.\n", value);
12560         else /* if (source == 5 || source == 7) */
12561            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12562               "  png:include-chunk=%s found in image artifacts.\n", value);
12563       }
12564
12565     if (IsOptionMember("all",value) != MagickFalse)
12566       {
12567         mng_info->ping_exclude_bKGD=excluding;
12568         mng_info->ping_exclude_caNv=excluding;
12569         mng_info->ping_exclude_cHRM=excluding;
12570         mng_info->ping_exclude_date=excluding;
12571         mng_info->ping_exclude_EXIF=excluding;
12572         mng_info->ping_exclude_eXIf=excluding;
12573         mng_info->ping_exclude_gAMA=excluding;
12574         mng_info->ping_exclude_iCCP=excluding;
12575         /* mng_info->ping_exclude_iTXt=excluding; */
12576         mng_info->ping_exclude_oFFs=excluding;
12577         mng_info->ping_exclude_pHYs=excluding;
12578         mng_info->ping_exclude_sRGB=excluding;
12579         mng_info->ping_exclude_tEXt=excluding;
12580         mng_info->ping_exclude_tIME=excluding;
12581         mng_info->ping_exclude_tRNS=excluding;
12582         mng_info->ping_exclude_vpAg=excluding;
12583         mng_info->ping_exclude_zCCP=excluding;
12584         mng_info->ping_exclude_zTXt=excluding;
12585       }
12586
12587     if (IsOptionMember("none",value) != MagickFalse)
12588       {
12589         mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12590           MagickTrue;
12591         mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
12592           MagickTrue;
12593         mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12594           MagickTrue;
12595         mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12596           MagickTrue;
12597         mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
12598           MagickTrue;
12599         mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12600           MagickTrue;
12601         mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12602           MagickTrue;
12603         mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12604           MagickTrue;
12605         /* mng_info->ping_exclude_iTXt=!excluding; */
12606         mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12607           MagickTrue;
12608         mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12609           MagickTrue;
12610         mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12611           MagickTrue;
12612         mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12613           MagickTrue;
12614         mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12615           MagickTrue;
12616         mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12617           MagickTrue;
12618         mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
12619           MagickTrue;
12620         mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12621           MagickTrue;
12622         mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12623           MagickTrue;
12624       }
12625
12626     if (IsOptionMember("bkgd",value) != MagickFalse)
12627       mng_info->ping_exclude_bKGD=excluding;
12628
12629     if (IsOptionMember("caNv",value) != MagickFalse)
12630       mng_info->ping_exclude_caNv=excluding;
12631
12632     if (IsOptionMember("chrm",value) != MagickFalse)
12633       mng_info->ping_exclude_cHRM=excluding;
12634
12635     if (IsOptionMember("date",value) != MagickFalse)
12636       mng_info->ping_exclude_date=excluding;
12637
12638     if (IsOptionMember("exif",value) != MagickFalse)
12639       {
12640         mng_info->ping_exclude_EXIF=excluding;
12641         mng_info->ping_exclude_eXIf=excluding;
12642       }
12643
12644     if (IsOptionMember("gama",value) != MagickFalse)
12645       mng_info->ping_exclude_gAMA=excluding;
12646
12647     if (IsOptionMember("iccp",value) != MagickFalse)
12648       mng_info->ping_exclude_iCCP=excluding;
12649
12650 #if 0
12651     if (IsOptionMember("itxt",value) != MagickFalse)
12652       mng_info->ping_exclude_iTXt=excluding;
12653 #endif
12654
12655     if (IsOptionMember("offs",value) != MagickFalse)
12656       mng_info->ping_exclude_oFFs=excluding;
12657
12658     if (IsOptionMember("phys",value) != MagickFalse)
12659       mng_info->ping_exclude_pHYs=excluding;
12660
12661     if (IsOptionMember("srgb",value) != MagickFalse)
12662       mng_info->ping_exclude_sRGB=excluding;
12663
12664     if (IsOptionMember("text",value) != MagickFalse)
12665       mng_info->ping_exclude_tEXt=excluding;
12666
12667     if (IsOptionMember("time",value) != MagickFalse)
12668       mng_info->ping_exclude_tIME=excluding;
12669
12670     if (IsOptionMember("trns",value) != MagickFalse)
12671       mng_info->ping_exclude_tRNS=excluding;
12672
12673     if (IsOptionMember("vpag",value) != MagickFalse)
12674       mng_info->ping_exclude_vpAg=excluding;
12675
12676     if (IsOptionMember("zccp",value) != MagickFalse)
12677       mng_info->ping_exclude_zCCP=excluding;
12678
12679     if (IsOptionMember("ztxt",value) != MagickFalse)
12680       mng_info->ping_exclude_zTXt=excluding;
12681   }
12682
12683   if (logging != MagickFalse)
12684   {
12685     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12686       "  Chunks to be excluded from the output png:");
12687     if (mng_info->ping_exclude_bKGD != MagickFalse)
12688       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12689           "    bKGD");
12690     if (mng_info->ping_exclude_caNv != MagickFalse)
12691       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12692           "    caNv");
12693     if (mng_info->ping_exclude_cHRM != MagickFalse)
12694       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12695           "    cHRM");
12696     if (mng_info->ping_exclude_date != MagickFalse)
12697       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12698           "    date");
12699     if (mng_info->ping_exclude_EXIF != MagickFalse)
12700       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12701           "    EXIF");
12702     if (mng_info->ping_exclude_eXIf != MagickFalse)
12703       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12704           "    eXIf");
12705     if (mng_info->ping_exclude_gAMA != MagickFalse)
12706       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12707           "    gAMA");
12708     if (mng_info->ping_exclude_iCCP != MagickFalse)
12709       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12710           "    iCCP");
12711 #if 0
12712     if (mng_info->ping_exclude_iTXt != MagickFalse)
12713       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12714           "    iTXt");
12715 #endif
12716
12717     if (mng_info->ping_exclude_oFFs != MagickFalse)
12718       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12719           "    oFFs");
12720     if (mng_info->ping_exclude_pHYs != MagickFalse)
12721       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12722           "    pHYs");
12723     if (mng_info->ping_exclude_sRGB != MagickFalse)
12724       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12725           "    sRGB");
12726     if (mng_info->ping_exclude_tEXt != MagickFalse)
12727       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12728           "    tEXt");
12729     if (mng_info->ping_exclude_tIME != MagickFalse)
12730       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12731           "    tIME");
12732     if (mng_info->ping_exclude_tRNS != MagickFalse)
12733       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12734           "    tRNS");
12735     if (mng_info->ping_exclude_vpAg != MagickFalse)
12736       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12737           "    vpAg");
12738     if (mng_info->ping_exclude_zCCP != MagickFalse)
12739       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12740           "    zCCP");
12741     if (mng_info->ping_exclude_zTXt != MagickFalse)
12742       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12743           "    zTXt");
12744   }
12745
12746   mng_info->need_blob = MagickTrue;
12747
12748   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12749
12750   MngInfoFreeStruct(mng_info,&have_mng_structure);
12751
12752   if (logging != MagickFalse)
12753     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12754
12755   return(status);
12756 }
12757
12758 #if defined(JNG_SUPPORTED)
12759
12760 /* Write one JNG image */
12761 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12762    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12763 {
12764   Image
12765     *jpeg_image;
12766
12767   ImageInfo
12768     *jpeg_image_info;
12769
12770   MagickBooleanType
12771     logging,
12772     status;
12773
12774   size_t
12775     length;
12776
12777   unsigned char
12778     *blob,
12779     chunk[80],
12780     *p;
12781
12782   unsigned int
12783     jng_alpha_compression_method,
12784     jng_alpha_sample_depth,
12785     jng_color_type,
12786     transparent;
12787
12788   size_t
12789     jng_alpha_quality,
12790     jng_quality;
12791
12792   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12793     "  Enter WriteOneJNGImage()");
12794
12795   blob=(unsigned char *) NULL;
12796   jpeg_image=(Image *) NULL;
12797   jpeg_image_info=(ImageInfo *) NULL;
12798   length=0;
12799
12800   status=MagickTrue;
12801   transparent=image_info->type==GrayscaleAlphaType ||
12802      image_info->type==TrueColorAlphaType ||
12803      image->alpha_trait != UndefinedPixelTrait;
12804
12805   jng_alpha_sample_depth = 0;
12806
12807   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12808
12809   jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12810
12811   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12812       image_info->quality;
12813
12814   if (jng_alpha_quality >= 1000)
12815     jng_alpha_quality /= 1000;
12816
12817   length=0;
12818
12819   if (transparent != 0)
12820     {
12821       jng_color_type=14;
12822
12823       /* Create JPEG blob, image, and image_info */
12824       if (logging != MagickFalse)
12825         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12826           "  Creating jpeg_image_info for alpha.");
12827
12828       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12829
12830       if (jpeg_image_info == (ImageInfo *) NULL)
12831         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12832
12833       if (logging != MagickFalse)
12834         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12835           "  Creating jpeg_image.");
12836
12837       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12838       if (jpeg_image == (Image *) NULL)
12839         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12840       (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12841       jpeg_image->alpha_trait=UndefinedPixelTrait;
12842       jpeg_image->quality=jng_alpha_quality;
12843       jpeg_image_info->type=GrayscaleType;
12844       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12845       (void) AcquireUniqueFilename(jpeg_image->filename);
12846       (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12847         "%s",jpeg_image->filename);
12848     }
12849   else
12850     {
12851       jng_alpha_compression_method=0;
12852       jng_color_type=10;
12853       jng_alpha_sample_depth=0;
12854     }
12855
12856   /* To do: check bit depth of PNG alpha channel */
12857
12858   /* Check if image is grayscale. */
12859   if (image_info->type != TrueColorAlphaType && image_info->type !=
12860     TrueColorType && SetImageGray(image,exception))
12861     jng_color_type-=2;
12862
12863   if (logging != MagickFalse)
12864     {
12865         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12866           "    JNG Quality           = %d",(int) jng_quality);
12867         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12868           "    JNG Color Type        = %d",jng_color_type);
12869         if (transparent != 0)
12870           {
12871             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12872               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12873             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12874               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12875             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12876               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12877           }
12878     }
12879
12880   if (transparent != 0)
12881     {
12882       if (jng_alpha_compression_method==0)
12883         {
12884           const char
12885             *value;
12886
12887           /* Encode alpha as a grayscale PNG blob */
12888           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12889             exception);
12890           if (status == MagickFalse)
12891             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12892
12893           if (logging != MagickFalse)
12894             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12895               "  Creating PNG blob.");
12896
12897           (void) CopyMagickString(jpeg_image_info->magick,"PNG",
12898              MagickPathExtent);
12899           (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12900           jpeg_image_info->interlace=NoInterlace;
12901
12902           /* Exclude all ancillary chunks */
12903           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12904
12905           blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12906             &length,exception);
12907
12908           /* Retrieve sample depth used */
12909           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12910           if (value != (char *) NULL)
12911             jng_alpha_sample_depth= (unsigned int) value[0];
12912         }
12913       else
12914         {
12915           /* Encode alpha as a grayscale JPEG blob */
12916
12917           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12918             exception);
12919           if (status == MagickFalse)
12920             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12921
12922           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",
12923             MagickPathExtent);
12924           (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12925           jpeg_image_info->interlace=NoInterlace;
12926           if (logging != MagickFalse)
12927             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12928               "  Creating blob.");
12929           blob=(unsigned char *) ImageToBlob(jpeg_image_info,
12930              jpeg_image,&length,
12931            exception);
12932           jng_alpha_sample_depth=8;
12933
12934           if (logging != MagickFalse)
12935             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12936               "  Successfully read jpeg_image into a blob, length=%.20g.",
12937               (double) length);
12938
12939         }
12940       /* Destroy JPEG image and image_info */
12941       jpeg_image=DestroyImage(jpeg_image);
12942       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12943       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12944     }
12945
12946   /* Write JHDR chunk */
12947   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12948   PNGType(chunk,mng_JHDR);
12949   LogPNGChunk(logging,mng_JHDR,16L);
12950   PNGLong(chunk+4,(png_uint_32) image->columns);
12951   PNGLong(chunk+8,(png_uint_32) image->rows);
12952   chunk[12]=jng_color_type;
12953   chunk[13]=8;  /* sample depth */
12954   chunk[14]=8; /*jng_image_compression_method */
12955   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12956   chunk[16]=jng_alpha_sample_depth;
12957   chunk[17]=jng_alpha_compression_method;
12958   chunk[18]=0; /*jng_alpha_filter_method */
12959   chunk[19]=0; /*jng_alpha_interlace_method */
12960   (void) WriteBlob(image,20,chunk);
12961   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12962   if (logging != MagickFalse)
12963     {
12964       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12965         "    JNG width:%15lu",(unsigned long) image->columns);
12966
12967       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12968         "    JNG height:%14lu",(unsigned long) image->rows);
12969
12970       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12971         "    JNG color type:%10d",jng_color_type);
12972
12973       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12974         "    JNG sample depth:%8d",8);
12975
12976       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12977         "    JNG compression:%9d",8);
12978
12979       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12980         "    JNG interlace:%11d",0);
12981
12982       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12983         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
12984
12985       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12986         "    JNG alpha compression:%3d",jng_alpha_compression_method);
12987
12988       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12989         "    JNG alpha filter:%8d",0);
12990
12991       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12992         "    JNG alpha interlace:%5d",0);
12993     }
12994
12995   /* Write any JNG-chunk-b profiles */
12996   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12997
12998   /*
12999      Write leading ancillary chunks
13000   */
13001
13002   if (transparent != 0)
13003   {
13004     /*
13005       Write JNG bKGD chunk
13006     */
13007
13008     unsigned char
13009       blue,
13010       green,
13011       red;
13012
13013     ssize_t
13014       num_bytes;
13015
13016     if (jng_color_type == 8 || jng_color_type == 12)
13017       num_bytes=6L;
13018     else
13019       num_bytes=10L;
13020     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
13021     PNGType(chunk,mng_bKGD);
13022     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
13023     red=ScaleQuantumToChar(image->background_color.red);
13024     green=ScaleQuantumToChar(image->background_color.green);
13025     blue=ScaleQuantumToChar(image->background_color.blue);
13026     *(chunk+4)=0;
13027     *(chunk+5)=red;
13028     *(chunk+6)=0;
13029     *(chunk+7)=green;
13030     *(chunk+8)=0;
13031     *(chunk+9)=blue;
13032     (void) WriteBlob(image,(size_t) num_bytes,chunk);
13033     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
13034   }
13035
13036   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
13037     {
13038       /*
13039         Write JNG sRGB chunk
13040       */
13041       (void) WriteBlobMSBULong(image,1L);
13042       PNGType(chunk,mng_sRGB);
13043       LogPNGChunk(logging,mng_sRGB,1L);
13044
13045       if (image->rendering_intent != UndefinedIntent)
13046         chunk[4]=(unsigned char)
13047           Magick_RenderingIntent_to_PNG_RenderingIntent(
13048           (image->rendering_intent));
13049
13050       else
13051         chunk[4]=(unsigned char)
13052           Magick_RenderingIntent_to_PNG_RenderingIntent(
13053           (PerceptualIntent));
13054
13055       (void) WriteBlob(image,5,chunk);
13056       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13057     }
13058   else
13059     {
13060       if (image->gamma != 0.0)
13061         {
13062           /*
13063              Write JNG gAMA chunk
13064           */
13065           (void) WriteBlobMSBULong(image,4L);
13066           PNGType(chunk,mng_gAMA);
13067           LogPNGChunk(logging,mng_gAMA,4L);
13068           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13069           (void) WriteBlob(image,8,chunk);
13070           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13071         }
13072
13073       if ((mng_info->equal_chrms == MagickFalse) &&
13074           (image->chromaticity.red_primary.x != 0.0))
13075         {
13076           PrimaryInfo
13077             primary;
13078
13079           /*
13080              Write JNG cHRM chunk
13081           */
13082           (void) WriteBlobMSBULong(image,32L);
13083           PNGType(chunk,mng_cHRM);
13084           LogPNGChunk(logging,mng_cHRM,32L);
13085           primary=image->chromaticity.white_point;
13086           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13087           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13088           primary=image->chromaticity.red_primary;
13089           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13090           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13091           primary=image->chromaticity.green_primary;
13092           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13093           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13094           primary=image->chromaticity.blue_primary;
13095           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13096           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13097           (void) WriteBlob(image,36,chunk);
13098           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13099         }
13100     }
13101
13102   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
13103     {
13104       /*
13105          Write JNG pHYs chunk
13106       */
13107       (void) WriteBlobMSBULong(image,9L);
13108       PNGType(chunk,mng_pHYs);
13109       LogPNGChunk(logging,mng_pHYs,9L);
13110       if (image->units == PixelsPerInchResolution)
13111         {
13112           PNGLong(chunk+4,(png_uint_32)
13113             (image->resolution.x*100.0/2.54+0.5));
13114
13115           PNGLong(chunk+8,(png_uint_32)
13116             (image->resolution.y*100.0/2.54+0.5));
13117
13118           chunk[12]=1;
13119         }
13120
13121       else
13122         {
13123           if (image->units == PixelsPerCentimeterResolution)
13124             {
13125               PNGLong(chunk+4,(png_uint_32)
13126                 (image->resolution.x*100.0+0.5));
13127
13128               PNGLong(chunk+8,(png_uint_32)
13129                 (image->resolution.y*100.0+0.5));
13130
13131               chunk[12]=1;
13132             }
13133
13134           else
13135             {
13136               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13137               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13138               chunk[12]=0;
13139             }
13140         }
13141       (void) WriteBlob(image,13,chunk);
13142       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13143     }
13144
13145   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
13146     {
13147       /*
13148          Write JNG oFFs chunk
13149       */
13150       (void) WriteBlobMSBULong(image,9L);
13151       PNGType(chunk,mng_oFFs);
13152       LogPNGChunk(logging,mng_oFFs,9L);
13153       PNGsLong(chunk+4,(ssize_t) (image->page.x));
13154       PNGsLong(chunk+8,(ssize_t) (image->page.y));
13155       chunk[12]=0;
13156       (void) WriteBlob(image,13,chunk);
13157       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13158     }
13159   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
13160     {
13161        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
13162        PNGType(chunk,mng_vpAg);
13163        LogPNGChunk(logging,mng_vpAg,9L);
13164        PNGLong(chunk+4,(png_uint_32) image->page.width);
13165        PNGLong(chunk+8,(png_uint_32) image->page.height);
13166        chunk[12]=0;   /* unit = pixels */
13167        (void) WriteBlob(image,13,chunk);
13168        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13169     }
13170
13171   if (transparent != 0)
13172     {
13173       if (jng_alpha_compression_method==0)
13174         {
13175           register ssize_t
13176             i;
13177
13178           size_t
13179             len;
13180
13181           /* Write IDAT chunk header */
13182           if (logging != MagickFalse)
13183             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13184               "  Write IDAT chunks from blob, length=%.20g.",(double)
13185               length);
13186
13187           /* Copy IDAT chunks */
13188           len=0;
13189           p=blob+8;
13190           for (i=8; i<(ssize_t) length; i+=len+12)
13191           {
13192             len=(size_t) (*p) << 24;
13193             len|=(size_t) (*(p+1)) << 16;
13194             len|=(size_t) (*(p+2)) << 8;
13195             len|=(size_t) (*(p+3));
13196             p+=4;
13197
13198             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
13199               {
13200                 /* Found an IDAT chunk. */
13201                 (void) WriteBlobMSBULong(image,len);
13202                 LogPNGChunk(logging,mng_IDAT,len);
13203                 (void) WriteBlob(image,len+4,p);
13204                 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
13205               }
13206
13207             else
13208               {
13209                 if (logging != MagickFalse)
13210                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13211                     "    Skipping %c%c%c%c chunk, length=%.20g.",
13212                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
13213               }
13214             p+=(8+len);
13215           }
13216         }
13217       else if (length != 0)
13218         {
13219           /* Write JDAA chunk header */
13220           if (logging != MagickFalse)
13221             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13222               "  Write JDAA chunk, length=%.20g.",(double) length);
13223           (void) WriteBlobMSBULong(image,(size_t) length);
13224           PNGType(chunk,mng_JDAA);
13225           LogPNGChunk(logging,mng_JDAA,length);
13226           /* Write JDAT chunk(s) data */
13227           (void) WriteBlob(image,4,chunk);
13228           (void) WriteBlob(image,length,blob);
13229           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
13230              (uInt) length));
13231         }
13232       blob=(unsigned char *) RelinquishMagickMemory(blob);
13233     }
13234
13235   /* Encode image as a JPEG blob */
13236   if (logging != MagickFalse)
13237     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13238       "  Creating jpeg_image_info.");
13239   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
13240   if (jpeg_image_info == (ImageInfo *) NULL)
13241     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13242
13243   if (logging != MagickFalse)
13244     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13245       "  Creating jpeg_image.");
13246
13247   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
13248   if (jpeg_image == (Image *) NULL)
13249     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13250   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13251
13252   (void) AcquireUniqueFilename(jpeg_image->filename);
13253   (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
13254     jpeg_image->filename);
13255
13256   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
13257     exception);
13258
13259   if (logging != MagickFalse)
13260     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13261       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
13262       (double) jpeg_image->rows);
13263
13264   if (status == MagickFalse)
13265     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13266
13267   if (jng_color_type == 8 || jng_color_type == 12)
13268     jpeg_image_info->type=GrayscaleType;
13269
13270   jpeg_image_info->quality=jng_quality;
13271   jpeg_image->quality=jng_quality;
13272   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
13273   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13274
13275   if (logging != MagickFalse)
13276     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13277       "  Creating blob.");
13278
13279   blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
13280     exception);
13281
13282   if (logging != MagickFalse)
13283     {
13284       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13285         "  Successfully read jpeg_image into a blob, length=%.20g.",
13286         (double) length);
13287
13288       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13289         "  Write JDAT chunk, length=%.20g.",(double) length);
13290     }
13291
13292   /* Write JDAT chunk(s) */
13293   (void) WriteBlobMSBULong(image,(size_t) length);
13294   PNGType(chunk,mng_JDAT);
13295   LogPNGChunk(logging,mng_JDAT,length);
13296   (void) WriteBlob(image,4,chunk);
13297   (void) WriteBlob(image,length,blob);
13298   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
13299
13300   jpeg_image=DestroyImage(jpeg_image);
13301   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
13302   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13303   blob=(unsigned char *) RelinquishMagickMemory(blob);
13304
13305   /* Write any JNG-chunk-e profiles */
13306   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
13307
13308   /* Write IEND chunk */
13309   (void) WriteBlobMSBULong(image,0L);
13310   PNGType(chunk,mng_IEND);
13311   LogPNGChunk(logging,mng_IEND,0);
13312   (void) WriteBlob(image,4,chunk);
13313   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13314
13315   if (logging != MagickFalse)
13316     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13317       "  exit WriteOneJNGImage()");
13318
13319   return(status);
13320 }
13321
13322 /*
13323 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13324 %                                                                             %
13325 %                                                                             %
13326 %                                                                             %
13327 %   W r i t e J N G I m a g e                                                 %
13328 %                                                                             %
13329 %                                                                             %
13330 %                                                                             %
13331 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13332 %
13333 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
13334 %
13335 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
13336 %
13337 %  The format of the WriteJNGImage method is:
13338 %
13339 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13340 %        Image *image,ExceptionInfo *exception)
13341 %
13342 %  A description of each parameter follows:
13343 %
13344 %    o image_info: the image info.
13345 %
13346 %    o image:  The image.
13347 %
13348 %    o exception: return any errors or warnings in this structure.
13349 %
13350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13351 */
13352 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13353   Image *image, ExceptionInfo *exception)
13354 {
13355   MagickBooleanType
13356     have_mng_structure,
13357     logging,
13358     status;
13359
13360   MngInfo
13361     *mng_info;
13362
13363   /*
13364     Open image file.
13365   */
13366   assert(image_info != (const ImageInfo *) NULL);
13367   assert(image_info->signature == MagickCoreSignature);
13368   assert(image != (Image *) NULL);
13369   assert(image->signature == MagickCoreSignature);
13370   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13371   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
13372   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13373   if (status == MagickFalse)
13374     return(status);
13375   if ((image->columns > 65535UL) || (image->rows > 65535UL))
13376     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
13377
13378   /*
13379     Allocate a MngInfo structure.
13380   */
13381   have_mng_structure=MagickFalse;
13382   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13383   if (mng_info == (MngInfo *) NULL)
13384     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13385   /*
13386     Initialize members of the MngInfo structure.
13387   */
13388   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13389   mng_info->image=image;
13390   have_mng_structure=MagickTrue;
13391
13392   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
13393
13394   status=WriteOneJNGImage(mng_info,image_info,image,exception);
13395   (void) CloseBlob(image);
13396
13397   (void) CatchImageException(image);
13398   MngInfoFreeStruct(mng_info,&have_mng_structure);
13399   if (logging != MagickFalse)
13400     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13401   return(status);
13402 }
13403 #endif
13404
13405 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
13406   Image *image, ExceptionInfo *exception)
13407 {
13408   Image
13409     *next_image;
13410
13411   MagickBooleanType
13412     have_mng_structure,
13413     status;
13414
13415   volatile MagickBooleanType
13416     logging;
13417
13418   MngInfo
13419     *mng_info;
13420
13421   int
13422     image_count,
13423     need_iterations,
13424     need_matte;
13425
13426   volatile int
13427 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13428     defined(PNG_MNG_FEATURES_SUPPORTED)
13429     need_local_plte,
13430 #endif
13431     all_images_are_gray,
13432     need_defi,
13433     use_global_plte;
13434
13435   register ssize_t
13436     i;
13437
13438   unsigned char
13439     chunk[800];
13440
13441   volatile unsigned int
13442     write_jng,
13443     write_mng;
13444
13445   volatile size_t
13446     scene;
13447
13448   size_t
13449     final_delay=0,
13450     initial_delay;
13451
13452 #if (PNG_LIBPNG_VER < 10200)
13453     if (image_info->verbose)
13454       printf("Your PNG library (libpng-%s) is rather old.\n",
13455          PNG_LIBPNG_VER_STRING);
13456 #endif
13457
13458   /*
13459     Open image file.
13460   */
13461   assert(image_info != (const ImageInfo *) NULL);
13462   assert(image_info->signature == MagickCoreSignature);
13463   assert(image != (Image *) NULL);
13464   assert(image->signature == MagickCoreSignature);
13465   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13466   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13467   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13468   if (status == MagickFalse)
13469     return(status);
13470
13471   /*
13472     Allocate a MngInfo structure.
13473   */
13474   have_mng_structure=MagickFalse;
13475   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13476   if (mng_info == (MngInfo *) NULL)
13477     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13478   /*
13479     Initialize members of the MngInfo structure.
13480   */
13481   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13482   mng_info->image=image;
13483   have_mng_structure=MagickTrue;
13484   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13485
13486   /*
13487    * See if user has requested a specific PNG subformat to be used
13488    * for all of the PNGs in the MNG being written, e.g.,
13489    *
13490    *    convert *.png png8:animation.mng
13491    *
13492    * To do: check -define png:bit_depth and png:color_type as well,
13493    * or perhaps use mng:bit_depth and mng:color_type instead for
13494    * global settings.
13495    */
13496
13497   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13498   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13499   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13500
13501   write_jng=MagickFalse;
13502   if (image_info->compression == JPEGCompression)
13503     write_jng=MagickTrue;
13504
13505   mng_info->adjoin=image_info->adjoin &&
13506     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13507
13508   if (logging != MagickFalse)
13509     {
13510       /* Log some info about the input */
13511       Image
13512         *p;
13513
13514       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13515         "  Checking input image(s)\n"
13516         "    Image_info depth: %.20g,    Type: %d",
13517         (double) image_info->depth, image_info->type);
13518
13519       scene=0;
13520       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13521       {
13522
13523         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13524            "    Scene: %.20g\n,   Image depth: %.20g",
13525            (double) scene++, (double) p->depth);
13526
13527         if (p->alpha_trait != UndefinedPixelTrait)
13528           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13529             "      Matte: True");
13530
13531         else
13532           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13533             "      Matte: False");
13534
13535         if (p->storage_class == PseudoClass)
13536           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13537             "      Storage class: PseudoClass");
13538
13539         else
13540           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13541             "      Storage class: DirectClass");
13542
13543         if (p->colors)
13544           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13545             "      Number of colors: %.20g",(double) p->colors);
13546
13547         else
13548           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13549             "      Number of colors: unspecified");
13550
13551         if (mng_info->adjoin == MagickFalse)
13552           break;
13553       }
13554     }
13555
13556   use_global_plte=MagickFalse;
13557   all_images_are_gray=MagickFalse;
13558 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13559   need_local_plte=MagickTrue;
13560 #endif
13561   need_defi=MagickFalse;
13562   need_matte=MagickFalse;
13563   mng_info->framing_mode=1;
13564   mng_info->old_framing_mode=1;
13565
13566   if (write_mng)
13567       if (image_info->page != (char *) NULL)
13568         {
13569           /*
13570             Determine image bounding box.
13571           */
13572           SetGeometry(image,&mng_info->page);
13573           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13574             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13575         }
13576   if (write_mng)
13577     {
13578       unsigned int
13579         need_geom;
13580
13581       unsigned short
13582         red,
13583         green,
13584         blue;
13585
13586       const char *
13587         option;
13588
13589       mng_info->page=image->page;
13590       need_geom=MagickTrue;
13591       if (mng_info->page.width || mng_info->page.height)
13592          need_geom=MagickFalse;
13593       /*
13594         Check all the scenes.
13595       */
13596       initial_delay=image->delay;
13597       need_iterations=MagickFalse;
13598       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13599       mng_info->equal_physs=MagickTrue,
13600       mng_info->equal_gammas=MagickTrue;
13601       mng_info->equal_srgbs=MagickTrue;
13602       mng_info->equal_backgrounds=MagickTrue;
13603       image_count=0;
13604 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13605     defined(PNG_MNG_FEATURES_SUPPORTED)
13606       all_images_are_gray=MagickTrue;
13607       mng_info->equal_palettes=MagickFalse;
13608       need_local_plte=MagickFalse;
13609 #endif
13610       for (next_image=image; next_image != (Image *) NULL; )
13611       {
13612         if (need_geom)
13613           {
13614             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13615               mng_info->page.width=next_image->columns+next_image->page.x;
13616
13617             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13618               mng_info->page.height=next_image->rows+next_image->page.y;
13619           }
13620
13621         if (next_image->page.x || next_image->page.y)
13622           need_defi=MagickTrue;
13623
13624         if (next_image->alpha_trait != UndefinedPixelTrait)
13625           need_matte=MagickTrue;
13626
13627         if ((int) next_image->dispose >= BackgroundDispose)
13628           if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13629                next_image->page.x || next_image->page.y ||
13630               ((next_image->columns < mng_info->page.width) &&
13631                (next_image->rows < mng_info->page.height)))
13632             mng_info->need_fram=MagickTrue;
13633
13634         if (next_image->iterations)
13635           need_iterations=MagickTrue;
13636
13637         final_delay=next_image->delay;
13638
13639         if (final_delay != initial_delay || final_delay > 1UL*
13640            next_image->ticks_per_second)
13641           mng_info->need_fram=1;
13642
13643 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13644     defined(PNG_MNG_FEATURES_SUPPORTED)
13645         /*
13646           check for global palette possibility.
13647         */
13648         if (image->alpha_trait != UndefinedPixelTrait)
13649            need_local_plte=MagickTrue;
13650
13651         if (need_local_plte == 0)
13652           {
13653             if (SetImageGray(image,exception) == MagickFalse)
13654               all_images_are_gray=MagickFalse;
13655             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13656             if (use_global_plte == 0)
13657               use_global_plte=mng_info->equal_palettes;
13658             need_local_plte=!mng_info->equal_palettes;
13659           }
13660 #endif
13661         if (GetNextImageInList(next_image) != (Image *) NULL)
13662           {
13663             if (next_image->background_color.red !=
13664                 next_image->next->background_color.red ||
13665                 next_image->background_color.green !=
13666                 next_image->next->background_color.green ||
13667                 next_image->background_color.blue !=
13668                 next_image->next->background_color.blue)
13669               mng_info->equal_backgrounds=MagickFalse;
13670
13671             if (next_image->gamma != next_image->next->gamma)
13672               mng_info->equal_gammas=MagickFalse;
13673
13674             if (next_image->rendering_intent !=
13675                 next_image->next->rendering_intent)
13676               mng_info->equal_srgbs=MagickFalse;
13677
13678             if ((next_image->units != next_image->next->units) ||
13679                 (next_image->resolution.x != next_image->next->resolution.x) ||
13680                 (next_image->resolution.y != next_image->next->resolution.y))
13681               mng_info->equal_physs=MagickFalse;
13682
13683             if (mng_info->equal_chrms)
13684               {
13685                 if (next_image->chromaticity.red_primary.x !=
13686                     next_image->next->chromaticity.red_primary.x ||
13687                     next_image->chromaticity.red_primary.y !=
13688                     next_image->next->chromaticity.red_primary.y ||
13689                     next_image->chromaticity.green_primary.x !=
13690                     next_image->next->chromaticity.green_primary.x ||
13691                     next_image->chromaticity.green_primary.y !=
13692                     next_image->next->chromaticity.green_primary.y ||
13693                     next_image->chromaticity.blue_primary.x !=
13694                     next_image->next->chromaticity.blue_primary.x ||
13695                     next_image->chromaticity.blue_primary.y !=
13696                     next_image->next->chromaticity.blue_primary.y ||
13697                     next_image->chromaticity.white_point.x !=
13698                     next_image->next->chromaticity.white_point.x ||
13699                     next_image->chromaticity.white_point.y !=
13700                     next_image->next->chromaticity.white_point.y)
13701                   mng_info->equal_chrms=MagickFalse;
13702               }
13703           }
13704         image_count++;
13705         next_image=GetNextImageInList(next_image);
13706       }
13707       if (image_count < 2)
13708         {
13709           mng_info->equal_backgrounds=MagickFalse;
13710           mng_info->equal_chrms=MagickFalse;
13711           mng_info->equal_gammas=MagickFalse;
13712           mng_info->equal_srgbs=MagickFalse;
13713           mng_info->equal_physs=MagickFalse;
13714           use_global_plte=MagickFalse;
13715 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13716           need_local_plte=MagickTrue;
13717 #endif
13718           need_iterations=MagickFalse;
13719         }
13720
13721      if (mng_info->need_fram == MagickFalse)
13722        {
13723          /*
13724            Only certain framing rates 100/n are exactly representable without
13725            the FRAM chunk but we'll allow some slop in VLC files
13726          */
13727          if (final_delay == 0)
13728            {
13729              if (need_iterations != MagickFalse)
13730                {
13731                  /*
13732                    It's probably a GIF with loop; don't run it *too* fast.
13733                  */
13734                  if (mng_info->adjoin)
13735                    {
13736                      final_delay=10;
13737                      (void) ThrowMagickException(exception,GetMagickModule(),
13738                        CoderWarning,
13739                        "input has zero delay between all frames; assuming",
13740                        " 10 cs `%s'","");
13741                    }
13742                }
13743              else
13744                mng_info->ticks_per_second=0;
13745            }
13746          if (final_delay != 0)
13747            mng_info->ticks_per_second=(png_uint_32)
13748               (image->ticks_per_second/final_delay);
13749          if (final_delay > 50)
13750            mng_info->ticks_per_second=2;
13751
13752          if (final_delay > 75)
13753            mng_info->ticks_per_second=1;
13754
13755          if (final_delay > 125)
13756            mng_info->need_fram=MagickTrue;
13757
13758          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13759             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13760             (final_delay != 25) && (final_delay != 50) &&
13761             (final_delay != (size_t) image->ticks_per_second))
13762            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13763        }
13764
13765      if (mng_info->need_fram != MagickFalse)
13766         mng_info->ticks_per_second=image->ticks_per_second;
13767      /*
13768         If pseudocolor, we should also check to see if all the
13769         palettes are identical and write a global PLTE if they are.
13770         ../glennrp Feb 99.
13771      */
13772      /*
13773         Write the MNG version 1.0 signature and MHDR chunk.
13774      */
13775      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13776      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13777      PNGType(chunk,mng_MHDR);
13778      LogPNGChunk(logging,mng_MHDR,28L);
13779      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13780      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13781      PNGLong(chunk+12,mng_info->ticks_per_second);
13782      PNGLong(chunk+16,0L);  /* layer count=unknown */
13783      PNGLong(chunk+20,0L);  /* frame count=unknown */
13784      PNGLong(chunk+24,0L);  /* play time=unknown   */
13785      if (write_jng)
13786        {
13787          if (need_matte)
13788            {
13789              if (need_defi || mng_info->need_fram || use_global_plte)
13790                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13791
13792              else
13793                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13794            }
13795
13796          else
13797            {
13798              if (need_defi || mng_info->need_fram || use_global_plte)
13799                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13800
13801              else
13802                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13803            }
13804        }
13805
13806      else
13807        {
13808          if (need_matte)
13809            {
13810              if (need_defi || mng_info->need_fram || use_global_plte)
13811                PNGLong(chunk+28,11L);    /* simplicity=LC */
13812
13813              else
13814                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13815            }
13816
13817          else
13818            {
13819              if (need_defi || mng_info->need_fram || use_global_plte)
13820                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13821
13822              else
13823                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13824            }
13825        }
13826      (void) WriteBlob(image,32,chunk);
13827      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13828      option=GetImageOption(image_info,"mng:need-cacheoff");
13829      if (option != (const char *) NULL)
13830        {
13831          size_t
13832            length;
13833          /*
13834            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13835          */
13836          PNGType(chunk,mng_nEED);
13837          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13838          (void) WriteBlobMSBULong(image,(size_t) length);
13839          LogPNGChunk(logging,mng_nEED,(size_t) length);
13840          length+=4;
13841          (void) WriteBlob(image,length,chunk);
13842          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13843        }
13844      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13845          (GetNextImageInList(image) != (Image *) NULL) &&
13846          (image->iterations != 1))
13847        {
13848          /*
13849            Write MNG TERM chunk
13850          */
13851          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13852          PNGType(chunk,mng_TERM);
13853          LogPNGChunk(logging,mng_TERM,10L);
13854          chunk[4]=3;  /* repeat animation */
13855          chunk[5]=0;  /* show last frame when done */
13856          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13857             final_delay/MagickMax(image->ticks_per_second,1)));
13858
13859          if (image->iterations == 0)
13860            PNGLong(chunk+10,PNG_UINT_31_MAX);
13861
13862          else
13863            PNGLong(chunk+10,(png_uint_32) image->iterations);
13864
13865          if (logging != MagickFalse)
13866            {
13867              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13868                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13869               final_delay/MagickMax(image->ticks_per_second,1)));
13870
13871              if (image->iterations == 0)
13872                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13873                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13874
13875              else
13876                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13877                  "     Image iterations: %.20g",(double) image->iterations);
13878            }
13879          (void) WriteBlob(image,14,chunk);
13880          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13881        }
13882      /*
13883        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13884      */
13885      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13886           mng_info->equal_srgbs)
13887        {
13888          /*
13889            Write MNG sRGB chunk
13890          */
13891          (void) WriteBlobMSBULong(image,1L);
13892          PNGType(chunk,mng_sRGB);
13893          LogPNGChunk(logging,mng_sRGB,1L);
13894
13895          if (image->rendering_intent != UndefinedIntent)
13896            chunk[4]=(unsigned char)
13897              Magick_RenderingIntent_to_PNG_RenderingIntent(
13898              (image->rendering_intent));
13899
13900          else
13901            chunk[4]=(unsigned char)
13902              Magick_RenderingIntent_to_PNG_RenderingIntent(
13903                (PerceptualIntent));
13904
13905          (void) WriteBlob(image,5,chunk);
13906          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13907          mng_info->have_write_global_srgb=MagickTrue;
13908        }
13909
13910      else
13911        {
13912          if (image->gamma && mng_info->equal_gammas)
13913            {
13914              /*
13915                 Write MNG gAMA chunk
13916              */
13917              (void) WriteBlobMSBULong(image,4L);
13918              PNGType(chunk,mng_gAMA);
13919              LogPNGChunk(logging,mng_gAMA,4L);
13920              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13921              (void) WriteBlob(image,8,chunk);
13922              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13923              mng_info->have_write_global_gama=MagickTrue;
13924            }
13925          if (mng_info->equal_chrms)
13926            {
13927              PrimaryInfo
13928                primary;
13929
13930              /*
13931                 Write MNG cHRM chunk
13932              */
13933              (void) WriteBlobMSBULong(image,32L);
13934              PNGType(chunk,mng_cHRM);
13935              LogPNGChunk(logging,mng_cHRM,32L);
13936              primary=image->chromaticity.white_point;
13937              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13938              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13939              primary=image->chromaticity.red_primary;
13940              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13941              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13942              primary=image->chromaticity.green_primary;
13943              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13944              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13945              primary=image->chromaticity.blue_primary;
13946              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13947              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13948              (void) WriteBlob(image,36,chunk);
13949              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13950              mng_info->have_write_global_chrm=MagickTrue;
13951            }
13952        }
13953      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13954        {
13955          /*
13956             Write MNG pHYs chunk
13957          */
13958          (void) WriteBlobMSBULong(image,9L);
13959          PNGType(chunk,mng_pHYs);
13960          LogPNGChunk(logging,mng_pHYs,9L);
13961
13962          if (image->units == PixelsPerInchResolution)
13963            {
13964              PNGLong(chunk+4,(png_uint_32)
13965                (image->resolution.x*100.0/2.54+0.5));
13966
13967              PNGLong(chunk+8,(png_uint_32)
13968                (image->resolution.y*100.0/2.54+0.5));
13969
13970              chunk[12]=1;
13971            }
13972
13973          else
13974            {
13975              if (image->units == PixelsPerCentimeterResolution)
13976                {
13977                  PNGLong(chunk+4,(png_uint_32)
13978                    (image->resolution.x*100.0+0.5));
13979
13980                  PNGLong(chunk+8,(png_uint_32)
13981                    (image->resolution.y*100.0+0.5));
13982
13983                  chunk[12]=1;
13984                }
13985
13986              else
13987                {
13988                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13989                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13990                  chunk[12]=0;
13991                }
13992            }
13993          (void) WriteBlob(image,13,chunk);
13994          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13995        }
13996      /*
13997        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13998        or does not cover the entire frame.
13999      */
14000      if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
14001          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
14002          (image->page.width+image->page.x < mng_info->page.width))
14003          || (image->page.height && (image->page.height+image->page.y
14004          < mng_info->page.height))))
14005        {
14006          (void) WriteBlobMSBULong(image,6L);
14007          PNGType(chunk,mng_BACK);
14008          LogPNGChunk(logging,mng_BACK,6L);
14009          red=ScaleQuantumToShort(image->background_color.red);
14010          green=ScaleQuantumToShort(image->background_color.green);
14011          blue=ScaleQuantumToShort(image->background_color.blue);
14012          PNGShort(chunk+4,red);
14013          PNGShort(chunk+6,green);
14014          PNGShort(chunk+8,blue);
14015          (void) WriteBlob(image,10,chunk);
14016          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
14017          if (mng_info->equal_backgrounds)
14018            {
14019              (void) WriteBlobMSBULong(image,6L);
14020              PNGType(chunk,mng_bKGD);
14021              LogPNGChunk(logging,mng_bKGD,6L);
14022              (void) WriteBlob(image,10,chunk);
14023              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
14024            }
14025        }
14026
14027 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
14028      if ((need_local_plte == MagickFalse) &&
14029          (image->storage_class == PseudoClass) &&
14030          (all_images_are_gray == MagickFalse))
14031        {
14032          size_t
14033            data_length;
14034
14035          /*
14036            Write MNG PLTE chunk
14037          */
14038          data_length=3*image->colors;
14039          (void) WriteBlobMSBULong(image,data_length);
14040          PNGType(chunk,mng_PLTE);
14041          LogPNGChunk(logging,mng_PLTE,data_length);
14042
14043          for (i=0; i < (ssize_t) image->colors; i++)
14044          {
14045            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
14046              image->colormap[i].red) & 0xff);
14047            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
14048              image->colormap[i].green) & 0xff);
14049            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
14050              image->colormap[i].blue) & 0xff);
14051          }
14052
14053          (void) WriteBlob(image,data_length+4,chunk);
14054          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
14055          mng_info->have_write_global_plte=MagickTrue;
14056        }
14057 #endif
14058     }
14059   scene=0;
14060   mng_info->delay=0;
14061 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14062     defined(PNG_MNG_FEATURES_SUPPORTED)
14063   mng_info->equal_palettes=MagickFalse;
14064 #endif
14065   do
14066   {
14067     if (mng_info->adjoin)
14068     {
14069 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14070     defined(PNG_MNG_FEATURES_SUPPORTED)
14071     /*
14072       If we aren't using a global palette for the entire MNG, check to
14073       see if we can use one for two or more consecutive images.
14074     */
14075     if (need_local_plte && use_global_plte && !all_images_are_gray)
14076       {
14077         if (mng_info->IsPalette)
14078           {
14079             /*
14080               When equal_palettes is true, this image has the same palette
14081               as the previous PseudoClass image
14082             */
14083             mng_info->have_write_global_plte=mng_info->equal_palettes;
14084             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
14085             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
14086               {
14087                 /*
14088                   Write MNG PLTE chunk
14089                 */
14090                 size_t
14091                   data_length;
14092
14093                 data_length=3*image->colors;
14094                 (void) WriteBlobMSBULong(image,data_length);
14095                 PNGType(chunk,mng_PLTE);
14096                 LogPNGChunk(logging,mng_PLTE,data_length);
14097
14098                 for (i=0; i < (ssize_t) image->colors; i++)
14099                 {
14100                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
14101                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
14102                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
14103                 }
14104
14105                 (void) WriteBlob(image,data_length+4,chunk);
14106                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
14107                    (uInt) (data_length+4)));
14108                 mng_info->have_write_global_plte=MagickTrue;
14109               }
14110           }
14111         else
14112           mng_info->have_write_global_plte=MagickFalse;
14113       }
14114 #endif
14115     if (need_defi)
14116       {
14117         ssize_t
14118           previous_x,
14119           previous_y;
14120
14121         if (scene)
14122           {
14123             previous_x=mng_info->page.x;
14124             previous_y=mng_info->page.y;
14125           }
14126         else
14127           {
14128             previous_x=0;
14129             previous_y=0;
14130           }
14131         mng_info->page=image->page;
14132         if ((mng_info->page.x !=  previous_x) ||
14133             (mng_info->page.y != previous_y))
14134           {
14135              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
14136              PNGType(chunk,mng_DEFI);
14137              LogPNGChunk(logging,mng_DEFI,12L);
14138              chunk[4]=0; /* object 0 MSB */
14139              chunk[5]=0; /* object 0 LSB */
14140              chunk[6]=0; /* visible  */
14141              chunk[7]=0; /* abstract */
14142              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
14143              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
14144              (void) WriteBlob(image,16,chunk);
14145              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
14146           }
14147       }
14148     }
14149
14150    mng_info->write_mng=write_mng;
14151
14152    if ((int) image->dispose >= 3)
14153      mng_info->framing_mode=3;
14154
14155    if (mng_info->need_fram && mng_info->adjoin &&
14156        ((image->delay != mng_info->delay) ||
14157         (mng_info->framing_mode != mng_info->old_framing_mode)))
14158      {
14159        if (image->delay == mng_info->delay)
14160          {
14161            /*
14162              Write a MNG FRAM chunk with the new framing mode.
14163            */
14164            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
14165            PNGType(chunk,mng_FRAM);
14166            LogPNGChunk(logging,mng_FRAM,1L);
14167            chunk[4]=(unsigned char) mng_info->framing_mode;
14168            (void) WriteBlob(image,5,chunk);
14169            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
14170          }
14171        else
14172          {
14173            /*
14174              Write a MNG FRAM chunk with the delay.
14175            */
14176            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
14177            PNGType(chunk,mng_FRAM);
14178            LogPNGChunk(logging,mng_FRAM,10L);
14179            chunk[4]=(unsigned char) mng_info->framing_mode;
14180            chunk[5]=0;  /* frame name separator (no name) */
14181            chunk[6]=2;  /* flag for changing default delay */
14182            chunk[7]=0;  /* flag for changing frame timeout */
14183            chunk[8]=0;  /* flag for changing frame clipping */
14184            chunk[9]=0;  /* flag for changing frame sync_id */
14185            PNGLong(chunk+10,(png_uint_32)
14186              ((mng_info->ticks_per_second*
14187              image->delay)/MagickMax(image->ticks_per_second,1)));
14188            (void) WriteBlob(image,14,chunk);
14189            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
14190            mng_info->delay=(png_uint_32) image->delay;
14191          }
14192        mng_info->old_framing_mode=mng_info->framing_mode;
14193      }
14194
14195 #if defined(JNG_SUPPORTED)
14196    if (image_info->compression == JPEGCompression)
14197      {
14198        ImageInfo
14199          *write_info;
14200
14201        if (logging != MagickFalse)
14202          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14203            "  Writing JNG object.");
14204        /* To do: specify the desired alpha compression method. */
14205        write_info=CloneImageInfo(image_info);
14206        write_info->compression=UndefinedCompression;
14207        status=WriteOneJNGImage(mng_info,write_info,image,exception);
14208        write_info=DestroyImageInfo(write_info);
14209      }
14210    else
14211 #endif
14212      {
14213        if (logging != MagickFalse)
14214          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14215            "  Writing PNG object.");
14216
14217        mng_info->need_blob = MagickFalse;
14218        mng_info->ping_preserve_colormap = MagickFalse;
14219
14220        /* We don't want any ancillary chunks written */
14221        mng_info->ping_exclude_bKGD=MagickTrue;
14222        mng_info->ping_exclude_caNv=MagickTrue;
14223        mng_info->ping_exclude_cHRM=MagickTrue;
14224        mng_info->ping_exclude_date=MagickTrue;
14225        mng_info->ping_exclude_EXIF=MagickTrue;
14226        mng_info->ping_exclude_gAMA=MagickTrue;
14227        mng_info->ping_exclude_iCCP=MagickTrue;
14228        /* mng_info->ping_exclude_iTXt=MagickTrue; */
14229        mng_info->ping_exclude_oFFs=MagickTrue;
14230        mng_info->ping_exclude_pHYs=MagickTrue;
14231        mng_info->ping_exclude_sRGB=MagickTrue;
14232        mng_info->ping_exclude_tEXt=MagickTrue;
14233        mng_info->ping_exclude_tRNS=MagickTrue;
14234        mng_info->ping_exclude_vpAg=MagickTrue;
14235        mng_info->ping_exclude_zCCP=MagickTrue;
14236        mng_info->ping_exclude_zTXt=MagickTrue;
14237
14238        status=WriteOnePNGImage(mng_info,image_info,image,exception);
14239      }
14240
14241     if (status == MagickFalse)
14242       {
14243         MngInfoFreeStruct(mng_info,&have_mng_structure);
14244         (void) CloseBlob(image);
14245         return(MagickFalse);
14246       }
14247     (void) CatchImageException(image);
14248     if (GetNextImageInList(image) == (Image *) NULL)
14249       break;
14250     image=SyncNextImageInList(image);
14251     status=SetImageProgress(image,SaveImagesTag,scene++,
14252       GetImageListLength(image));
14253
14254     if (status == MagickFalse)
14255       break;
14256
14257   } while (mng_info->adjoin);
14258
14259   if (write_mng)
14260     {
14261       while (GetPreviousImageInList(image) != (Image *) NULL)
14262         image=GetPreviousImageInList(image);
14263       /*
14264         Write the MEND chunk.
14265       */
14266       (void) WriteBlobMSBULong(image,0x00000000L);
14267       PNGType(chunk,mng_MEND);
14268       LogPNGChunk(logging,mng_MEND,0L);
14269       (void) WriteBlob(image,4,chunk);
14270       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
14271     }
14272   /*
14273     Relinquish resources.
14274   */
14275   (void) CloseBlob(image);
14276   MngInfoFreeStruct(mng_info,&have_mng_structure);
14277
14278   if (logging != MagickFalse)
14279     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
14280
14281   return(MagickTrue);
14282 }
14283 #else /* PNG_LIBPNG_VER > 10011 */
14284
14285 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
14286   Image *image)
14287 {
14288   (void) image;
14289   printf("Your PNG library is too old: You have libpng-%s\n",
14290      PNG_LIBPNG_VER_STRING);
14291
14292   ThrowBinaryException(CoderError,"PNG library is too old",
14293      image_info->filename);
14294 }
14295
14296 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
14297   Image *image)
14298 {
14299   return(WritePNGImage(image_info,image));
14300 }
14301 #endif /* PNG_LIBPNG_VER > 10011 */
14302 #endif