]> granicus.if.org Git - imagemagick/blob - coders/png.c
Added check for incorrect number of meta channels report in #492.
[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 MngInfo *MngInfoFreeStruct(MngInfo *mng_info)
1558 {
1559   register ssize_t
1560     i;
1561
1562   if (mng_info == (MngInfo *) NULL)
1563     return((MngInfo *) NULL);
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   return((MngInfo *) RelinquishMagickMemory(mng_info));
1573 }
1574
1575 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1576 {
1577   MngBox
1578     box;
1579
1580   box=box1;
1581   if (box.left < box2.left)
1582     box.left=box2.left;
1583
1584   if (box.top < box2.top)
1585     box.top=box2.top;
1586
1587   if (box.right > box2.right)
1588     box.right=box2.right;
1589
1590   if (box.bottom > box2.bottom)
1591     box.bottom=box2.bottom;
1592
1593   return box;
1594 }
1595
1596 static MngBox mng_read_box(MngBox previous_box,char delta_type,
1597   unsigned char *p)
1598 {
1599    MngBox
1600       box;
1601
1602   /*
1603     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1604   */
1605   box.left=(ssize_t) ((p[0]  << 24) | (p[1]  << 16) | (p[2]  << 8) | p[3]);
1606   box.right=(ssize_t) ((p[4]  << 24) | (p[5]  << 16) | (p[6]  << 8) | p[7]);
1607   box.top=(ssize_t) ((p[8]  << 24) | (p[9]  << 16) | (p[10] << 8) | p[11]);
1608   box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1609   if (delta_type != 0)
1610     {
1611       box.left+=previous_box.left;
1612       box.right+=previous_box.right;
1613       box.top+=previous_box.top;
1614       box.bottom+=previous_box.bottom;
1615     }
1616
1617   return(box);
1618 }
1619
1620 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1621   unsigned char *p)
1622 {
1623   MngPair
1624     pair;
1625   /*
1626     Read two ssize_ts from CLON, MOVE or PAST chunk
1627   */
1628   pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1629   pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1630
1631   if (delta_type != 0)
1632     {
1633       pair.a+=previous_pair.a;
1634       pair.b+=previous_pair.b;
1635     }
1636
1637   return(pair);
1638 }
1639
1640 static long mng_get_long(unsigned char *p)
1641 {
1642   return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1643 }
1644
1645 typedef struct _PNGErrorInfo
1646 {
1647   Image
1648     *image;
1649
1650   ExceptionInfo
1651     *exception;
1652 } PNGErrorInfo;
1653
1654 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1655 {
1656   ExceptionInfo
1657     *exception;
1658
1659   Image
1660     *image;
1661
1662   PNGErrorInfo
1663     *error_info;
1664
1665   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1666   image=error_info->image;
1667   exception=error_info->exception;
1668
1669   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1670     "  libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1671
1672   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1673     "`%s'",image->filename);
1674
1675 #if (PNG_LIBPNG_VER < 10500)
1676   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1677    * are building with libpng-1.4.x and can be ignored.
1678    */
1679   longjmp(ping->jmpbuf,1);
1680 #else
1681   png_longjmp(ping,1);
1682 #endif
1683 }
1684
1685 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1686 {
1687   ExceptionInfo
1688     *exception;
1689
1690   Image
1691     *image;
1692
1693   PNGErrorInfo
1694     *error_info;
1695
1696   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1697     png_error(ping, message);
1698
1699   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1700   image=error_info->image;
1701   exception=error_info->exception;
1702   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1703     "  libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1704
1705   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1706     message,"`%s'",image->filename);
1707 }
1708
1709 #ifdef PNG_USER_MEM_SUPPORTED
1710 #if PNG_LIBPNG_VER >= 10400
1711 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1712 #else
1713 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1714 #endif
1715 {
1716   (void) png_ptr;
1717   return((png_voidp) AcquireMagickMemory((size_t) size));
1718 }
1719
1720 /*
1721   Free a pointer.  It is removed from the list at the same time.
1722 */
1723 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1724 {
1725   (void) png_ptr;
1726   ptr=RelinquishMagickMemory(ptr);
1727   return((png_free_ptr) NULL);
1728 }
1729 #endif
1730
1731 #if defined(__cplusplus) || defined(c_plusplus)
1732 }
1733 #endif
1734
1735 static int
1736 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1737    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1738 {
1739   register ssize_t
1740     i;
1741
1742   register unsigned char
1743     *dp;
1744
1745   register png_charp
1746     sp;
1747
1748   png_uint_32
1749     length,
1750     nibbles;
1751
1752   StringInfo
1753     *profile;
1754
1755   const unsigned char
1756     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1757                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1758                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,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,0, 0,0,0,0,0,0,0,10,11,12,
1761                  13,14,15};
1762
1763   sp=text[ii].text+1;
1764   /* look for newline */
1765   while (*sp != '\n')
1766      sp++;
1767
1768   /* look for length */
1769   while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1770      sp++;
1771
1772   length=(png_uint_32) StringToLong(sp);
1773
1774   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1775        "      length: %lu",(unsigned long) length);
1776
1777   while (*sp != ' ' && *sp != '\n')
1778      sp++;
1779
1780   /* allocate space */
1781   if (length == 0)
1782   {
1783     png_warning(ping,"invalid profile length");
1784     return(MagickFalse);
1785   }
1786
1787   profile=BlobToStringInfo((const void *) NULL,length);
1788
1789   if (profile == (StringInfo *) NULL)
1790   {
1791     png_warning(ping, "unable to copy profile");
1792     return(MagickFalse);
1793   }
1794
1795   /* copy profile, skipping white space and column 1 "=" signs */
1796   dp=GetStringInfoDatum(profile);
1797   nibbles=length*2;
1798
1799   for (i=0; i < (ssize_t) nibbles; i++)
1800   {
1801     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1802     {
1803       if (*sp == '\0')
1804         {
1805           png_warning(ping, "ran out of profile data");
1806           profile=DestroyStringInfo(profile);
1807           return(MagickFalse);
1808         }
1809       sp++;
1810     }
1811
1812     if (i%2 == 0)
1813       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1814
1815     else
1816       (*dp++)+=unhex[(int) *sp++];
1817   }
1818   /*
1819     We have already read "Raw profile type.
1820   */
1821   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1822   profile=DestroyStringInfo(profile);
1823
1824   if (image_info->verbose)
1825     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1826
1827   return MagickTrue;
1828 }
1829
1830 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1831 #ifdef zxIf_SUPPORTED
1832
1833 /* exif_inf() was derived from zlib-1.2.11/examples/zpipe.c/inf()
1834    Not copyrighted -- provided to the public domain
1835    Version 1.4  11 December 2005  Mark Adler */
1836
1837 #include <assert.h>
1838
1839 /* Decompress from source to dest (copied from zlib-1.2.11/examples).
1840    inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
1841    allocated for processing, Z_DATA_ERROR if the deflate data is
1842    invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
1843    the version of the library linked do not match, or Z_ERRNO if there
1844    is an error reading or writing the files. */
1845
1846 int exif_inf(png_structp png_ptr, unsigned char *source,
1847     unsigned char **dest, size_t n, png_uint_32 inflated_size)
1848 {
1849     /* *source: compressed data stream (input)
1850        *dest:   inflated data (output)
1851        n: length of input
1852
1853        Returns one of the following:
1854        return(-1);  chunk had an error
1855        return(n);  success, n is length of inflated data
1856      */
1857
1858     int ret;
1859     z_stream strm;
1860
1861     size_t inflated_length = inflated_size;
1862
1863     if (inflated_length >= PNG_USER_CHUNK_MALLOC_MAX - 1U ||
1864         inflated_length == 0)
1865         return (-1);
1866
1867     /* allocate dest */
1868 #if PNG_LIBPNG_VER >= 14000
1869     *dest=(unsigned char *) png_malloc(png_ptr,
1870        (png_alloc_size_t) inflated_length);
1871 #else
1872     *dest=(unsigned char *) png_malloc(png_ptr,
1873        (png_size_t) inflated_length);
1874 #endif
1875     /* allocate inflate state */
1876     strm.zalloc = Z_NULL;
1877     strm.zfree = Z_NULL;
1878     strm.opaque = Z_NULL;
1879     strm.avail_in = 0;
1880     strm.next_in = Z_NULL;
1881     ret = inflateInit(&strm);
1882     if (ret != Z_OK)
1883         return (-1);
1884     /* decompress until deflate stream ends or end of file */
1885     do {
1886         strm.avail_in = (int)n;
1887         strm.next_in = source;
1888
1889         /* run inflate() on input until output buffer not full */
1890         do {
1891             strm.avail_out = (int)inflated_length;
1892             strm.next_out = *dest;
1893             ret = inflate(&strm, Z_NO_FLUSH);
1894             assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
1895             switch (ret) {
1896             case Z_NEED_DICT:
1897             case Z_DATA_ERROR:
1898             case Z_MEM_ERROR:
1899                 (void)inflateEnd(&strm);
1900                 return (-1);
1901             }
1902         } while (strm.avail_out == 0);
1903         /* done when inflate() says it's done */
1904     } while (ret != Z_STREAM_END);
1905
1906     /* clean up and return */
1907
1908     /* To do: take care of too little or too much data */
1909
1910     (void)inflateEnd(&strm);
1911     return (inflated_length);
1912 }
1913 #endif /* zxIf */
1914
1915 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1916 {
1917   Image
1918     *image;
1919
1920
1921   /* The unknown chunk structure contains the chunk data:
1922      png_byte name[5];
1923      png_byte *data;
1924      png_size_t size;
1925
1926      Note that libpng has already taken care of the CRC handling.
1927
1928      Returns one of the following:
1929          return(-n);  chunk had an error
1930          return(0);  did not recognize
1931          return(n);  success
1932   */
1933
1934   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1935      "    read_user_chunk: found %c%c%c%c chunk",
1936        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1937
1938 #if defined(zxIf_SUPPORTED)
1939   if ((chunk->name[0]  == 122 || chunk->name[0] == 117 ) &&
1940       (chunk->name[1] ==   88 || chunk->name[1] == 120 ) &&
1941       chunk->name[2] ==   73 &&
1942       chunk-> name[3] == 102)
1943     {
1944       /* process uxIf or zxIf chunk */
1945       StringInfo *
1946         profile;
1947
1948       PNGErrorInfo
1949         *error_info;
1950
1951       unsigned char
1952         *p;
1953
1954       png_byte
1955         *s;
1956
1957       size_t
1958         i;
1959
1960       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1961         "     recognized uxIf|zxIf chunk");
1962
1963       image=(Image *) png_get_user_chunk_ptr(ping);
1964
1965       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1966
1967       profile=BlobToStringInfo((const void *) NULL,chunk->size+6);
1968
1969       if (profile == (StringInfo *) NULL)
1970         {
1971           (void) ThrowMagickException(error_info->exception,GetMagickModule(),
1972             ResourceLimitError,"MemoryAllocationFailed","`%s'",
1973             image->filename);
1974           return(-1);
1975         }
1976       p=GetStringInfoDatum(profile);
1977
1978       /* Initialize profile with "Exif\0\0" */
1979       *p++ ='E';
1980       *p++ ='x';
1981       *p++ ='i';
1982       *p++ ='f';
1983       *p++ ='\0';
1984       *p++ ='\0';
1985
1986       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1987         "     initialized uxIf|zxIf chunk");
1988
1989       switch (chunk->data[0])
1990         {
1991           PNGErrorInfo
1992             *error_info;
1993           case 'E':
1994           case 'I':
1995           {
1996             /* Uncompressed */
1997             /* copy chunk->data to profile */
1998             s=chunk->data;
1999             for (i=0; i<chunk->size; i++)
2000               *p++ = *s++;
2001
2002             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2003                "     SetImageProfile with %lu bytes",
2004                (unsigned long) chunk->size+6);
2005             error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2006             (void) SetImageProfile(image,"exif",profile,
2007               error_info->exception);
2008             return(1);
2009           }
2010           case '\0':
2011           {
2012             /* Zlib compressed */
2013
2014             unsigned char *
2015               temp;
2016
2017             png_uint_32 inflated_size;
2018
2019             png_free(ping,profile);
2020
2021             if (chunk->size < 5)
2022                return(-1);
2023
2024             s=chunk->data;
2025             s++;  // skip compression byte
2026
2027             inflated_size = (png_uint_32)
2028                (((s[0] & 0xff) << 24) | ((s[1] & 0xff) << 16) |
2029                 ((s[2] & 0xff) <<  8) | ((s[3] & 0xff)      ));
2030
2031             s+=4;
2032
2033             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2034                "     inflated_size = %lu bytes",inflated_size);
2035
2036             /* uncompress chunk->data to temporary profile */
2037             inflated_size=exif_inf(ping,s,&temp,chunk->size-1,inflated_size);
2038
2039             if (inflated_size <= 0)
2040             {
2041                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2042                   "     inflated_size = %lu bytes",inflated_size);
2043                return(-1);
2044             }
2045            error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2046
2047            profile=BlobToStringInfo((const void *) NULL,inflated_size+6);
2048
2049            if (profile == (StringInfo *) NULL)
2050              {
2051                (void) ThrowMagickException(error_info->exception,
2052                  GetMagickModule(),
2053                  ResourceLimitError,"MemoryAllocationFailed","`%s'",
2054                  image->filename);
2055                return(-1);
2056              }
2057            p=GetStringInfoDatum(profile);
2058            /* Initialize profile with "Exif\0\0" */
2059            *p++ ='E';
2060            *p++ ='x';
2061            *p++ ='i';
2062            *p++ ='f';
2063            *p++ ='\0';
2064            *p++ ='\0';
2065
2066            for (i=0; i<inflated_size; i++)
2067               *p++=temp[i];
2068
2069             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2070                "     SetImageProfile with %lu bytes",
2071                (unsigned long) inflated_size+6);
2072             error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2073             (void) SetImageProfile(image,"exif",profile,
2074               error_info->exception);
2075
2076             png_free(ping,temp);
2077
2078             return(1);
2079           }
2080        }
2081     }
2082 #endif /* zxIf_SUPPORTED */
2083
2084   if (chunk->name[0]  == 101 &&
2085       (chunk->name[1] ==  88 || chunk->name[1] == 120 ) &&
2086       chunk->name[2] ==   73 &&
2087       chunk-> name[3] == 102)
2088     {
2089       /* process eXIf or exIf chunk */
2090
2091       PNGErrorInfo
2092         *error_info;
2093
2094       StringInfo
2095         *profile;
2096
2097       unsigned char
2098         *p;
2099
2100       png_byte
2101         *s;
2102
2103       size_t
2104         i;
2105
2106       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2107         " recognized eXIf|exIf chunk");
2108
2109       image=(Image *) png_get_user_chunk_ptr(ping);
2110
2111       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2112
2113       profile=BlobToStringInfo((const void *) NULL,chunk->size+6);
2114
2115       if (profile == (StringInfo *) NULL)
2116         {
2117           (void) ThrowMagickException(error_info->exception,GetMagickModule(),
2118             ResourceLimitError,"MemoryAllocationFailed","`%s'",
2119             image->filename);
2120           return(-1);
2121         }
2122       p=GetStringInfoDatum(profile);
2123
2124       /* Initialize profile with "Exif\0\0" */
2125       *p++ ='E';
2126       *p++ ='x';
2127       *p++ ='i';
2128       *p++ ='f';
2129       *p++ ='\0';
2130       *p++ ='\0';
2131
2132       /* copy chunk->data to profile */
2133       s=chunk->data;
2134       for (i=0; i<chunk->size; i++)
2135         *p++ = *s++;
2136
2137       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2138       (void) SetImageProfile(image,"exif",profile,
2139         error_info->exception);
2140
2141       return(1);
2142     }
2143
2144   /* vpAg (deprecated, replaced by caNv) */
2145   if (chunk->name[0] == 118 &&
2146       chunk->name[1] == 112 &&
2147       chunk->name[2] ==  65 &&
2148       chunk->name[3] == 103)
2149     {
2150      /* recognized vpAg */
2151
2152      if (chunk->size != 9)
2153        return(-1); /* Error return */
2154
2155      if (chunk->data[8] != 0)
2156        return(0);  /* ImageMagick requires pixel units */
2157
2158      image=(Image *) png_get_user_chunk_ptr(ping);
2159
2160      image->page.width=(size_t) ((chunk->data[0] << 24) |
2161         (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
2162
2163      image->page.height=(size_t) ((chunk->data[4] << 24) |
2164         (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
2165
2166      return(1);
2167     }
2168
2169   /* caNv */
2170   if (chunk->name[0] ==  99 &&
2171       chunk->name[1] ==  97 &&
2172       chunk->name[2] ==  78 &&
2173       chunk->name[3] == 118)
2174     {
2175      /* recognized caNv */
2176
2177      if (chunk->size != 16)
2178        return(-1); /* Error return */
2179
2180      image=(Image *) png_get_user_chunk_ptr(ping);
2181
2182      image->page.width=(size_t) ((chunk->data[0] << 24) |
2183         (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
2184
2185      image->page.height=(size_t) ((chunk->data[4] << 24) |
2186         (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
2187
2188      image->page.x=(size_t) ((chunk->data[8] << 24) |
2189         (chunk->data[9] << 16) | (chunk->data[10] << 8) | chunk->data[11]);
2190
2191      image->page.y=(size_t) ((chunk->data[12] << 24) |
2192         (chunk->data[13] << 16) | (chunk->data[14] << 8) | chunk->data[15]);
2193
2194      return(1);
2195     }
2196
2197   return(0); /* Did not recognize */
2198 }
2199 #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
2200
2201 #if defined(PNG_tIME_SUPPORTED)
2202 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
2203   ExceptionInfo *exception)
2204 {
2205   png_timep
2206     time;
2207
2208   if (png_get_tIME(ping,info,&time))
2209     {
2210       char
2211         timestamp[21];
2212
2213       FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
2214         time->year,time->month,time->day,time->hour,time->minute,time->second);
2215       SetImageProperty(image,"png:tIME",timestamp,exception);
2216     }
2217 }
2218 #endif
2219
2220 /*
2221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2222 %                                                                             %
2223 %                                                                             %
2224 %                                                                             %
2225 %   R e a d O n e P N G I m a g e                                             %
2226 %                                                                             %
2227 %                                                                             %
2228 %                                                                             %
2229 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2230 %
2231 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2232 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
2233 %  necessary for the new Image structure and returns a pointer to the new
2234 %  image.
2235 %
2236 %  The format of the ReadOnePNGImage method is:
2237 %
2238 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2239 %         ExceptionInfo *exception)
2240 %
2241 %  A description of each parameter follows:
2242 %
2243 %    o mng_info: Specifies a pointer to a MngInfo structure.
2244 %
2245 %    o image_info: the image info.
2246 %
2247 %    o exception: return any errors or warnings in this structure.
2248 %
2249 */
2250 static Image *ReadOnePNGImage(MngInfo *mng_info,
2251     const ImageInfo *image_info, ExceptionInfo *exception)
2252 {
2253   /* Read one PNG image */
2254
2255   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2256
2257   Image
2258     *image;
2259
2260   char
2261     im_vers[32],
2262     libpng_runv[32],
2263     libpng_vers[32],
2264     zlib_runv[32],
2265     zlib_vers[32];
2266
2267   int
2268     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2269     num_raw_profiles,
2270     num_text,
2271     num_text_total,
2272     num_passes,
2273     number_colors,
2274     pass,
2275     ping_bit_depth,
2276     ping_color_type,
2277     ping_file_depth,
2278     ping_interlace_method,
2279     ping_compression_method,
2280     ping_filter_method,
2281     ping_num_trans,
2282     unit_type;
2283
2284   double
2285     file_gamma;
2286
2287   MagickBooleanType
2288     logging,
2289     ping_found_cHRM,
2290     ping_found_gAMA,
2291     ping_found_iCCP,
2292     ping_found_sRGB,
2293     ping_found_sRGB_cHRM,
2294     ping_preserve_iCCP,
2295     status;
2296
2297   MemoryInfo
2298     *volatile pixel_info;
2299
2300   PixelInfo
2301     transparent_color;
2302
2303   PNGErrorInfo
2304     error_info;
2305
2306   png_bytep
2307      ping_trans_alpha;
2308
2309   png_color_16p
2310      ping_background,
2311      ping_trans_color;
2312
2313   png_info
2314     *end_info,
2315     *ping_info;
2316
2317   png_struct
2318     *ping;
2319
2320   png_textp
2321     text;
2322
2323   png_uint_32
2324     ping_height,
2325     ping_width,
2326     x_resolution,
2327     y_resolution;
2328
2329   QuantumInfo
2330     *quantum_info;
2331
2332   ssize_t
2333     ping_rowbytes,
2334     y;
2335
2336   register unsigned char
2337     *p;
2338
2339   register ssize_t
2340     i,
2341     x;
2342
2343   register Quantum
2344     *q;
2345
2346   size_t
2347     length,
2348     row_offset;
2349
2350   ssize_t
2351     j;
2352
2353   unsigned char
2354     *ping_pixels;
2355
2356 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2357   png_byte unused_chunks[]=
2358   {
2359     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2360     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2361     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2362     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2363     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2364 #if !defined(PNG_tIME_SUPPORTED)
2365     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2366 #endif
2367 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2368                           /* ignore the APNG chunks */
2369      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2370     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2371     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2372 #endif
2373   };
2374 #endif
2375
2376   /* Define these outside of the following "if logging()" block so they will
2377    * show in debuggers.
2378    */
2379   *im_vers='\0';
2380   (void) ConcatenateMagickString(im_vers,
2381          MagickLibVersionText,32);
2382   (void) ConcatenateMagickString(im_vers,
2383          MagickLibAddendum,32);
2384
2385   *libpng_vers='\0';
2386   (void) ConcatenateMagickString(libpng_vers,
2387          PNG_LIBPNG_VER_STRING,32);
2388   *libpng_runv='\0';
2389   (void) ConcatenateMagickString(libpng_runv,
2390          png_get_libpng_ver(NULL),32);
2391
2392   *zlib_vers='\0';
2393   (void) ConcatenateMagickString(zlib_vers,
2394          ZLIB_VERSION,32);
2395   *zlib_runv='\0';
2396   (void) ConcatenateMagickString(zlib_runv,
2397          zlib_version,32);
2398
2399   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2400        "  Enter ReadOnePNGImage()\n"
2401        "    IM version     = %s\n"
2402        "    Libpng version = %s",
2403        im_vers, libpng_vers);
2404
2405   if (logging != MagickFalse)
2406   {
2407     if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2408     {
2409    (void) LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2410         libpng_runv);
2411     }
2412     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
2413         zlib_vers);
2414     if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2415     {
2416     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2417         zlib_runv);
2418     }
2419   }
2420
2421 #if (PNG_LIBPNG_VER < 10200)
2422   if (image_info->verbose)
2423     printf("Your PNG library (libpng-%s) is rather old.\n",
2424        PNG_LIBPNG_VER_STRING);
2425 #endif
2426
2427 #if (PNG_LIBPNG_VER >= 10400)
2428 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
2429   if (image_info->verbose)
2430     {
2431       printf("Your PNG library (libpng-%s) is an old beta version.\n",
2432            PNG_LIBPNG_VER_STRING);
2433       printf("Please update it.\n");
2434     }
2435 #  endif
2436 #endif
2437
2438
2439   quantum_info = (QuantumInfo *) NULL;
2440   image=mng_info->image;
2441
2442   if (logging != MagickFalse)
2443   {
2444     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2445        "    Before reading:\n"
2446        "      image->alpha_trait=%d"
2447        "      image->rendering_intent=%d\n"
2448        "      image->colorspace=%d\n"
2449        "      image->gamma=%f",
2450        (int) image->alpha_trait, (int) image->rendering_intent,
2451        (int) image->colorspace, image->gamma);
2452   }
2453   intent=
2454     Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2455
2456   /* Set to an out-of-range color unless tRNS chunk is present */
2457   transparent_color.red=65537;
2458   transparent_color.green=65537;
2459   transparent_color.blue=65537;
2460   transparent_color.alpha=65537;
2461
2462   number_colors=0;
2463   num_text = 0;
2464   num_text_total = 0;
2465   num_raw_profiles = 0;
2466
2467   ping_found_cHRM = MagickFalse;
2468   ping_found_gAMA = MagickFalse;
2469   ping_found_iCCP = MagickFalse;
2470   ping_found_sRGB = MagickFalse;
2471   ping_found_sRGB_cHRM = MagickFalse;
2472   ping_preserve_iCCP = MagickFalse;
2473
2474
2475   /*
2476     Allocate the PNG structures
2477   */
2478 #ifdef PNG_USER_MEM_SUPPORTED
2479  error_info.image=image;
2480  error_info.exception=exception;
2481  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2482    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2483    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2484 #else
2485   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2486     MagickPNGErrorHandler,MagickPNGWarningHandler);
2487 #endif
2488   if (ping == (png_struct *) NULL)
2489     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2490
2491   ping_info=png_create_info_struct(ping);
2492
2493   if (ping_info == (png_info *) NULL)
2494     {
2495       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2496       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2497     }
2498
2499   end_info=png_create_info_struct(ping);
2500
2501   if (end_info == (png_info *) NULL)
2502     {
2503       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2504       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2505     }
2506
2507   pixel_info=(MemoryInfo *) NULL;
2508
2509   if (setjmp(png_jmpbuf(ping)))
2510     {
2511       /*
2512         PNG image is corrupt.
2513       */
2514       png_destroy_read_struct(&ping,&ping_info,&end_info);
2515
2516 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2517       UnlockSemaphoreInfo(ping_semaphore);
2518 #endif
2519
2520       if (pixel_info != (MemoryInfo *) NULL)
2521         pixel_info=RelinquishVirtualMemory(pixel_info);
2522
2523       if (logging != MagickFalse)
2524         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2525           "  exit ReadOnePNGImage() with error.");
2526
2527       return(GetFirstImageInList(image));
2528     }
2529
2530   /* {  For navigation to end of SETJMP-protected block.  Within this
2531    *    block, use png_error() instead of Throwing an Exception, to ensure
2532    *    that libpng is able to clean up, and that the semaphore is unlocked.
2533    */
2534
2535 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2536   LockSemaphoreInfo(ping_semaphore);
2537 #endif
2538
2539 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2540   /* Allow benign errors */
2541   png_set_benign_errors(ping, 1);
2542 #endif
2543
2544 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2545   /* Reject images with too many rows or columns */
2546   png_set_user_limits(ping,
2547     (png_uint_32) MagickMin(0x7fffffffL,
2548         GetMagickResourceLimit(WidthResource)),
2549     (png_uint_32) MagickMin(0x7fffffffL,
2550         GetMagickResourceLimit(HeightResource)));
2551 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2552
2553   /*
2554     Prepare PNG for reading.
2555   */
2556
2557   mng_info->image_found++;
2558   png_set_sig_bytes(ping,8);
2559
2560   if (LocaleCompare(image_info->magick,"MNG") == 0)
2561     {
2562 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2563       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2564       png_set_read_fn(ping,image,png_get_data);
2565 #else
2566 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2567       png_permit_empty_plte(ping,MagickTrue);
2568       png_set_read_fn(ping,image,png_get_data);
2569 #else
2570       mng_info->image=image;
2571       mng_info->bytes_in_read_buffer=0;
2572       mng_info->found_empty_plte=MagickFalse;
2573       mng_info->have_saved_bkgd_index=MagickFalse;
2574       png_set_read_fn(ping,mng_info,mng_get_data);
2575 #endif
2576 #endif
2577     }
2578
2579   else
2580     png_set_read_fn(ping,image,png_get_data);
2581
2582   {
2583     const char
2584       *value;
2585
2586     value=GetImageOption(image_info,"profile:skip");
2587
2588     if (IsOptionMember("ICC",value) == MagickFalse)
2589     {
2590
2591        value=GetImageOption(image_info,"png:preserve-iCCP");
2592
2593        if (value == NULL)
2594           value=GetImageArtifact(image,"png:preserve-iCCP");
2595
2596        if (value != NULL)
2597           ping_preserve_iCCP=MagickTrue;
2598
2599 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2600        /* Don't let libpng check for ICC/sRGB profile because we're going
2601         * to do that anyway.  This feature was added at libpng-1.6.12.
2602         * If logging, go ahead and check and issue a warning as appropriate.
2603         */
2604        if (logging == MagickFalse)
2605           png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2606 #endif
2607     }
2608 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2609     else
2610     {
2611        png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
2612     }
2613 #endif
2614   }
2615 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2616   /* Ignore unused chunks and all unknown chunks except for eXIf,
2617      zxIf, caNv, and vpAg */
2618 # if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2619   png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2620 # else
2621   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2622 # endif
2623 #if defined(zxIf_SUPPORTED)
2624   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_uxIf, 1);
2625   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_zxIf, 1);
2626 #endif
2627   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_eXIf, 1);
2628   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
2629   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
2630   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2631      (int)sizeof(unused_chunks)/5);
2632   /* Callback for other unknown chunks */
2633   png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
2634 #endif
2635
2636 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2637 #  if (PNG_LIBPNG_VER >= 10400)
2638     /* Limit the size of the chunk storage cache used for sPLT, text,
2639      * and unknown chunks.
2640      */
2641     png_set_chunk_cache_max(ping, 32767);
2642 #  endif
2643 #endif
2644
2645 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2646     /* Disable new libpng-1.5.10 feature */
2647     png_set_check_for_invalid_index (ping, 0);
2648 #endif
2649
2650 #if (PNG_LIBPNG_VER < 10400)
2651 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2652    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2653   /* Disable thread-unsafe features of pnggccrd */
2654   if (png_access_version_number() >= 10200)
2655   {
2656     png_uint_32 mmx_disable_mask=0;
2657     png_uint_32 asm_flags;
2658
2659     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2660                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2661                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2662                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2663     asm_flags=png_get_asm_flags(ping);
2664     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2665   }
2666 #  endif
2667 #endif
2668
2669   png_read_info(ping,ping_info);
2670
2671   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2672                &ping_bit_depth,&ping_color_type,
2673                &ping_interlace_method,&ping_compression_method,
2674                &ping_filter_method);
2675
2676   ping_file_depth = ping_bit_depth;
2677
2678   /* Swap bytes if requested */
2679   if (ping_file_depth == 16)
2680   {
2681      const char
2682        *value;
2683
2684      value=GetImageOption(image_info,"png:swap-bytes");
2685
2686      if (value == NULL)
2687         value=GetImageArtifact(image,"png:swap-bytes");
2688
2689      if (value != NULL)
2690         png_set_swap(ping);
2691   }
2692
2693   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2694   {
2695       char
2696         msg[MagickPathExtent];
2697
2698       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2699          (int) ping_color_type);
2700       (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2701
2702       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2703          (int) ping_bit_depth);
2704       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2705   }
2706
2707   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2708                       &ping_trans_color);
2709
2710   (void) png_get_bKGD(ping, ping_info, &ping_background);
2711
2712   if (ping_bit_depth < 8)
2713     {
2714        png_set_packing(ping);
2715        ping_bit_depth = 8;
2716     }
2717
2718   image->depth=ping_bit_depth;
2719   image->depth=GetImageQuantumDepth(image,MagickFalse);
2720   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2721
2722   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2723       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2724     {
2725       image->rendering_intent=UndefinedIntent;
2726       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2727       (void) ResetMagickMemory(&image->chromaticity,0,
2728         sizeof(image->chromaticity));
2729     }
2730
2731   if (logging != MagickFalse)
2732     {
2733       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2734         "    PNG width: %.20g, height: %.20g\n"
2735         "    PNG color_type: %d, bit_depth: %d\n"
2736         "    PNG compression_method: %d\n"
2737         "    PNG interlace_method: %d, filter_method: %d",
2738         (double) ping_width, (double) ping_height,
2739         ping_color_type, ping_bit_depth,
2740         ping_compression_method,
2741         ping_interlace_method,ping_filter_method);
2742
2743     }
2744
2745   if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2746     {
2747       ping_found_iCCP=MagickTrue;
2748       if (logging != MagickFalse)
2749         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2750           "    Found PNG iCCP chunk.");
2751     }
2752
2753   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2754     {
2755       ping_found_gAMA=MagickTrue;
2756       if (logging != MagickFalse)
2757         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2758           "    Found PNG gAMA chunk.");
2759     }
2760
2761   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2762     {
2763       ping_found_cHRM=MagickTrue;
2764       if (logging != MagickFalse)
2765         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2766           "    Found PNG cHRM chunk.");
2767     }
2768
2769   if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2770       PNG_INFO_sRGB))
2771     {
2772       ping_found_sRGB=MagickTrue;
2773       if (logging != MagickFalse)
2774         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2775           "    Found PNG sRGB chunk.");
2776     }
2777
2778 #ifdef PNG_READ_iCCP_SUPPORTED
2779     if (ping_found_iCCP !=MagickTrue &&
2780       ping_found_sRGB != MagickTrue &&
2781       png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2782     {
2783       ping_found_iCCP=MagickTrue;
2784       if (logging != MagickFalse)
2785         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2786           "    Found PNG iCCP chunk.");
2787     }
2788
2789   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2790     {
2791       int
2792         compression;
2793
2794 #if (PNG_LIBPNG_VER < 10500)
2795       png_charp
2796         info;
2797 #else
2798       png_bytep
2799         info;
2800 #endif
2801
2802       png_charp
2803         name;
2804
2805       png_uint_32
2806         profile_length;
2807
2808       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2809         &profile_length);
2810
2811       if (profile_length != 0)
2812         {
2813           StringInfo
2814             *profile;
2815
2816           if (logging != MagickFalse)
2817             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2818               "    Reading PNG iCCP chunk.");
2819
2820           profile=BlobToStringInfo(info,profile_length);
2821
2822           if (profile == (StringInfo *) NULL)
2823           {
2824             png_warning(ping, "ICC profile is NULL");
2825             profile=DestroyStringInfo(profile);
2826           }
2827           else
2828           {
2829             if (ping_preserve_iCCP == MagickFalse)
2830             {
2831                  int
2832                    icheck,
2833                    got_crc=0;
2834
2835
2836                  png_uint_32
2837                    length,
2838                    profile_crc=0;
2839
2840                  unsigned char
2841                    *data;
2842
2843                  length=(png_uint_32) GetStringInfoLength(profile);
2844
2845                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2846                  {
2847                    if (length == sRGB_info[icheck].len)
2848                    {
2849                      if (got_crc == 0)
2850                      {
2851                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2852                          "    Got a %lu-byte ICC profile (potentially sRGB)",
2853                          (unsigned long) length);
2854
2855                        data=GetStringInfoDatum(profile);
2856                        profile_crc=crc32(0,data,length);
2857
2858                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2859                            "      with crc=%8x",(unsigned int) profile_crc);
2860                        got_crc++;
2861                      }
2862
2863                      if (profile_crc == sRGB_info[icheck].crc)
2864                      {
2865                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2866                             "      It is sRGB with rendering intent = %s",
2867                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
2868                              sRGB_info[icheck].intent));
2869                         if (image->rendering_intent==UndefinedIntent)
2870                         {
2871                           image->rendering_intent=
2872                           Magick_RenderingIntent_from_PNG_RenderingIntent(
2873                              sRGB_info[icheck].intent);
2874                         }
2875                         break;
2876                      }
2877                    }
2878                  }
2879                  if (sRGB_info[icheck].len == 0)
2880                  {
2881                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2882                         "    Got %lu-byte ICC profile not recognized as sRGB",
2883                         (unsigned long) length);
2884                     (void) SetImageProfile(image,"icc",profile,exception);
2885                  }
2886             }
2887             else /* Preserve-iCCP */
2888             {
2889                     (void) SetImageProfile(image,"icc",profile,exception);
2890             }
2891
2892             profile=DestroyStringInfo(profile);
2893           }
2894       }
2895     }
2896 #endif
2897
2898 #if defined(PNG_READ_sRGB_SUPPORTED)
2899   {
2900     if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2901         PNG_INFO_sRGB))
2902     {
2903       if (png_get_sRGB(ping,ping_info,&intent))
2904       {
2905         if (image->rendering_intent == UndefinedIntent)
2906           image->rendering_intent=
2907              Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2908
2909         if (logging != MagickFalse)
2910           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2911             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2912       }
2913     }
2914
2915     else if (mng_info->have_global_srgb)
2916       {
2917         if (image->rendering_intent == UndefinedIntent)
2918           image->rendering_intent=
2919             Magick_RenderingIntent_from_PNG_RenderingIntent
2920             (mng_info->global_srgb_intent);
2921       }
2922   }
2923 #endif
2924
2925
2926   {
2927      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2928        if (mng_info->have_global_gama)
2929          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2930
2931      if (png_get_gAMA(ping,ping_info,&file_gamma))
2932        {
2933          image->gamma=(float) file_gamma;
2934          if (logging != MagickFalse)
2935            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2936              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2937        }
2938   }
2939
2940   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2941     {
2942       if (mng_info->have_global_chrm != MagickFalse)
2943         {
2944           (void) png_set_cHRM(ping,ping_info,
2945             mng_info->global_chrm.white_point.x,
2946             mng_info->global_chrm.white_point.y,
2947             mng_info->global_chrm.red_primary.x,
2948             mng_info->global_chrm.red_primary.y,
2949             mng_info->global_chrm.green_primary.x,
2950             mng_info->global_chrm.green_primary.y,
2951             mng_info->global_chrm.blue_primary.x,
2952             mng_info->global_chrm.blue_primary.y);
2953         }
2954     }
2955
2956   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2957     {
2958       (void) png_get_cHRM(ping,ping_info,
2959         &image->chromaticity.white_point.x,
2960         &image->chromaticity.white_point.y,
2961         &image->chromaticity.red_primary.x,
2962         &image->chromaticity.red_primary.y,
2963         &image->chromaticity.green_primary.x,
2964         &image->chromaticity.green_primary.y,
2965         &image->chromaticity.blue_primary.x,
2966         &image->chromaticity.blue_primary.y);
2967
2968        ping_found_cHRM=MagickTrue;
2969
2970        if (image->chromaticity.red_primary.x>0.6399f &&
2971            image->chromaticity.red_primary.x<0.6401f &&
2972            image->chromaticity.red_primary.y>0.3299f &&
2973            image->chromaticity.red_primary.y<0.3301f &&
2974            image->chromaticity.green_primary.x>0.2999f &&
2975            image->chromaticity.green_primary.x<0.3001f &&
2976            image->chromaticity.green_primary.y>0.5999f &&
2977            image->chromaticity.green_primary.y<0.6001f &&
2978            image->chromaticity.blue_primary.x>0.1499f &&
2979            image->chromaticity.blue_primary.x<0.1501f &&
2980            image->chromaticity.blue_primary.y>0.0599f &&
2981            image->chromaticity.blue_primary.y<0.0601f &&
2982            image->chromaticity.white_point.x>0.3126f &&
2983            image->chromaticity.white_point.x<0.3128f &&
2984            image->chromaticity.white_point.y>0.3289f &&
2985            image->chromaticity.white_point.y<0.3291f)
2986           ping_found_sRGB_cHRM=MagickTrue;
2987     }
2988
2989   if (image->rendering_intent != UndefinedIntent)
2990     {
2991       if (ping_found_sRGB != MagickTrue &&
2992           (ping_found_gAMA != MagickTrue ||
2993           (image->gamma > .45 && image->gamma < .46)) &&
2994           (ping_found_cHRM != MagickTrue ||
2995           ping_found_sRGB_cHRM != MagickFalse) &&
2996           ping_found_iCCP != MagickTrue)
2997       {
2998          png_set_sRGB(ping,ping_info,
2999             Magick_RenderingIntent_to_PNG_RenderingIntent
3000             (image->rendering_intent));
3001          file_gamma=1.000f/2.200f;
3002          ping_found_sRGB=MagickTrue;
3003          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3004            "    Setting sRGB as if in input");
3005       }
3006     }
3007
3008 #if defined(PNG_oFFs_SUPPORTED)
3009   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3010     {
3011       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
3012       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
3013
3014       if (logging != MagickFalse)
3015         if (image->page.x || image->page.y)
3016           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3017             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
3018             image->page.x,(double) image->page.y);
3019     }
3020 #endif
3021 #if defined(PNG_pHYs_SUPPORTED)
3022   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3023     {
3024       if (mng_info->have_global_phys)
3025         {
3026           png_set_pHYs(ping,ping_info,
3027                        mng_info->global_x_pixels_per_unit,
3028                        mng_info->global_y_pixels_per_unit,
3029                        mng_info->global_phys_unit_type);
3030         }
3031     }
3032
3033   x_resolution=0;
3034   y_resolution=0;
3035   unit_type=0;
3036   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3037     {
3038       /*
3039         Set image resolution.
3040       */
3041       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
3042         &unit_type);
3043       image->resolution.x=(double) x_resolution;
3044       image->resolution.y=(double) y_resolution;
3045
3046       if (unit_type == PNG_RESOLUTION_METER)
3047         {
3048           image->units=PixelsPerCentimeterResolution;
3049           image->resolution.x=(double) x_resolution/100.0;
3050           image->resolution.y=(double) y_resolution/100.0;
3051         }
3052
3053       if (logging != MagickFalse)
3054         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3055           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
3056           (double) x_resolution,(double) y_resolution,unit_type);
3057     }
3058 #endif
3059
3060   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3061     {
3062       png_colorp
3063         palette;
3064
3065       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3066
3067       if ((number_colors == 0) &&
3068           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
3069         {
3070           if (mng_info->global_plte_length)
3071             {
3072               png_set_PLTE(ping,ping_info,mng_info->global_plte,
3073                 (int) mng_info->global_plte_length);
3074
3075               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3076               {
3077                 if (mng_info->global_trns_length)
3078                   {
3079                     png_warning(ping,
3080                       "global tRNS has more entries than global PLTE");
3081                   }
3082                 else
3083                   {
3084                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
3085                        (int) mng_info->global_trns_length,NULL);
3086                   }
3087                }
3088 #ifdef PNG_READ_bKGD_SUPPORTED
3089               if (
3090 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3091                    mng_info->have_saved_bkgd_index ||
3092 #endif
3093                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3094                     {
3095                       png_color_16
3096                          background;
3097
3098 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3099                       if (mng_info->have_saved_bkgd_index)
3100                         background.index=mng_info->saved_bkgd_index;
3101 #endif
3102                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
3103                         background.index=ping_background->index;
3104
3105                       background.red=(png_uint_16)
3106                         mng_info->global_plte[background.index].red;
3107
3108                       background.green=(png_uint_16)
3109                         mng_info->global_plte[background.index].green;
3110
3111                       background.blue=(png_uint_16)
3112                         mng_info->global_plte[background.index].blue;
3113
3114                       background.gray=(png_uint_16)
3115                         mng_info->global_plte[background.index].green;
3116
3117                       png_set_bKGD(ping,ping_info,&background);
3118                     }
3119 #endif
3120                 }
3121               else
3122                 png_error(ping,"No global PLTE in file");
3123             }
3124         }
3125
3126 #ifdef PNG_READ_bKGD_SUPPORTED
3127   if (mng_info->have_global_bkgd &&
3128           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
3129       image->background_color=mng_info->mng_global_bkgd;
3130
3131   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3132     {
3133       unsigned int
3134         bkgd_scale;
3135
3136       /* Set image background color.
3137        * Scale background components to 16-bit, then scale
3138        * to quantum depth
3139        */
3140
3141         bkgd_scale = 1;
3142
3143         if (ping_file_depth == 1)
3144            bkgd_scale = 255;
3145
3146         else if (ping_file_depth == 2)
3147            bkgd_scale = 85;
3148
3149         else if (ping_file_depth == 4)
3150            bkgd_scale = 17;
3151
3152         if (ping_file_depth <= 8)
3153            bkgd_scale *= 257;
3154
3155         ping_background->red *= bkgd_scale;
3156         ping_background->green *= bkgd_scale;
3157         ping_background->blue *= bkgd_scale;
3158
3159         if (logging != MagickFalse)
3160           {
3161             if (logging != MagickFalse)
3162               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3163                  "    Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
3164                  "    bkgd_scale=%d.  ping_background=(%d,%d,%d)",
3165                  ping_background->red,ping_background->green,
3166                  ping_background->blue,
3167                  bkgd_scale,ping_background->red,
3168                  ping_background->green,ping_background->blue);
3169           }
3170
3171         image->background_color.red=
3172             ScaleShortToQuantum(ping_background->red);
3173
3174         image->background_color.green=
3175             ScaleShortToQuantum(ping_background->green);
3176
3177         image->background_color.blue=
3178           ScaleShortToQuantum(ping_background->blue);
3179
3180         image->background_color.alpha=OpaqueAlpha;
3181
3182         if (logging != MagickFalse)
3183           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3184             "    image->background_color=(%.20g,%.20g,%.20g).",
3185             (double) image->background_color.red,
3186             (double) image->background_color.green,
3187             (double) image->background_color.blue);
3188     }
3189 #endif /* PNG_READ_bKGD_SUPPORTED */
3190
3191   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3192     {
3193       /*
3194         Image has a tRNS chunk.
3195       */
3196       int
3197         max_sample;
3198
3199       size_t
3200         one=1;
3201
3202       if (logging != MagickFalse)
3203         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3204           "    Reading PNG tRNS chunk.");
3205
3206       max_sample = (int) ((one << ping_file_depth) - 1);
3207
3208       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3209           (int)ping_trans_color->gray > max_sample) ||
3210           (ping_color_type == PNG_COLOR_TYPE_RGB &&
3211           ((int)ping_trans_color->red > max_sample ||
3212           (int)ping_trans_color->green > max_sample ||
3213           (int)ping_trans_color->blue > max_sample)))
3214         {
3215           if (logging != MagickFalse)
3216             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3217               "    Ignoring PNG tRNS chunk with out-of-range sample.");
3218           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3219           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3220           image->alpha_trait=UndefinedPixelTrait;
3221         }
3222       else
3223         {
3224           int
3225             scale_to_short;
3226
3227           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3228
3229           /* Scale transparent_color to short */
3230           transparent_color.red= scale_to_short*ping_trans_color->red;
3231           transparent_color.green= scale_to_short*ping_trans_color->green;
3232           transparent_color.blue= scale_to_short*ping_trans_color->blue;
3233           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3234
3235           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3236             {
3237               if (logging != MagickFalse)
3238               {
3239                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3240                   "    Raw tRNS graylevel = %d, scaled graylevel = %d.",
3241                   (int) ping_trans_color->gray,(int) transparent_color.alpha);
3242
3243               }
3244               transparent_color.red=transparent_color.alpha;
3245               transparent_color.green=transparent_color.alpha;
3246               transparent_color.blue=transparent_color.alpha;
3247             }
3248         }
3249     }
3250 #if defined(PNG_READ_sBIT_SUPPORTED)
3251   if (mng_info->have_global_sbit)
3252     {
3253       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3254         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3255     }
3256 #endif
3257   num_passes=png_set_interlace_handling(ping);
3258
3259   png_read_update_info(ping,ping_info);
3260
3261   ping_rowbytes=png_get_rowbytes(ping,ping_info);
3262
3263   /*
3264     Initialize image structure.
3265   */
3266   mng_info->image_box.left=0;
3267   mng_info->image_box.right=(ssize_t) ping_width;
3268   mng_info->image_box.top=0;
3269   mng_info->image_box.bottom=(ssize_t) ping_height;
3270   if (mng_info->mng_type == 0)
3271     {
3272       mng_info->mng_width=ping_width;
3273       mng_info->mng_height=ping_height;
3274       mng_info->frame=mng_info->image_box;
3275       mng_info->clip=mng_info->image_box;
3276     }
3277
3278   else
3279     {
3280       image->page.y=mng_info->y_off[mng_info->object_id];
3281     }
3282
3283   image->compression=ZipCompression;
3284   image->columns=ping_width;
3285   image->rows=ping_height;
3286
3287   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3288       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3289     {
3290       double
3291         image_gamma = image->gamma;
3292
3293       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3294          "    image->gamma=%f",(float) image_gamma);
3295
3296       if (image_gamma > 0.75)
3297         {
3298           /* Set image->rendering_intent to Undefined,
3299            * image->colorspace to GRAY, and reset image->chromaticity.
3300            */
3301           image->intensity = Rec709LuminancePixelIntensityMethod;
3302           SetImageColorspace(image,GRAYColorspace,exception);
3303         }
3304       else
3305         {
3306           RenderingIntent
3307             save_rendering_intent = image->rendering_intent;
3308           ChromaticityInfo
3309             save_chromaticity = image->chromaticity;
3310
3311           SetImageColorspace(image,GRAYColorspace,exception);
3312           image->rendering_intent = save_rendering_intent;
3313           image->chromaticity = save_chromaticity;
3314         }
3315
3316       image->gamma = image_gamma;
3317     }
3318
3319   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3320       "    image->colorspace=%d",(int) image->colorspace);
3321
3322   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3323       ((int) ping_bit_depth < 16 &&
3324       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3325     {
3326       size_t
3327         one;
3328
3329       image->storage_class=PseudoClass;
3330       one=1;
3331       image->colors=one << ping_file_depth;
3332 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3333       if (image->colors > 256)
3334         image->colors=256;
3335 #else
3336       if (image->colors > 65536L)
3337         image->colors=65536L;
3338 #endif
3339       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3340         {
3341           png_colorp
3342             palette;
3343
3344           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3345           image->colors=(size_t) number_colors;
3346
3347           if (logging != MagickFalse)
3348             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3349               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3350         }
3351     }
3352
3353   if (image->storage_class == PseudoClass)
3354     {
3355       /*
3356         Initialize image colormap.
3357       */
3358       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3359         png_error(ping,"Memory allocation failed");
3360
3361       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3362         {
3363           png_colorp
3364             palette;
3365
3366           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3367
3368           for (i=0; i < (ssize_t) number_colors; i++)
3369           {
3370             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3371             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3372             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3373           }
3374
3375           for ( ; i < (ssize_t) image->colors; i++)
3376           {
3377             image->colormap[i].red=0;
3378             image->colormap[i].green=0;
3379             image->colormap[i].blue=0;
3380           }
3381         }
3382
3383       else
3384         {
3385           Quantum
3386             scale;
3387
3388           scale = (Quantum) (65535.0/((1UL << ping_file_depth)-1.0));
3389
3390 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
3391           scale = ScaleShortToQuantum(scale);
3392 #endif
3393
3394           for (i=0; i < (ssize_t) image->colors; i++)
3395           {
3396             image->colormap[i].red=(Quantum) (i*scale);
3397             image->colormap[i].green=(Quantum) (i*scale);
3398             image->colormap[i].blue=(Quantum) (i*scale);
3399           }
3400        }
3401     }
3402
3403    /* Set some properties for reporting by "identify" */
3404     {
3405       char
3406         msg[MagickPathExtent];
3407
3408      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3409         ping_interlace_method in value */
3410
3411      (void) FormatLocaleString(msg,MagickPathExtent,
3412          "%d, %d",(int) ping_width, (int) ping_height);
3413      (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3414
3415      (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3416         (int) ping_file_depth);
3417      (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3418
3419      (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3420          (int) ping_color_type,
3421          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3422      (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3423
3424      if (ping_interlace_method == 0)
3425        {
3426          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3427             (int) ping_interlace_method);
3428        }
3429      else if (ping_interlace_method == 1)
3430        {
3431          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3432             (int) ping_interlace_method);
3433        }
3434      else
3435        {
3436          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3437             (int) ping_interlace_method);
3438        }
3439        (void) SetImageProperty(image,"png:IHDR.interlace_method",
3440          msg,exception);
3441
3442      if (number_colors != 0)
3443        {
3444          (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3445             (int) number_colors);
3446          (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3447             exception);
3448        }
3449    }
3450 #if defined(PNG_tIME_SUPPORTED)
3451    read_tIME_chunk(image,ping,ping_info,exception);
3452 #endif
3453
3454
3455   /*
3456     Read image scanlines.
3457   */
3458   if (image->delay != 0)
3459     mng_info->scenes_found++;
3460
3461   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3462       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3463       (image_info->first_scene+image_info->number_scenes))))
3464     {
3465       /* This happens later in non-ping decodes */
3466       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3467         image->storage_class=DirectClass;
3468       image->alpha_trait=
3469         (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3470          ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3471          (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3472         BlendPixelTrait : UndefinedPixelTrait;
3473
3474       if (logging != MagickFalse)
3475         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3476           "    Skipping PNG image data for scene %.20g",(double)
3477           mng_info->scenes_found-1);
3478       png_destroy_read_struct(&ping,&ping_info,&end_info);
3479
3480 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3481       UnlockSemaphoreInfo(ping_semaphore);
3482 #endif
3483
3484       if (logging != MagickFalse)
3485         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3486           "  exit ReadOnePNGImage().");
3487
3488       return(image);
3489     }
3490
3491   if (logging != MagickFalse)
3492     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3493       "    Reading PNG IDAT chunk(s)");
3494
3495   status=SetImageExtent(image,image->columns,image->rows,exception);
3496   if (status == MagickFalse)
3497     return(DestroyImageList(image));
3498
3499   if (num_passes > 1)
3500     pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3501       sizeof(*ping_pixels));
3502   else
3503     pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3504
3505   if (pixel_info == (MemoryInfo *) NULL)
3506     png_error(ping,"Memory allocation failed");
3507   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3508
3509   if (logging != MagickFalse)
3510     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3511       "    Converting PNG pixels to pixel packets");
3512   /*
3513     Convert PNG pixels to pixel packets.
3514   */
3515   quantum_info=AcquireQuantumInfo(image_info,image);
3516
3517   if (quantum_info == (QuantumInfo *) NULL)
3518      png_error(ping,"Failed to allocate quantum_info");
3519
3520   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3521
3522   {
3523
3524    MagickBooleanType
3525      found_transparent_pixel;
3526
3527   found_transparent_pixel=MagickFalse;
3528
3529   if (image->storage_class == DirectClass)
3530     {
3531       for (pass=0; pass < num_passes; pass++)
3532       {
3533         /*
3534           Convert image to DirectClass pixel packets.
3535         */
3536         image->alpha_trait=
3537             (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3538             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3539             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3540             BlendPixelTrait : UndefinedPixelTrait;
3541
3542         for (y=0; y < (ssize_t) image->rows; y++)
3543         {
3544           if (num_passes > 1)
3545             row_offset=ping_rowbytes*y;
3546
3547           else
3548             row_offset=0;
3549
3550           png_read_row(ping,ping_pixels+row_offset,NULL);
3551
3552           if (pass < num_passes-1)
3553             continue;
3554
3555           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3556
3557           if (q == (Quantum *) NULL)
3558             break;
3559
3560           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3561             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3562               GrayQuantum,ping_pixels+row_offset,exception);
3563
3564           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3565             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3566               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3567
3568           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3569             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3570               RGBAQuantum,ping_pixels+row_offset,exception);
3571
3572           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3573             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3574               IndexQuantum,ping_pixels+row_offset,exception);
3575
3576           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3577             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3578               RGBQuantum,ping_pixels+row_offset,exception);
3579
3580           if (found_transparent_pixel == MagickFalse)
3581             {
3582               /* Is there a transparent pixel in the row? */
3583               if (y== 0 && logging != MagickFalse)
3584                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3585                    "    Looking for cheap transparent pixel");
3586
3587               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3588               {
3589                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3590                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3591                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3592                   {
3593                     if (logging != MagickFalse)
3594                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3595                         "    ...got one.");
3596
3597                     found_transparent_pixel = MagickTrue;
3598                     break;
3599                   }
3600                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3601                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3602                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3603                     transparent_color.red &&
3604                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3605                     transparent_color.green &&
3606                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3607                     transparent_color.blue))
3608                   {
3609                     if (logging != MagickFalse)
3610                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3611                         "    ...got one.");
3612                     found_transparent_pixel = MagickTrue;
3613                     break;
3614                   }
3615                 q+=GetPixelChannels(image);
3616               }
3617             }
3618
3619           if (num_passes == 1)
3620             {
3621               status=SetImageProgress(image,LoadImageTag,
3622                   (MagickOffsetType) y, image->rows);
3623
3624               if (status == MagickFalse)
3625                 break;
3626             }
3627           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3628             break;
3629         }
3630
3631         if (num_passes != 1)
3632           {
3633             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3634             if (status == MagickFalse)
3635               break;
3636           }
3637       }
3638     }
3639
3640   else /* image->storage_class != DirectClass */
3641
3642     for (pass=0; pass < num_passes; pass++)
3643     {
3644       Quantum
3645         *quantum_scanline;
3646
3647       register Quantum
3648         *r;
3649
3650       /*
3651         Convert grayscale image to PseudoClass pixel packets.
3652       */
3653       if (logging != MagickFalse)
3654         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3655           "    Converting grayscale pixels to pixel packets");
3656
3657       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3658         BlendPixelTrait : UndefinedPixelTrait;
3659
3660       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3661         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3662         sizeof(*quantum_scanline));
3663
3664       if (quantum_scanline == (Quantum *) NULL)
3665         png_error(ping,"Memory allocation failed");
3666
3667       for (y=0; y < (ssize_t) image->rows; y++)
3668       {
3669         Quantum
3670            alpha;
3671
3672         if (num_passes > 1)
3673           row_offset=ping_rowbytes*y;
3674
3675         else
3676           row_offset=0;
3677
3678         png_read_row(ping,ping_pixels+row_offset,NULL);
3679
3680         if (pass < num_passes-1)
3681           continue;
3682
3683         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3684
3685         if (q == (Quantum *) NULL)
3686           break;
3687
3688         p=ping_pixels+row_offset;
3689         r=quantum_scanline;
3690
3691         switch (ping_bit_depth)
3692         {
3693           case 8:
3694           {
3695
3696             if (ping_color_type == 4)
3697               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3698               {
3699                 *r++=*p++;
3700
3701                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3702
3703                 SetPixelAlpha(image,alpha,q);
3704
3705                 if (alpha != OpaqueAlpha)
3706                   found_transparent_pixel = MagickTrue;
3707
3708                 q+=GetPixelChannels(image);
3709               }
3710
3711             else
3712               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3713                 *r++=*p++;
3714
3715             break;
3716           }
3717
3718           case 16:
3719           {
3720             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3721             {
3722 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3723               unsigned short
3724                 quantum;
3725
3726               if (image->colors > 256)
3727                 quantum=((*p++) << 8);
3728
3729               else
3730                 quantum=0;
3731
3732               quantum|=(*p++);
3733               *r=ScaleShortToQuantum(quantum);
3734               r++;
3735
3736               if (ping_color_type == 4)
3737                 {
3738                   if (image->colors > 256)
3739                     quantum=((*p++) << 8);
3740                   else
3741                     quantum=0;
3742
3743                   quantum|=(*p++);
3744
3745                   alpha=ScaleShortToQuantum(quantum);
3746                   SetPixelAlpha(image,alpha,q);
3747
3748                   if (alpha != OpaqueAlpha)
3749                     found_transparent_pixel = MagickTrue;
3750
3751                   q+=GetPixelChannels(image);
3752                 }
3753
3754 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3755               *r++=(*p++);
3756               p++; /* strip low byte */
3757
3758               if (ping_color_type == 4)
3759                 {
3760                   SetPixelAlpha(image,*p++,q);
3761
3762                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3763                     found_transparent_pixel = MagickTrue;
3764
3765                   p++;
3766                   q+=GetPixelChannels(image);
3767                 }
3768 #endif
3769             }
3770
3771             break;
3772           }
3773
3774           default:
3775             break;
3776         }
3777
3778         /*
3779           Transfer image scanline.
3780         */
3781         r=quantum_scanline;
3782
3783         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3784
3785         if (q == (Quantum *) NULL)
3786           break;
3787         for (x=0; x < (ssize_t) image->columns; x++)
3788         {
3789           SetPixelIndex(image,*r++,q);
3790           q+=GetPixelChannels(image);
3791         }
3792
3793         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3794           break;
3795
3796         if (num_passes == 1)
3797           {
3798             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3799               image->rows);
3800
3801             if (status == MagickFalse)
3802               break;
3803           }
3804       }
3805
3806       if (num_passes != 1)
3807         {
3808           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3809
3810           if (status == MagickFalse)
3811             break;
3812         }
3813
3814       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3815     }
3816
3817     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3818       UndefinedPixelTrait;
3819
3820     if (logging != MagickFalse)
3821       {
3822         if (found_transparent_pixel != MagickFalse)
3823           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3824             "    Found transparent pixel");
3825         else
3826           {
3827             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3828               "    No transparent pixel was found");
3829
3830             ping_color_type&=0x03;
3831           }
3832       }
3833     }
3834
3835   quantum_info=DestroyQuantumInfo(quantum_info);
3836
3837   if (image->storage_class == PseudoClass)
3838     {
3839       PixelTrait
3840         alpha_trait;
3841
3842       alpha_trait=image->alpha_trait;
3843       image->alpha_trait=UndefinedPixelTrait;
3844       (void) SyncImage(image,exception);
3845       image->alpha_trait=alpha_trait;
3846     }
3847
3848   png_read_end(ping,end_info);
3849
3850   if (logging != MagickFalse)
3851   {
3852     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3853        "  image->storage_class=%d\n",(int) image->storage_class);
3854   }
3855
3856   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3857       (ssize_t) image_info->first_scene && image->delay != 0)
3858     {
3859       png_destroy_read_struct(&ping,&ping_info,&end_info);
3860       pixel_info=RelinquishVirtualMemory(pixel_info);
3861       image->colors=2;
3862       (void) SetImageBackgroundColor(image,exception);
3863 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3864       UnlockSemaphoreInfo(ping_semaphore);
3865 #endif
3866       if (logging != MagickFalse)
3867         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3868           "  exit ReadOnePNGImage() early.");
3869       return(image);
3870     }
3871
3872   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3873     {
3874       ClassType
3875         storage_class;
3876
3877       /*
3878         Image has a transparent background.
3879       */
3880       storage_class=image->storage_class;
3881       image->alpha_trait=BlendPixelTrait;
3882
3883 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3884
3885       if (storage_class == PseudoClass)
3886         {
3887           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3888             {
3889               for (x=0; x < ping_num_trans; x++)
3890               {
3891                  image->colormap[x].alpha_trait=BlendPixelTrait;
3892                  image->colormap[x].alpha =
3893                    ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3894               }
3895             }
3896
3897           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3898             {
3899               for (x=0; x < (int) image->colors; x++)
3900               {
3901                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3902                      transparent_color.alpha)
3903                  {
3904                     image->colormap[x].alpha_trait=BlendPixelTrait;
3905                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3906                  }
3907               }
3908             }
3909           (void) SyncImage(image,exception);
3910         }
3911
3912 #if 1 /* Should have already been done above, but glennrp problem P10
3913        * needs this.
3914        */
3915       else
3916         {
3917           for (y=0; y < (ssize_t) image->rows; y++)
3918           {
3919             image->storage_class=storage_class;
3920             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3921
3922             if (q == (Quantum *) NULL)
3923               break;
3924
3925
3926             /* Caution: on a Q8 build, this does not distinguish between
3927              * 16-bit colors that differ only in the low byte
3928              */
3929             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3930             {
3931               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3932                   transparent_color.red &&
3933                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3934                   transparent_color.green &&
3935                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3936                   transparent_color.blue)
3937                 {
3938                   SetPixelAlpha(image,TransparentAlpha,q);
3939                 }
3940
3941 #if 0 /* I have not found a case where this is needed. */
3942               else
3943                 {
3944                   SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3945                 }
3946 #endif
3947
3948               q+=GetPixelChannels(image);
3949             }
3950
3951             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3952                break;
3953           }
3954         }
3955 #endif
3956
3957       image->storage_class=DirectClass;
3958     }
3959
3960   for (j = 0; j < 2; j++)
3961   {
3962     if (j == 0)
3963       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3964           MagickTrue : MagickFalse;
3965     else
3966       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3967           MagickTrue : MagickFalse;
3968
3969     if (status != MagickFalse)
3970       for (i=0; i < (ssize_t) num_text; i++)
3971       {
3972         /* Check for a profile */
3973
3974         if (logging != MagickFalse)
3975           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3976             "    Reading PNG text chunk");
3977
3978         if (strlen(text[i].key) > 16 &&
3979             memcmp(text[i].key, "Raw profile type ",17) == 0)
3980           {
3981             const char
3982               *value;
3983
3984             value=GetImageOption(image_info,"profile:skip");
3985
3986             if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3987             {
3988                (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3989                   (int) i,exception);
3990                num_raw_profiles++;
3991                if (logging != MagickFalse)
3992                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3993                    "    Read raw profile %s",text[i].key+17);
3994             }
3995             else
3996             {
3997                if (logging != MagickFalse)
3998                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3999                    "    Skipping raw profile %s",text[i].key+17);
4000             }
4001           }
4002
4003         else
4004           {
4005             char
4006               *value;
4007
4008             length=text[i].text_length;
4009             value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
4010               sizeof(*value));
4011             if (value == (char *) NULL)
4012               {
4013                 png_error(ping,"Memory allocation failed");
4014                 break;
4015               }
4016             *value='\0';
4017             (void) ConcatenateMagickString(value,text[i].text,length+2);
4018
4019             /* Don't save "density" or "units" property if we have a pHYs
4020              * chunk
4021              */
4022             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
4023                 (LocaleCompare(text[i].key,"density") != 0 &&
4024                 LocaleCompare(text[i].key,"units") != 0))
4025                (void) SetImageProperty(image,text[i].key,value,exception);
4026
4027             if (logging != MagickFalse)
4028             {
4029               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4030                 "      length: %lu\n"
4031                 "      Keyword: %s",
4032                 (unsigned long) length,
4033                 text[i].key);
4034             }
4035
4036             value=DestroyString(value);
4037           }
4038       }
4039     num_text_total += num_text;
4040   }
4041
4042 #ifdef MNG_OBJECT_BUFFERS
4043   /*
4044     Store the object if necessary.
4045   */
4046   if (object_id && !mng_info->frozen[object_id])
4047     {
4048       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
4049         {
4050           /*
4051             create a new object buffer.
4052           */
4053           mng_info->ob[object_id]=(MngBuffer *)
4054             AcquireMagickMemory(sizeof(MngBuffer));
4055
4056           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
4057             {
4058               mng_info->ob[object_id]->image=(Image *) NULL;
4059               mng_info->ob[object_id]->reference_count=1;
4060             }
4061         }
4062
4063       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
4064           mng_info->ob[object_id]->frozen)
4065         {
4066           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
4067              png_error(ping,"Memory allocation failed");
4068
4069           if (mng_info->ob[object_id]->frozen)
4070             png_error(ping,"Cannot overwrite frozen MNG object buffer");
4071         }
4072
4073       else
4074         {
4075
4076           if (mng_info->ob[object_id]->image != (Image *) NULL)
4077             mng_info->ob[object_id]->image=DestroyImage
4078                 (mng_info->ob[object_id]->image);
4079
4080           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
4081             exception);
4082
4083           if (mng_info->ob[object_id]->image != (Image *) NULL)
4084             mng_info->ob[object_id]->image->file=(FILE *) NULL;
4085
4086           else
4087             png_error(ping, "Cloning image for object buffer failed");
4088
4089           if (ping_width > 250000L || ping_height > 250000L)
4090              png_error(ping,"PNG Image dimensions are too large.");
4091
4092           mng_info->ob[object_id]->width=ping_width;
4093           mng_info->ob[object_id]->height=ping_height;
4094           mng_info->ob[object_id]->color_type=ping_color_type;
4095           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
4096           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
4097           mng_info->ob[object_id]->compression_method=
4098              ping_compression_method;
4099           mng_info->ob[object_id]->filter_method=ping_filter_method;
4100
4101           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
4102             {
4103               png_colorp
4104                 plte;
4105
4106               /*
4107                 Copy the PLTE to the object buffer.
4108               */
4109               png_get_PLTE(ping,ping_info,&plte,&number_colors);
4110               mng_info->ob[object_id]->plte_length=number_colors;
4111
4112               for (i=0; i < number_colors; i++)
4113               {
4114                 mng_info->ob[object_id]->plte[i]=plte[i];
4115               }
4116             }
4117
4118           else
4119               mng_info->ob[object_id]->plte_length=0;
4120         }
4121     }
4122 #endif
4123
4124    /* Set image->alpha_trait to MagickTrue if the input colortype supports
4125     * alpha or if a valid tRNS chunk is present, no matter whether there
4126     * is actual transparency present.
4127     */
4128     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
4129         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
4130         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
4131         BlendPixelTrait : UndefinedPixelTrait;
4132
4133 #if 0  /* I'm not sure what's wrong here but it does not work. */
4134     if (image->alpha_trait != UndefinedPixelTrait)
4135     {
4136       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
4137         (void) SetImageType(image,GrayscaleAlphaType,exception);
4138
4139       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4140         (void) SetImageType(image,PaletteAlphaType,exception);
4141
4142       else
4143         (void) SetImageType(image,TrueColorAlphaType,exception);
4144     }
4145
4146     else
4147     {
4148       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
4149         (void) SetImageType(image,GrayscaleType,exception);
4150
4151       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4152         (void) SetImageType(image,PaletteType,exception);
4153
4154       else
4155         (void) SetImageType(image,TrueColorType,exception);
4156     }
4157 #endif
4158
4159    /* Set more properties for identify to retrieve */
4160    {
4161      char
4162        msg[MagickPathExtent];
4163
4164      if (num_text_total != 0)
4165        {
4166          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
4167          (void) FormatLocaleString(msg,MagickPathExtent,
4168             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
4169          (void) SetImageProperty(image,"png:text",msg,
4170                 exception);
4171        }
4172
4173      if (num_raw_profiles != 0)
4174        {
4175          (void) FormatLocaleString(msg,MagickPathExtent,
4176             "%d were found", num_raw_profiles);
4177          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
4178                 exception);
4179        }
4180
4181      /* cHRM chunk: */
4182      if (ping_found_cHRM != MagickFalse)
4183        {
4184          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4185             "chunk was found (see Chromaticity, above)");
4186          (void) SetImageProperty(image,"png:cHRM",msg,
4187                 exception);
4188        }
4189
4190      /* bKGD chunk: */
4191      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
4192        {
4193          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4194             "chunk was found (see Background color, above)");
4195          (void) SetImageProperty(image,"png:bKGD",msg,
4196                 exception);
4197        }
4198
4199      (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4200         "chunk was found");
4201
4202 #if defined(PNG_iCCP_SUPPORTED)
4203      /* iCCP chunk: */
4204      if (ping_found_iCCP != MagickFalse)
4205         (void) SetImageProperty(image,"png:iCCP",msg,
4206                 exception);
4207 #endif
4208
4209      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
4210         (void) SetImageProperty(image,"png:tRNS",msg,
4211                 exception);
4212
4213 #if defined(PNG_sRGB_SUPPORTED)
4214      /* sRGB chunk: */
4215      if (ping_found_sRGB != MagickFalse)
4216        {
4217          (void) FormatLocaleString(msg,MagickPathExtent,
4218             "intent=%d (%s)",
4219             (int) intent,
4220             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
4221          (void) SetImageProperty(image,"png:sRGB",msg,
4222                  exception);
4223        }
4224 #endif
4225
4226      /* gAMA chunk: */
4227      if (ping_found_gAMA != MagickFalse)
4228        {
4229          (void) FormatLocaleString(msg,MagickPathExtent,
4230             "gamma=%.8g (See Gamma, above)",
4231             file_gamma);
4232          (void) SetImageProperty(image,"png:gAMA",msg,
4233                 exception);
4234        }
4235
4236 #if defined(PNG_pHYs_SUPPORTED)
4237      /* pHYs chunk: */
4238      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
4239        {
4240          (void) FormatLocaleString(msg,MagickPathExtent,
4241             "x_res=%.10g, y_res=%.10g, units=%d",
4242             (double) x_resolution,(double) y_resolution, unit_type);
4243          (void) SetImageProperty(image,"png:pHYs",msg,
4244                 exception);
4245        }
4246 #endif
4247
4248 #if defined(PNG_oFFs_SUPPORTED)
4249      /* oFFs chunk: */
4250      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4251        {
4252          (void) FormatLocaleString(msg,MagickPathExtent,
4253             "x_off=%.20g, y_off=%.20g",
4254             (double) image->page.x,(double) image->page.y);
4255          (void) SetImageProperty(image,"png:oFFs",msg,
4256                 exception);
4257        }
4258 #endif
4259
4260 #if defined(PNG_tIME_SUPPORTED)
4261      read_tIME_chunk(image,ping,end_info,exception);
4262 #endif
4263
4264      /* caNv chunk: */
4265      if ((image->page.width != 0 && image->page.width != image->columns) ||
4266          (image->page.height != 0 && image->page.height != image->rows) ||
4267          (image->page.x != 0 || image->page.y != 0))
4268        {
4269          (void) FormatLocaleString(msg,MagickPathExtent,
4270             "width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
4271             (double) image->page.width,(double) image->page.height,
4272             (double) image->page.x,(double) image->page.y);
4273          (void) SetImageProperty(image,"png:caNv",msg,
4274                 exception);
4275        }
4276
4277      /* vpAg chunk: */
4278      if ((image->page.width != 0 && image->page.width != image->columns) ||
4279          (image->page.height != 0 && image->page.height != image->rows))
4280        {
4281          (void) FormatLocaleString(msg,MagickPathExtent,
4282             "width=%.20g, height=%.20g",
4283             (double) image->page.width,(double) image->page.height);
4284          (void) SetImageProperty(image,"png:vpAg",msg,
4285                 exception);
4286        }
4287    }
4288
4289   /*
4290     Relinquish resources.
4291   */
4292   png_destroy_read_struct(&ping,&ping_info,&end_info);
4293
4294   pixel_info=RelinquishVirtualMemory(pixel_info);
4295
4296   if (logging != MagickFalse)
4297     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4298       "  exit ReadOnePNGImage()");
4299
4300 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4301   UnlockSemaphoreInfo(ping_semaphore);
4302 #endif
4303
4304   /* }  for navigation to beginning of SETJMP-protected block, revert to
4305    *    Throwing an Exception when an error occurs.
4306    */
4307
4308   return(image);
4309
4310 /* end of reading one PNG image */
4311 }
4312
4313 static Image *ReadPNGImage(const ImageInfo *image_info,
4314   ExceptionInfo *exception)
4315 {
4316   Image
4317     *image;
4318
4319   MagickBooleanType
4320     logging,
4321     status;
4322
4323   MngInfo
4324     *mng_info;
4325
4326   char
4327     magic_number[MagickPathExtent];
4328
4329   ssize_t
4330     count;
4331
4332   /*
4333     Open image file.
4334   */
4335   assert(image_info != (const ImageInfo *) NULL);
4336   assert(image_info->signature == MagickCoreSignature);
4337
4338   if (image_info->debug != MagickFalse)
4339     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4340       image_info->filename);
4341
4342   assert(exception != (ExceptionInfo *) NULL);
4343   assert(exception->signature == MagickCoreSignature);
4344   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4345   image=AcquireImage(image_info,exception);
4346   mng_info=(MngInfo *) NULL;
4347   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4348
4349   if (status == MagickFalse)
4350     ThrowReaderException(FileOpenError,"UnableToOpenFile");
4351
4352   /*
4353     Verify PNG signature.
4354   */
4355   count=ReadBlob(image,8,(unsigned char *) magic_number);
4356
4357   if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4358     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4359
4360   /*
4361     Allocate a MngInfo structure.
4362   */
4363   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4364
4365   if (mng_info == (MngInfo *) NULL)
4366     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4367
4368   /*
4369     Initialize members of the MngInfo structure.
4370   */
4371   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4372   mng_info->image=image;
4373
4374   image=ReadOnePNGImage(mng_info,image_info,exception);
4375   mng_info=MngInfoFreeStruct(mng_info);
4376
4377   if (image == (Image *) NULL)
4378     {
4379       if (logging != MagickFalse)
4380         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4381           "exit ReadPNGImage() with error");
4382
4383       return((Image *) NULL);
4384     }
4385
4386   (void) CloseBlob(image);
4387
4388   if ((image->columns == 0) || (image->rows == 0))
4389     {
4390       if (logging != MagickFalse)
4391         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4392           "exit ReadPNGImage() with error.");
4393
4394       ThrowReaderException(CorruptImageError,"CorruptImage");
4395     }
4396
4397   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4398       ((image->gamma < .45) || (image->gamma > .46)) &&
4399            !(image->chromaticity.red_primary.x>0.6399f &&
4400            image->chromaticity.red_primary.x<0.6401f &&
4401            image->chromaticity.red_primary.y>0.3299f &&
4402            image->chromaticity.red_primary.y<0.3301f &&
4403            image->chromaticity.green_primary.x>0.2999f &&
4404            image->chromaticity.green_primary.x<0.3001f &&
4405            image->chromaticity.green_primary.y>0.5999f &&
4406            image->chromaticity.green_primary.y<0.6001f &&
4407            image->chromaticity.blue_primary.x>0.1499f &&
4408            image->chromaticity.blue_primary.x<0.1501f &&
4409            image->chromaticity.blue_primary.y>0.0599f &&
4410            image->chromaticity.blue_primary.y<0.0601f &&
4411            image->chromaticity.white_point.x>0.3126f &&
4412            image->chromaticity.white_point.x<0.3128f &&
4413            image->chromaticity.white_point.y>0.3289f &&
4414            image->chromaticity.white_point.y<0.3291f))
4415     {
4416        SetImageColorspace(image,RGBColorspace,exception);
4417     }
4418
4419   if (logging != MagickFalse)
4420     {
4421        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4422            "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4423                (double) image->page.width,(double) image->page.height,
4424                (double) image->page.x,(double) image->page.y);
4425        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4426            "  image->colorspace: %d", (int) image->colorspace);
4427     }
4428
4429   if (logging != MagickFalse)
4430     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4431
4432   return(image);
4433 }
4434
4435
4436
4437 #if defined(JNG_SUPPORTED)
4438 /*
4439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4440 %                                                                             %
4441 %                                                                             %
4442 %                                                                             %
4443 %   R e a d O n e J N G I m a g e                                             %
4444 %                                                                             %
4445 %                                                                             %
4446 %                                                                             %
4447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4448 %
4449 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4450 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
4451 %  necessary for the new Image structure and returns a pointer to the new
4452 %  image.
4453 %
4454 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4455 %
4456 %  The format of the ReadOneJNGImage method is:
4457 %
4458 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4459 %         ExceptionInfo *exception)
4460 %
4461 %  A description of each parameter follows:
4462 %
4463 %    o mng_info: Specifies a pointer to a MngInfo structure.
4464 %
4465 %    o image_info: the image info.
4466 %
4467 %    o exception: return any errors or warnings in this structure.
4468 %
4469 */
4470 static Image *ReadOneJNGImage(MngInfo *mng_info,
4471     const ImageInfo *image_info, ExceptionInfo *exception)
4472 {
4473   Image
4474     *alpha_image,
4475     *color_image,
4476     *image,
4477     *jng_image;
4478
4479   ImageInfo
4480     *alpha_image_info,
4481     *color_image_info;
4482
4483   MagickBooleanType
4484     logging;
4485
4486   ssize_t
4487     y;
4488
4489   MagickBooleanType
4490     status;
4491
4492   png_uint_32
4493     jng_height,
4494     jng_width;
4495
4496   png_byte
4497     jng_color_type,
4498     jng_image_sample_depth,
4499     jng_image_compression_method,
4500     jng_image_interlace_method,
4501     jng_alpha_sample_depth,
4502     jng_alpha_compression_method,
4503     jng_alpha_filter_method,
4504     jng_alpha_interlace_method;
4505
4506   register const Quantum
4507     *s;
4508
4509   register ssize_t
4510     i,
4511     x;
4512
4513   register Quantum
4514     *q;
4515
4516   register unsigned char
4517     *p;
4518
4519   unsigned int
4520     read_JSEP,
4521     reading_idat;
4522
4523   size_t
4524     length;
4525
4526   jng_alpha_compression_method=0;
4527   jng_alpha_sample_depth=8;
4528   jng_color_type=0;
4529   jng_height=0;
4530   jng_width=0;
4531   alpha_image=(Image *) NULL;
4532   color_image=(Image *) NULL;
4533   alpha_image_info=(ImageInfo *) NULL;
4534   color_image_info=(ImageInfo *) NULL;
4535
4536   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4537     "  Enter ReadOneJNGImage()");
4538
4539   image=mng_info->image;
4540
4541   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4542     {
4543       /*
4544         Allocate next image structure.
4545       */
4546       if (logging != MagickFalse)
4547         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4548            "  AcquireNextImage()");
4549
4550       AcquireNextImage(image_info,image,exception);
4551
4552       if (GetNextImageInList(image) == (Image *) NULL)
4553         return(DestroyImageList(image));
4554
4555       image=SyncNextImageInList(image);
4556     }
4557   mng_info->image=image;
4558
4559   /*
4560     Signature bytes have already been read.
4561   */
4562
4563   read_JSEP=MagickFalse;
4564   reading_idat=MagickFalse;
4565   for (;;)
4566   {
4567     char
4568       type[MagickPathExtent];
4569
4570     unsigned char
4571       *chunk;
4572
4573     unsigned int
4574       count;
4575
4576     /*
4577       Read a new JNG chunk.
4578     */
4579     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4580       2*GetBlobSize(image));
4581
4582     if (status == MagickFalse)
4583       break;
4584
4585     type[0]='\0';
4586     (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4587     length=ReadBlobMSBLong(image);
4588     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4589
4590     if (logging != MagickFalse)
4591       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4592         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4593         type[0],type[1],type[2],type[3],(double) length);
4594
4595     if (length > PNG_UINT_31_MAX || count == 0)
4596       ThrowReaderException(CorruptImageError,"CorruptImage");
4597
4598     p=NULL;
4599     chunk=(unsigned char *) NULL;
4600
4601     if (length != 0)
4602       {
4603         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4604
4605         if (chunk == (unsigned char *) NULL)
4606           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4607
4608         for (i=0; i < (ssize_t) length; i++)
4609           chunk[i]=(unsigned char) ReadBlobByte(image);
4610
4611         p=chunk;
4612       }
4613
4614     (void) ReadBlobMSBLong(image);  /* read crc word */
4615
4616     if (memcmp(type,mng_JHDR,4) == 0)
4617       {
4618         if (length == 16)
4619           {
4620             jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4621               (p[2] << 8) | p[3]);
4622             jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4623               (p[6] << 8) | p[7]);
4624             if ((jng_width == 0) || (jng_height == 0))
4625               ThrowReaderException(CorruptImageError,
4626                 "NegativeOrZeroImageSize");
4627             jng_color_type=p[8];
4628             jng_image_sample_depth=p[9];
4629             jng_image_compression_method=p[10];
4630             jng_image_interlace_method=p[11];
4631
4632             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4633               NoInterlace;
4634
4635             jng_alpha_sample_depth=p[12];
4636             jng_alpha_compression_method=p[13];
4637             jng_alpha_filter_method=p[14];
4638             jng_alpha_interlace_method=p[15];
4639
4640             if (logging != MagickFalse)
4641               {
4642                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4643                   "    jng_width:      %16lu,    jng_height:     %16lu\n"
4644                   "    jng_color_type: %16d,     jng_image_sample_depth: %3d\n"
4645                   "    jng_image_compression_method:%3d",
4646                   (unsigned long) jng_width, (unsigned long) jng_height,
4647                   jng_color_type, jng_image_sample_depth,
4648                   jng_image_compression_method);
4649
4650                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4651                   "    jng_image_interlace_method:  %3d"
4652                   "    jng_alpha_sample_depth:      %3d",
4653                   jng_image_interlace_method,
4654                   jng_alpha_sample_depth);
4655
4656                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4657                   "    jng_alpha_compression_method:%3d\n"
4658                   "    jng_alpha_filter_method:     %3d\n"
4659                   "    jng_alpha_interlace_method:  %3d",
4660                   jng_alpha_compression_method,
4661                   jng_alpha_filter_method,
4662                   jng_alpha_interlace_method);
4663               }
4664           }
4665
4666         if (length != 0)
4667           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4668
4669         continue;
4670       }
4671
4672
4673     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4674         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4675          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4676       {
4677         /*
4678            o create color_image
4679            o open color_blob, attached to color_image
4680            o if (color type has alpha)
4681                open alpha_blob, attached to alpha_image
4682         */
4683
4684         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4685
4686         if (color_image_info == (ImageInfo *) NULL)
4687           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4688
4689         GetImageInfo(color_image_info);
4690         color_image=AcquireImage(color_image_info,exception);
4691
4692         if (color_image == (Image *) NULL)
4693           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4694
4695         if (logging != MagickFalse)
4696           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4697             "    Creating color_blob.");
4698
4699         (void) AcquireUniqueFilename(color_image->filename);
4700         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4701           exception);
4702
4703         if (status == MagickFalse)
4704           {
4705             color_image=DestroyImage(color_image);
4706             return(DestroyImageList(image));
4707           }
4708
4709         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4710           {
4711             alpha_image_info=(ImageInfo *)
4712               AcquireMagickMemory(sizeof(ImageInfo));
4713
4714             if (alpha_image_info == (ImageInfo *) NULL)
4715               {
4716                 color_image=DestroyImage(color_image);
4717                 ThrowReaderException(ResourceLimitError,
4718                   "MemoryAllocationFailed");
4719               }
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_info=DestroyImageInfo(alpha_image_info);
4727                 color_image=DestroyImage(color_image);
4728                 ThrowReaderException(ResourceLimitError,
4729                   "MemoryAllocationFailed");
4730               }
4731
4732             if (logging != MagickFalse)
4733               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4734                 "    Creating alpha_blob.");
4735
4736             (void) AcquireUniqueFilename(alpha_image->filename);
4737             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4738               exception);
4739
4740             if (status == MagickFalse)
4741               {
4742                 alpha_image=DestroyImage(alpha_image);
4743                 alpha_image_info=DestroyImageInfo(alpha_image_info);
4744                 color_image=DestroyImage(color_image);
4745                 return(DestroyImageList(image));
4746               }
4747
4748             if (jng_alpha_compression_method == 0)
4749               {
4750                 unsigned char
4751                   data[18];
4752
4753                 if (logging != MagickFalse)
4754                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4755                     "    Writing IHDR chunk to alpha_blob.");
4756
4757                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4758                   "\211PNG\r\n\032\n");
4759
4760                 (void) WriteBlobMSBULong(alpha_image,13L);
4761                 PNGType(data,mng_IHDR);
4762                 LogPNGChunk(logging,mng_IHDR,13L);
4763                 PNGLong(data+4,jng_width);
4764                 PNGLong(data+8,jng_height);
4765                 data[12]=jng_alpha_sample_depth;
4766                 data[13]=0; /* color_type gray */
4767                 data[14]=0; /* compression method 0 */
4768                 data[15]=0; /* filter_method 0 */
4769                 data[16]=0; /* interlace_method 0 */
4770                 (void) WriteBlob(alpha_image,17,data);
4771                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4772               }
4773           }
4774         reading_idat=MagickTrue;
4775       }
4776
4777     if (memcmp(type,mng_JDAT,4) == 0)
4778       {
4779         /* Copy chunk to color_image->blob */
4780
4781         if (logging != MagickFalse)
4782           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4783             "    Copying JDAT chunk data to color_blob.");
4784
4785         if (length != 0)
4786           {
4787             (void) WriteBlob(color_image,length,chunk);
4788             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4789           }
4790
4791         continue;
4792       }
4793
4794     if (memcmp(type,mng_IDAT,4) == 0)
4795       {
4796         png_byte
4797            data[5];
4798
4799         /* Copy IDAT header and chunk data to alpha_image->blob */
4800
4801         if (alpha_image != NULL && image_info->ping == MagickFalse)
4802           {
4803             if (logging != MagickFalse)
4804               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4805                 "    Copying IDAT chunk data to alpha_blob.");
4806
4807             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4808             PNGType(data,mng_IDAT);
4809             LogPNGChunk(logging,mng_IDAT,length);
4810             (void) WriteBlob(alpha_image,4,data);
4811             (void) WriteBlob(alpha_image,length,chunk);
4812             (void) WriteBlobMSBULong(alpha_image,
4813               crc32(crc32(0,data,4),chunk,(uInt) length));
4814           }
4815
4816         if (length != 0)
4817           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4818
4819         continue;
4820       }
4821
4822     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4823       {
4824         /* Copy chunk data to alpha_image->blob */
4825
4826         if (alpha_image != NULL && image_info->ping == MagickFalse)
4827           {
4828             if (logging != MagickFalse)
4829               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4830                 "    Copying JDAA chunk data to alpha_blob.");
4831
4832             (void) WriteBlob(alpha_image,length,chunk);
4833           }
4834
4835         if (length != 0)
4836           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4837
4838         continue;
4839       }
4840
4841     if (memcmp(type,mng_JSEP,4) == 0)
4842       {
4843         read_JSEP=MagickTrue;
4844
4845         if (length != 0)
4846           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4847
4848         continue;
4849       }
4850
4851     if (memcmp(type,mng_bKGD,4) == 0)
4852       {
4853         if (length == 2)
4854           {
4855             image->background_color.red=ScaleCharToQuantum(p[1]);
4856             image->background_color.green=image->background_color.red;
4857             image->background_color.blue=image->background_color.red;
4858           }
4859
4860         if (length == 6)
4861           {
4862             image->background_color.red=ScaleCharToQuantum(p[1]);
4863             image->background_color.green=ScaleCharToQuantum(p[3]);
4864             image->background_color.blue=ScaleCharToQuantum(p[5]);
4865           }
4866
4867         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4868         continue;
4869       }
4870
4871     if (memcmp(type,mng_gAMA,4) == 0)
4872       {
4873         if (length == 4)
4874           image->gamma=((float) mng_get_long(p))*0.00001;
4875
4876         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4877         continue;
4878       }
4879
4880     if (memcmp(type,mng_cHRM,4) == 0)
4881       {
4882         if (length == 32)
4883           {
4884             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4885             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4886             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4887             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4888             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4889             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4890             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4891             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4892           }
4893
4894         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4895         continue;
4896       }
4897
4898     if (memcmp(type,mng_sRGB,4) == 0)
4899       {
4900         if (length == 1)
4901           {
4902             image->rendering_intent=
4903               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4904             image->gamma=1.000f/2.200f;
4905             image->chromaticity.red_primary.x=0.6400f;
4906             image->chromaticity.red_primary.y=0.3300f;
4907             image->chromaticity.green_primary.x=0.3000f;
4908             image->chromaticity.green_primary.y=0.6000f;
4909             image->chromaticity.blue_primary.x=0.1500f;
4910             image->chromaticity.blue_primary.y=0.0600f;
4911             image->chromaticity.white_point.x=0.3127f;
4912             image->chromaticity.white_point.y=0.3290f;
4913           }
4914
4915         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4916         continue;
4917       }
4918
4919     if (memcmp(type,mng_oFFs,4) == 0)
4920       {
4921         if (length > 8)
4922           {
4923             image->page.x=(ssize_t) mng_get_long(p);
4924             image->page.y=(ssize_t) mng_get_long(&p[4]);
4925
4926             if ((int) p[8] != 0)
4927               {
4928                 image->page.x/=10000;
4929                 image->page.y/=10000;
4930               }
4931           }
4932
4933         if (length != 0)
4934           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4935
4936         continue;
4937       }
4938
4939     if (memcmp(type,mng_pHYs,4) == 0)
4940       {
4941         if (length > 8)
4942           {
4943             image->resolution.x=(double) mng_get_long(p);
4944             image->resolution.y=(double) mng_get_long(&p[4]);
4945             if ((int) p[8] == PNG_RESOLUTION_METER)
4946               {
4947                 image->units=PixelsPerCentimeterResolution;
4948                 image->resolution.x=image->resolution.x/100.0f;
4949                 image->resolution.y=image->resolution.y/100.0f;
4950               }
4951           }
4952
4953         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4954         continue;
4955       }
4956
4957 #if 0
4958     if (memcmp(type,mng_iCCP,4) == 0)
4959       {
4960         /* To do: */
4961         if (length != 0)
4962           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4963
4964         continue;
4965       }
4966 #endif
4967
4968     if (length != 0)
4969       chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4970
4971     if (memcmp(type,mng_IEND,4))
4972       continue;
4973
4974     break;
4975   }
4976
4977
4978   /* IEND found */
4979
4980   /*
4981     Finish up reading image data:
4982
4983        o read main image from color_blob.
4984
4985        o close color_blob.
4986
4987        o if (color_type has alpha)
4988             if alpha_encoding is PNG
4989                read secondary image from alpha_blob via ReadPNG
4990             if alpha_encoding is JPEG
4991                read secondary image from alpha_blob via ReadJPEG
4992
4993        o close alpha_blob.
4994
4995        o copy intensity of secondary image into
4996          alpha samples of main image.
4997
4998        o destroy the secondary image.
4999   */
5000
5001   if (color_image_info == (ImageInfo *) NULL)
5002     {
5003       assert(color_image == (Image *) NULL);
5004       assert(alpha_image == (Image *) NULL);
5005       return(DestroyImageList(image));
5006     }
5007
5008   if (color_image == (Image *) NULL)
5009     {
5010       assert(alpha_image == (Image *) NULL);
5011       return(DestroyImageList(image));
5012     }
5013
5014   (void) SeekBlob(color_image,0,SEEK_SET);
5015
5016   if (logging != MagickFalse)
5017     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5018       "    Reading jng_image from color_blob.");
5019
5020   assert(color_image_info != (ImageInfo *) NULL);
5021   (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,"%s",
5022     color_image->filename);
5023
5024   color_image_info->ping=MagickFalse;   /* To do: avoid this */
5025   jng_image=ReadImage(color_image_info,exception);
5026
5027   (void) RelinquishUniqueFileResource(color_image->filename);
5028   color_image=DestroyImage(color_image);
5029   color_image_info=DestroyImageInfo(color_image_info);
5030
5031   if (jng_image == (Image *) NULL)
5032     return(DestroyImageList(image));
5033
5034   if (logging != MagickFalse)
5035     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5036       "    Copying jng_image pixels to main image.");
5037
5038   image->rows=jng_height;
5039   image->columns=jng_width;
5040
5041   status=SetImageExtent(image,image->columns,image->rows,exception);
5042   if (status == MagickFalse)
5043     return(DestroyImageList(image));
5044
5045   for (y=0; y < (ssize_t) image->rows; y++)
5046   {
5047     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5048     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5049     for (x=(ssize_t) image->columns; x != 0; x--)
5050     {
5051       SetPixelRed(image,GetPixelRed(jng_image,s),q);
5052       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
5053       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
5054       q+=GetPixelChannels(image);
5055       s+=GetPixelChannels(jng_image);
5056     }
5057
5058     if (SyncAuthenticPixels(image,exception) == MagickFalse)
5059       break;
5060   }
5061
5062   jng_image=DestroyImage(jng_image);
5063
5064   if (image_info->ping == MagickFalse)
5065     {
5066      if (jng_color_type >= 12)
5067        {
5068          if (jng_alpha_compression_method == 0)
5069            {
5070              png_byte
5071                data[5];
5072              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
5073              PNGType(data,mng_IEND);
5074              LogPNGChunk(logging,mng_IEND,0L);
5075              (void) WriteBlob(alpha_image,4,data);
5076              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
5077            }
5078
5079          (void) CloseBlob(alpha_image);
5080
5081          if (logging != MagickFalse)
5082            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5083              "    Reading alpha from alpha_blob.");
5084
5085          (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
5086            "%s",alpha_image->filename);
5087
5088          jng_image=ReadImage(alpha_image_info,exception);
5089
5090          if (jng_image != (Image *) NULL)
5091            for (y=0; y < (ssize_t) image->rows; y++)
5092            {
5093              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
5094                exception);
5095              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5096
5097              if (image->alpha_trait != UndefinedPixelTrait)
5098                for (x=(ssize_t) image->columns; x != 0; x--)
5099                {
5100                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5101                   q+=GetPixelChannels(image);
5102                   s+=GetPixelChannels(jng_image);
5103                }
5104
5105              else
5106                for (x=(ssize_t) image->columns; x != 0; x--)
5107                {
5108                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5109                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
5110                     image->alpha_trait=BlendPixelTrait;
5111                   q+=GetPixelChannels(image);
5112                   s+=GetPixelChannels(jng_image);
5113                }
5114
5115              if (SyncAuthenticPixels(image,exception) == MagickFalse)
5116                break;
5117            }
5118          (void) RelinquishUniqueFileResource(alpha_image->filename);
5119          alpha_image=DestroyImage(alpha_image);
5120          alpha_image_info=DestroyImageInfo(alpha_image_info);
5121          if (jng_image != (Image *) NULL)
5122            jng_image=DestroyImage(jng_image);
5123        }
5124     }
5125
5126   /* Read the JNG image.  */
5127
5128   if (mng_info->mng_type == 0)
5129     {
5130       mng_info->mng_width=jng_width;
5131       mng_info->mng_height=jng_height;
5132     }
5133
5134   if (image->page.width == 0 && image->page.height == 0)
5135     {
5136       image->page.width=jng_width;
5137       image->page.height=jng_height;
5138     }
5139
5140   if (image->page.x == 0 && image->page.y == 0)
5141     {
5142       image->page.x=mng_info->x_off[mng_info->object_id];
5143       image->page.y=mng_info->y_off[mng_info->object_id];
5144     }
5145
5146   else
5147     {
5148       image->page.y=mng_info->y_off[mng_info->object_id];
5149     }
5150
5151   mng_info->image_found++;
5152   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
5153     2*GetBlobSize(image));
5154
5155   if (status == MagickFalse)
5156     return(DestroyImageList(image));
5157
5158   if (logging != MagickFalse)
5159     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5160       "  exit ReadOneJNGImage()");
5161
5162   return(image);
5163 }
5164
5165 /*
5166 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5167 %                                                                             %
5168 %                                                                             %
5169 %                                                                             %
5170 %   R e a d J N G I m a g e                                                   %
5171 %                                                                             %
5172 %                                                                             %
5173 %                                                                             %
5174 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5175 %
5176 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
5177 %  (including the 8-byte signature)  and returns it.  It allocates the memory
5178 %  necessary for the new Image structure and returns a pointer to the new
5179 %  image.
5180 %
5181 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
5182 %
5183 %  The format of the ReadJNGImage method is:
5184 %
5185 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
5186 %         *exception)
5187 %
5188 %  A description of each parameter follows:
5189 %
5190 %    o image_info: the image info.
5191 %
5192 %    o exception: return any errors or warnings in this structure.
5193 %
5194 */
5195
5196 static Image *ReadJNGImage(const ImageInfo *image_info,
5197                 ExceptionInfo *exception)
5198 {
5199   Image
5200     *image;
5201
5202   MagickBooleanType
5203     logging,
5204     status;
5205
5206   MngInfo
5207     *mng_info;
5208
5209   char
5210     magic_number[MagickPathExtent];
5211
5212   size_t
5213     count;
5214
5215   /*
5216     Open image file.
5217   */
5218   assert(image_info != (const ImageInfo *) NULL);
5219   assert(image_info->signature == MagickCoreSignature);
5220   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5221      image_info->filename);
5222   assert(exception != (ExceptionInfo *) NULL);
5223   assert(exception->signature == MagickCoreSignature);
5224   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
5225   image=AcquireImage(image_info,exception);
5226   mng_info=(MngInfo *) NULL;
5227   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5228
5229   if (status == MagickFalse)
5230     return((Image *) NULL);
5231
5232   if (LocaleCompare(image_info->magick,"JNG") != 0)
5233     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5234
5235   /* Verify JNG signature.  */
5236
5237   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5238
5239   if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
5240     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5241
5242   /* Allocate a MngInfo structure.  */
5243
5244   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
5245
5246   if (mng_info == (MngInfo *) NULL)
5247     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5248
5249   /* Initialize members of the MngInfo structure.  */
5250
5251   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5252
5253   mng_info->image=image;
5254   image=ReadOneJNGImage(mng_info,image_info,exception);
5255   mng_info=MngInfoFreeStruct(mng_info);
5256
5257   if (image == (Image *) NULL)
5258     {
5259       if (logging != MagickFalse)
5260         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5261           "exit ReadJNGImage() with error");
5262
5263       return((Image *) NULL);
5264     }
5265   (void) CloseBlob(image);
5266
5267   if (image->columns == 0 || image->rows == 0)
5268     {
5269       if (logging != MagickFalse)
5270         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5271           "exit ReadJNGImage() with error");
5272
5273       ThrowReaderException(CorruptImageError,"CorruptImage");
5274     }
5275
5276   if (logging != MagickFalse)
5277     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5278
5279   return(image);
5280 }
5281 #endif
5282
5283 static Image *ReadOneMNGImage(MngInfo* mng_info, const ImageInfo *image_info,
5284      ExceptionInfo *exception)
5285 {
5286   char
5287     page_geometry[MagickPathExtent];
5288
5289   Image
5290     *image;
5291
5292   MagickBooleanType
5293     logging;
5294
5295   volatile int
5296     first_mng_object,
5297     object_id,
5298     term_chunk_found,
5299     skip_to_iend;
5300
5301   volatile ssize_t
5302     image_count=0;
5303
5304   MagickBooleanType
5305     status;
5306
5307   MagickOffsetType
5308     offset;
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   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
5381     "  Enter ReadOneMNGImage()");
5382
5383   image=mng_info->image;
5384
5385   if (LocaleCompare(image_info->magick,"MNG") == 0)
5386     {
5387       char
5388         magic_number[MagickPathExtent];
5389
5390       /* Verify MNG signature.  */
5391       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5392       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5393         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5394
5395       /* Initialize some nonzero members of the MngInfo structure.  */
5396       for (i=0; i < MNG_MAX_OBJECTS; i++)
5397       {
5398         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5399         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5400       }
5401       mng_info->exists[0]=MagickTrue;
5402     }
5403
5404   skipping_loop=(-1);
5405   first_mng_object=MagickTrue;
5406   mng_type=0;
5407 #if defined(MNG_INSERT_LAYERS)
5408   insert_layers=MagickFalse; /* should be False during convert or mogrify */
5409 #endif
5410   default_frame_delay=0;
5411   default_frame_timeout=0;
5412   frame_delay=0;
5413   final_delay=1;
5414   mng_info->ticks_per_second=1UL*image->ticks_per_second;
5415   object_id=0;
5416   skip_to_iend=MagickFalse;
5417   term_chunk_found=MagickFalse;
5418   mng_info->framing_mode=1;
5419 #if defined(MNG_INSERT_LAYERS)
5420   mandatory_back=MagickFalse;
5421 #endif
5422 #if defined(MNG_INSERT_LAYERS)
5423   mng_background_color=image->background_color;
5424 #endif
5425   default_fb=mng_info->frame;
5426   previous_fb=mng_info->frame;
5427   do
5428   {
5429     char
5430       type[MagickPathExtent];
5431
5432     if (LocaleCompare(image_info->magick,"MNG") == 0)
5433       {
5434         unsigned char
5435           *chunk;
5436
5437         /*
5438           Read a new chunk.
5439         */
5440         type[0]='\0';
5441         (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5442         length=ReadBlobMSBLong(image);
5443         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5444
5445         if (logging != MagickFalse)
5446           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5447            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
5448            type[0],type[1],type[2],type[3],(double) length);
5449
5450         if (length > PNG_UINT_31_MAX)
5451           {
5452             status=MagickFalse;
5453             break;
5454           }
5455
5456         if (count == 0)
5457           ThrowReaderException(CorruptImageError,"CorruptImage");
5458
5459         p=NULL;
5460         chunk=(unsigned char *) NULL;
5461
5462         if (length != 0)
5463           {
5464             chunk=(unsigned char *) AcquireQuantumMemory(length,
5465              sizeof(*chunk));
5466
5467             if (chunk == (unsigned char *) NULL)
5468               ThrowReaderException(ResourceLimitError,
5469                 "MemoryAllocationFailed");
5470
5471             for (i=0; i < (ssize_t) length; i++)
5472               chunk[i]=(unsigned char) ReadBlobByte(image);
5473
5474             p=chunk;
5475           }
5476
5477         (void) ReadBlobMSBLong(image);  /* read crc word */
5478
5479 #if !defined(JNG_SUPPORTED)
5480         if (memcmp(type,mng_JHDR,4) == 0)
5481           {
5482             skip_to_iend=MagickTrue;
5483
5484             if (mng_info->jhdr_warning == 0)
5485               (void) ThrowMagickException(exception,GetMagickModule(),
5486                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5487
5488             mng_info->jhdr_warning++;
5489           }
5490 #endif
5491         if (memcmp(type,mng_DHDR,4) == 0)
5492           {
5493             skip_to_iend=MagickTrue;
5494
5495             if (mng_info->dhdr_warning == 0)
5496               (void) ThrowMagickException(exception,GetMagickModule(),
5497                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5498
5499             mng_info->dhdr_warning++;
5500           }
5501         if (memcmp(type,mng_MEND,4) == 0)
5502           break;
5503
5504         if (skip_to_iend)
5505           {
5506             if (memcmp(type,mng_IEND,4) == 0)
5507               skip_to_iend=MagickFalse;
5508
5509             if (length != 0)
5510               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5511
5512             if (logging != MagickFalse)
5513               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5514                 "  Skip to IEND.");
5515
5516             continue;
5517           }
5518
5519         if (memcmp(type,mng_MHDR,4) == 0)
5520           {
5521             if (length != 28)
5522               {
5523                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5524                 ThrowReaderException(CorruptImageError,"CorruptImage");
5525               }
5526
5527             mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5528                 (p[2] << 8) | p[3]);
5529
5530             mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5531                 (p[6] << 8) | p[7]);
5532
5533             if (logging != MagickFalse)
5534               {
5535                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5536                   "  MNG width: %.20g",(double) mng_info->mng_width);
5537                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5538                   "  MNG height: %.20g",(double) mng_info->mng_height);
5539               }
5540
5541             p+=8;
5542             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5543
5544             if (mng_info->ticks_per_second == 0)
5545               default_frame_delay=0;
5546
5547             else
5548               default_frame_delay=1UL*image->ticks_per_second/
5549                 mng_info->ticks_per_second;
5550
5551             frame_delay=default_frame_delay;
5552             simplicity=0;
5553
5554             p+=16;
5555             simplicity=(size_t) mng_get_long(p);
5556
5557             mng_type=1;    /* Full MNG */
5558
5559             if ((simplicity != 0) && ((simplicity | 11) == 11))
5560               mng_type=2; /* LC */
5561
5562             if ((simplicity != 0) && ((simplicity | 9) == 9))
5563               mng_type=3; /* VLC */
5564
5565 #if defined(MNG_INSERT_LAYERS)
5566             if (mng_type != 3)
5567               insert_layers=MagickTrue;
5568 #endif
5569             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5570               {
5571                 /* Allocate next image structure.  */
5572                 AcquireNextImage(image_info,image,exception);
5573
5574                 if (GetNextImageInList(image) == (Image *) NULL)
5575                   return((Image *) NULL);
5576
5577                 image=SyncNextImageInList(image);
5578                 mng_info->image=image;
5579               }
5580
5581             if ((mng_info->mng_width > 65535L) ||
5582                 (mng_info->mng_height > 65535L))
5583               {
5584                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5585                 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5586               }
5587
5588             (void) FormatLocaleString(page_geometry,MagickPathExtent,
5589               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5590               mng_info->mng_height);
5591
5592             mng_info->frame.left=0;
5593             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5594             mng_info->frame.top=0;
5595             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5596             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5597
5598             for (i=0; i < MNG_MAX_OBJECTS; i++)
5599               mng_info->object_clip[i]=mng_info->frame;
5600
5601             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5602             continue;
5603           }
5604
5605         if (memcmp(type,mng_TERM,4) == 0)
5606           {
5607             int
5608               repeat=0;
5609
5610             if (length != 0)
5611               repeat=p[0];
5612
5613             if (repeat == 3)
5614               {
5615                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5616                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5617
5618                 if (mng_iterations == PNG_UINT_31_MAX)
5619                   mng_iterations=0;
5620
5621                 image->iterations=mng_iterations;
5622                 term_chunk_found=MagickTrue;
5623               }
5624
5625             if (logging != MagickFalse)
5626               {
5627                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5628                   "    repeat=%d,  final_delay=%.20g,  iterations=%.20g",
5629                   repeat,(double) final_delay, (double) image->iterations);
5630               }
5631
5632             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5633             continue;
5634           }
5635         if (memcmp(type,mng_DEFI,4) == 0)
5636           {
5637             if (mng_type == 3)
5638               (void) ThrowMagickException(exception,GetMagickModule(),
5639                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5640                 image->filename);
5641
5642             if (length < 2)
5643               {
5644                 if (chunk)
5645                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5646                 ThrowReaderException(CorruptImageError,"CorruptImage");
5647               }
5648
5649             object_id=(p[0] << 8) | p[1];
5650
5651             if (mng_type == 2 && object_id != 0)
5652               (void) ThrowMagickException(exception,GetMagickModule(),
5653                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5654                 image->filename);
5655
5656             if (object_id > MNG_MAX_OBJECTS)
5657               {
5658                 /*
5659                   Instead of using a warning we should allocate a larger
5660                   MngInfo structure and continue.
5661                 */
5662                 (void) ThrowMagickException(exception,GetMagickModule(),
5663                   CoderError,"object id too large","`%s'",image->filename);
5664                 object_id=MNG_MAX_OBJECTS;
5665               }
5666
5667             if (mng_info->exists[object_id])
5668               if (mng_info->frozen[object_id])
5669                 {
5670                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5671                   (void) ThrowMagickException(exception,
5672                     GetMagickModule(),CoderError,
5673                     "DEFI cannot redefine a frozen MNG object","`%s'",
5674                     image->filename);
5675                   continue;
5676                 }
5677
5678             mng_info->exists[object_id]=MagickTrue;
5679
5680             if (length > 2)
5681               mng_info->invisible[object_id]=p[2];
5682
5683             /*
5684               Extract object offset info.
5685             */
5686             if (length > 11)
5687               {
5688                 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5689                     (p[5] << 16) | (p[6] << 8) | p[7]);
5690
5691                 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5692                     (p[9] << 16) | (p[10] << 8) | p[11]);
5693
5694                 if (logging != MagickFalse)
5695                   {
5696                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5697                       "  x_off[%d]: %.20g,  y_off[%d]: %.20g",
5698                       object_id,(double) mng_info->x_off[object_id],
5699                       object_id,(double) mng_info->y_off[object_id]);
5700                   }
5701               }
5702
5703             /*
5704               Extract object clipping info.
5705             */
5706             if (length > 27)
5707               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5708                 &p[12]);
5709
5710             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5711             continue;
5712           }
5713         if (memcmp(type,mng_bKGD,4) == 0)
5714           {
5715             mng_info->have_global_bkgd=MagickFalse;
5716
5717             if (length > 5)
5718               {
5719                 mng_info->mng_global_bkgd.red=
5720                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5721
5722                 mng_info->mng_global_bkgd.green=
5723                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5724
5725                 mng_info->mng_global_bkgd.blue=
5726                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5727
5728                 mng_info->have_global_bkgd=MagickTrue;
5729               }
5730
5731             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5732             continue;
5733           }
5734         if (memcmp(type,mng_BACK,4) == 0)
5735           {
5736 #if defined(MNG_INSERT_LAYERS)
5737             if (length > 6)
5738               mandatory_back=p[6];
5739
5740             else
5741               mandatory_back=0;
5742
5743             if (mandatory_back && length > 5)
5744               {
5745                 mng_background_color.red=
5746                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5747
5748                 mng_background_color.green=
5749                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5750
5751                 mng_background_color.blue=
5752                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5753
5754                 mng_background_color.alpha=OpaqueAlpha;
5755               }
5756
5757 #ifdef MNG_OBJECT_BUFFERS
5758             if (length > 8)
5759               mng_background_object=(p[7] << 8) | p[8];
5760 #endif
5761 #endif
5762             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5763             continue;
5764           }
5765
5766         if (memcmp(type,mng_PLTE,4) == 0)
5767           {
5768             /* Read global PLTE.  */
5769
5770             if (length && (length < 769))
5771               {
5772                 if (mng_info->global_plte == (png_colorp) NULL)
5773                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5774                     sizeof(*mng_info->global_plte));
5775
5776                 for (i=0; i < (ssize_t) (length/3); i++)
5777                 {
5778                   mng_info->global_plte[i].red=p[3*i];
5779                   mng_info->global_plte[i].green=p[3*i+1];
5780                   mng_info->global_plte[i].blue=p[3*i+2];
5781                 }
5782
5783                 mng_info->global_plte_length=(unsigned int) (length/3);
5784               }
5785 #ifdef MNG_LOOSE
5786             for ( ; i < 256; i++)
5787             {
5788               mng_info->global_plte[i].red=i;
5789               mng_info->global_plte[i].green=i;
5790               mng_info->global_plte[i].blue=i;
5791             }
5792
5793             if (length != 0)
5794               mng_info->global_plte_length=256;
5795 #endif
5796             else
5797               mng_info->global_plte_length=0;
5798
5799             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5800             continue;
5801           }
5802
5803         if (memcmp(type,mng_tRNS,4) == 0)
5804           {
5805             /* read global tRNS */
5806
5807             if (length > 0 && length < 257)
5808               for (i=0; i < (ssize_t) length; i++)
5809                 mng_info->global_trns[i]=p[i];
5810
5811 #ifdef MNG_LOOSE
5812             for ( ; i < 256; i++)
5813               mng_info->global_trns[i]=255;
5814 #endif
5815             mng_info->global_trns_length=(unsigned int) length;
5816             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5817             continue;
5818           }
5819         if (memcmp(type,mng_gAMA,4) == 0)
5820           {
5821             if (length == 4)
5822               {
5823                 ssize_t
5824                   igamma;
5825
5826                 igamma=mng_get_long(p);
5827                 mng_info->global_gamma=((float) igamma)*0.00001;
5828                 mng_info->have_global_gama=MagickTrue;
5829               }
5830
5831             else
5832               mng_info->have_global_gama=MagickFalse;
5833
5834             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5835             continue;
5836           }
5837
5838         if (memcmp(type,mng_cHRM,4) == 0)
5839           {
5840             /* Read global cHRM */
5841
5842             if (length == 32)
5843               {
5844                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5845                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5846                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5847                 mng_info->global_chrm.red_primary.y=0.00001*
5848                   mng_get_long(&p[12]);
5849                 mng_info->global_chrm.green_primary.x=0.00001*
5850                   mng_get_long(&p[16]);
5851                 mng_info->global_chrm.green_primary.y=0.00001*
5852                   mng_get_long(&p[20]);
5853                 mng_info->global_chrm.blue_primary.x=0.00001*
5854                   mng_get_long(&p[24]);
5855                 mng_info->global_chrm.blue_primary.y=0.00001*
5856                   mng_get_long(&p[28]);
5857                 mng_info->have_global_chrm=MagickTrue;
5858               }
5859             else
5860               mng_info->have_global_chrm=MagickFalse;
5861
5862             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5863             continue;
5864           }
5865
5866         if (memcmp(type,mng_sRGB,4) == 0)
5867           {
5868             /*
5869               Read global sRGB.
5870             */
5871             if (length != 0)
5872               {
5873                 mng_info->global_srgb_intent=
5874                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5875                 mng_info->have_global_srgb=MagickTrue;
5876               }
5877             else
5878               mng_info->have_global_srgb=MagickFalse;
5879
5880             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5881             continue;
5882           }
5883
5884         if (memcmp(type,mng_iCCP,4) == 0)
5885           {
5886             /* To do: */
5887
5888             /*
5889               Read global iCCP.
5890             */
5891             if (length != 0)
5892               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5893
5894             continue;
5895           }
5896
5897         if (memcmp(type,mng_FRAM,4) == 0)
5898           {
5899             if (mng_type == 3)
5900               (void) ThrowMagickException(exception,GetMagickModule(),
5901                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5902                 image->filename);
5903
5904             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5905               image->delay=frame_delay;
5906
5907             frame_delay=default_frame_delay;
5908             frame_timeout=default_frame_timeout;
5909             fb=default_fb;
5910
5911             if (length != 0)
5912               if (p[0])
5913                 mng_info->framing_mode=p[0];
5914
5915             if (logging != MagickFalse)
5916               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5917                 "    Framing_mode=%d",mng_info->framing_mode);
5918
5919             if (length > 6)
5920               {
5921                 /* Note the delay and frame clipping boundaries.  */
5922
5923                 p++; /* framing mode */
5924
5925                 while (*p && ((p-chunk) < (ssize_t) length))
5926                   p++;  /* frame name */
5927
5928                 p++;  /* frame name terminator */
5929
5930                 if ((p-chunk) < (ssize_t) (length-4))
5931                   {
5932                     int
5933                       change_delay,
5934                       change_timeout,
5935                       change_clipping;
5936
5937                     change_delay=(*p++);
5938                     change_timeout=(*p++);
5939                     change_clipping=(*p++);
5940                     p++; /* change_sync */
5941
5942                     if (change_delay)
5943                       {
5944                         frame_delay=1UL*image->ticks_per_second*
5945                           mng_get_long(p);
5946
5947                         if (mng_info->ticks_per_second != 0)
5948                           frame_delay/=mng_info->ticks_per_second;
5949
5950                         else
5951                           frame_delay=PNG_UINT_31_MAX;
5952
5953                         if (change_delay == 2)
5954                           default_frame_delay=frame_delay;
5955
5956                         p+=4;
5957
5958                         if (logging != MagickFalse)
5959                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5960                             "    Framing_delay=%.20g",(double) frame_delay);
5961                       }
5962
5963                     if (change_timeout)
5964                       {
5965                         frame_timeout=1UL*image->ticks_per_second*
5966                           mng_get_long(p);
5967
5968                         if (mng_info->ticks_per_second != 0)
5969                           frame_timeout/=mng_info->ticks_per_second;
5970
5971                         else
5972                           frame_timeout=PNG_UINT_31_MAX;
5973
5974                         if (change_timeout == 2)
5975                           default_frame_timeout=frame_timeout;
5976
5977                         p+=4;
5978
5979                         if (logging != MagickFalse)
5980                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5981                             "    Framing_timeout=%.20g",(double) frame_timeout);
5982                       }
5983
5984                     if (change_clipping)
5985                       {
5986                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5987                         p+=17;
5988                         previous_fb=fb;
5989
5990                         if (logging != MagickFalse)
5991                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5992                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5993                             (double) fb.left,(double) fb.right,(double) fb.top,
5994                             (double) fb.bottom);
5995
5996                         if (change_clipping == 2)
5997                           default_fb=fb;
5998                       }
5999                   }
6000               }
6001             mng_info->clip=fb;
6002             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
6003
6004             subframe_width=(size_t) (mng_info->clip.right
6005                -mng_info->clip.left);
6006
6007             subframe_height=(size_t) (mng_info->clip.bottom
6008                -mng_info->clip.top);
6009             /*
6010               Insert a background layer behind the frame if framing_mode is 4.
6011             */
6012 #if defined(MNG_INSERT_LAYERS)
6013             if (logging != MagickFalse)
6014               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6015                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
6016                 subframe_width,(double) subframe_height);
6017
6018             if (insert_layers && (mng_info->framing_mode == 4) &&
6019                 (subframe_width) && (subframe_height))
6020               {
6021                 /* Allocate next image structure.  */
6022                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6023                   {
6024                     AcquireNextImage(image_info,image,exception);
6025
6026                     if (GetNextImageInList(image) == (Image *) NULL)
6027                       return(DestroyImageList(image));
6028
6029                     image=SyncNextImageInList(image);
6030                   }
6031
6032                 mng_info->image=image;
6033
6034                 if (term_chunk_found)
6035                   {
6036                     image->start_loop=MagickTrue;
6037                     image->iterations=mng_iterations;
6038                     term_chunk_found=MagickFalse;
6039                   }
6040
6041                 else
6042                     image->start_loop=MagickFalse;
6043
6044                 image->columns=subframe_width;
6045                 image->rows=subframe_height;
6046                 image->page.width=subframe_width;
6047                 image->page.height=subframe_height;
6048                 image->page.x=mng_info->clip.left;
6049                 image->page.y=mng_info->clip.top;
6050                 image->background_color=mng_background_color;
6051                 image->alpha_trait=UndefinedPixelTrait;
6052                 image->delay=0;
6053                 (void) SetImageBackgroundColor(image,exception);
6054
6055                 if (logging != MagickFalse)
6056                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6057                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6058                     (double) mng_info->clip.left,
6059                     (double) mng_info->clip.right,
6060                     (double) mng_info->clip.top,
6061                     (double) mng_info->clip.bottom);
6062               }
6063 #endif
6064             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6065             continue;
6066           }
6067
6068         if (memcmp(type,mng_CLIP,4) == 0)
6069           {
6070             unsigned int
6071               first_object,
6072               last_object;
6073
6074             /*
6075               Read CLIP.
6076             */
6077             if (length > 3)
6078               {
6079                 first_object=(p[0] << 8) | p[1];
6080                 last_object=(p[2] << 8) | p[3];
6081                 p+=4;
6082
6083                 for (i=(int) first_object; i <= (int) last_object; i++)
6084                 {
6085                   if (mng_info->exists[i] && !mng_info->frozen[i])
6086                     {
6087                       MngBox
6088                         box;
6089
6090                       box=mng_info->object_clip[i];
6091                       if ((p-chunk) < (ssize_t) (length-17))
6092                         mng_info->object_clip[i]=
6093                            mng_read_box(box,(char) p[0],&p[1]);
6094                     }
6095                 }
6096
6097               }
6098             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6099             continue;
6100           }
6101
6102         if (memcmp(type,mng_SAVE,4) == 0)
6103           {
6104             for (i=1; i < MNG_MAX_OBJECTS; i++)
6105               if (mng_info->exists[i])
6106                 {
6107                  mng_info->frozen[i]=MagickTrue;
6108 #ifdef MNG_OBJECT_BUFFERS
6109                  if (mng_info->ob[i] != (MngBuffer *) NULL)
6110                     mng_info->ob[i]->frozen=MagickTrue;
6111 #endif
6112                 }
6113
6114             if (length != 0)
6115               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6116
6117             continue;
6118           }
6119
6120         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
6121           {
6122             /* Read DISC or SEEK.  */
6123
6124             if ((length == 0) || !memcmp(type,mng_SEEK,4))
6125               {
6126                 for (i=1; i < MNG_MAX_OBJECTS; i++)
6127                   MngInfoDiscardObject(mng_info,i);
6128               }
6129
6130             else
6131               {
6132                 register ssize_t
6133                   j;
6134
6135                 for (j=1; j < (ssize_t) length; j+=2)
6136                 {
6137                   i=p[j-1] << 8 | p[j];
6138                   MngInfoDiscardObject(mng_info,i);
6139                 }
6140               }
6141
6142             if (length != 0)
6143               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6144
6145             continue;
6146           }
6147
6148         if (memcmp(type,mng_MOVE,4) == 0)
6149           {
6150             size_t
6151               first_object,
6152               last_object;
6153
6154             /* read MOVE */
6155
6156             if (length > 3)
6157             {
6158               first_object=(p[0] << 8) | p[1];
6159               last_object=(p[2] << 8) | p[3];
6160               p+=4;
6161
6162               for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
6163               {
6164                 if (mng_info->exists[i] && !mng_info->frozen[i] &&
6165                     (p-chunk) < (ssize_t) (length-8))
6166                   {
6167                     MngPair
6168                       new_pair;
6169
6170                     MngPair
6171                       old_pair;
6172
6173                     old_pair.a=mng_info->x_off[i];
6174                     old_pair.b=mng_info->y_off[i];
6175                     new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
6176                     mng_info->x_off[i]=new_pair.a;
6177                     mng_info->y_off[i]=new_pair.b;
6178                   }
6179               }
6180             }
6181
6182             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6183             continue;
6184           }
6185
6186         if (memcmp(type,mng_LOOP,4) == 0)
6187           {
6188             ssize_t loop_iters=1;
6189             if (length > 4)
6190               {
6191                 loop_level=chunk[0];
6192                 mng_info->loop_active[loop_level]=1;  /* mark loop active */
6193
6194                 /* Record starting point.  */
6195                 loop_iters=mng_get_long(&chunk[1]);
6196
6197                 if (logging != MagickFalse)
6198                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6199                     "  LOOP level %.20g has %.20g iterations ",
6200                     (double) loop_level, (double) loop_iters);
6201
6202                 if (loop_iters == 0)
6203                   skipping_loop=loop_level;
6204
6205                 else
6206                   {
6207                     mng_info->loop_jump[loop_level]=TellBlob(image);
6208                     mng_info->loop_count[loop_level]=loop_iters;
6209                   }
6210
6211                 mng_info->loop_iteration[loop_level]=0;
6212               }
6213             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6214             continue;
6215           }
6216
6217         if (memcmp(type,mng_ENDL,4) == 0)
6218           {
6219             if (length > 0)
6220               {
6221                 loop_level=chunk[0];
6222
6223                 if (skipping_loop > 0)
6224                   {
6225                     if (skipping_loop == loop_level)
6226                       {
6227                         /*
6228                           Found end of zero-iteration loop.
6229                         */
6230                         skipping_loop=(-1);
6231                         mng_info->loop_active[loop_level]=0;
6232                       }
6233                   }
6234
6235                 else
6236                   {
6237                     if (mng_info->loop_active[loop_level] == 1)
6238                       {
6239                         mng_info->loop_count[loop_level]--;
6240                         mng_info->loop_iteration[loop_level]++;
6241
6242                         if (logging != MagickFalse)
6243                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6244                           "  ENDL: LOOP level %.20g has %.20g remaining iters",
6245                             (double) loop_level,(double)
6246                             mng_info->loop_count[loop_level]);
6247
6248                         if (mng_info->loop_count[loop_level] != 0)
6249                           {
6250                             offset=
6251                               SeekBlob(image,mng_info->loop_jump[loop_level],
6252                               SEEK_SET);
6253
6254                             if (offset < 0)
6255                               {
6256                                 chunk=(unsigned char *) RelinquishMagickMemory(
6257                                   chunk);
6258                                 ThrowReaderException(CorruptImageError,
6259                                   "ImproperImageHeader");
6260                               }
6261                           }
6262
6263                         else
6264                           {
6265                             short
6266                               last_level;
6267
6268                             /*
6269                               Finished loop.
6270                             */
6271                             mng_info->loop_active[loop_level]=0;
6272                             last_level=(-1);
6273                             for (i=0; i < loop_level; i++)
6274                               if (mng_info->loop_active[i] == 1)
6275                                 last_level=(short) i;
6276                             loop_level=last_level;
6277                           }
6278                       }
6279                   }
6280               }
6281
6282             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6283             continue;
6284           }
6285
6286         if (memcmp(type,mng_CLON,4) == 0)
6287           {
6288             if (mng_info->clon_warning == 0)
6289               (void) ThrowMagickException(exception,GetMagickModule(),
6290                 CoderError,"CLON is not implemented yet","`%s'",
6291                 image->filename);
6292
6293             mng_info->clon_warning++;
6294           }
6295
6296         if (memcmp(type,mng_MAGN,4) == 0)
6297           {
6298             png_uint_16
6299               magn_first,
6300               magn_last,
6301               magn_mb,
6302               magn_ml,
6303               magn_mr,
6304               magn_mt,
6305               magn_mx,
6306               magn_my,
6307               magn_methx,
6308               magn_methy;
6309
6310             if (length > 1)
6311               magn_first=(p[0] << 8) | p[1];
6312
6313             else
6314               magn_first=0;
6315
6316             if (length > 3)
6317               magn_last=(p[2] << 8) | p[3];
6318
6319             else
6320               magn_last=magn_first;
6321 #ifndef MNG_OBJECT_BUFFERS
6322             if (magn_first || magn_last)
6323               if (mng_info->magn_warning == 0)
6324                 {
6325                   (void) ThrowMagickException(exception,
6326                      GetMagickModule(),CoderError,
6327                      "MAGN is not implemented yet for nonzero objects",
6328                      "`%s'",image->filename);
6329
6330                    mng_info->magn_warning++;
6331                 }
6332 #endif
6333             if (length > 4)
6334               magn_methx=p[4];
6335
6336             else
6337               magn_methx=0;
6338
6339             if (length > 6)
6340               magn_mx=(p[5] << 8) | p[6];
6341
6342             else
6343               magn_mx=1;
6344
6345             if (magn_mx == 0)
6346               magn_mx=1;
6347
6348             if (length > 8)
6349               magn_my=(p[7] << 8) | p[8];
6350
6351             else
6352               magn_my=magn_mx;
6353
6354             if (magn_my == 0)
6355               magn_my=1;
6356
6357             if (length > 10)
6358               magn_ml=(p[9] << 8) | p[10];
6359
6360             else
6361               magn_ml=magn_mx;
6362
6363             if (magn_ml == 0)
6364               magn_ml=1;
6365
6366             if (length > 12)
6367               magn_mr=(p[11] << 8) | p[12];
6368
6369             else
6370               magn_mr=magn_mx;
6371
6372             if (magn_mr == 0)
6373               magn_mr=1;
6374
6375             if (length > 14)
6376               magn_mt=(p[13] << 8) | p[14];
6377
6378             else
6379               magn_mt=magn_my;
6380
6381             if (magn_mt == 0)
6382               magn_mt=1;
6383
6384             if (length > 16)
6385               magn_mb=(p[15] << 8) | p[16];
6386
6387             else
6388               magn_mb=magn_my;
6389
6390             if (magn_mb == 0)
6391               magn_mb=1;
6392
6393             if (length > 17)
6394               magn_methy=p[17];
6395
6396             else
6397               magn_methy=magn_methx;
6398
6399
6400             if (magn_methx > 5 || magn_methy > 5)
6401               if (mng_info->magn_warning == 0)
6402                 {
6403                   (void) ThrowMagickException(exception,
6404                      GetMagickModule(),CoderError,
6405                      "Unknown MAGN method in MNG datastream","`%s'",
6406                      image->filename);
6407
6408                    mng_info->magn_warning++;
6409                 }
6410 #ifdef MNG_OBJECT_BUFFERS
6411           /* Magnify existing objects in the range magn_first to magn_last */
6412 #endif
6413             if (magn_first == 0 || magn_last == 0)
6414               {
6415                 /* Save the magnification factors for object 0 */
6416                 mng_info->magn_mb=magn_mb;
6417                 mng_info->magn_ml=magn_ml;
6418                 mng_info->magn_mr=magn_mr;
6419                 mng_info->magn_mt=magn_mt;
6420                 mng_info->magn_mx=magn_mx;
6421                 mng_info->magn_my=magn_my;
6422                 mng_info->magn_methx=magn_methx;
6423                 mng_info->magn_methy=magn_methy;
6424               }
6425           }
6426
6427         if (memcmp(type,mng_PAST,4) == 0)
6428           {
6429             if (mng_info->past_warning == 0)
6430               (void) ThrowMagickException(exception,GetMagickModule(),
6431                 CoderError,"PAST is not implemented yet","`%s'",
6432                 image->filename);
6433
6434             mng_info->past_warning++;
6435           }
6436
6437         if (memcmp(type,mng_SHOW,4) == 0)
6438           {
6439             if (mng_info->show_warning == 0)
6440               (void) ThrowMagickException(exception,GetMagickModule(),
6441                 CoderError,"SHOW is not implemented yet","`%s'",
6442                 image->filename);
6443
6444             mng_info->show_warning++;
6445           }
6446
6447         if (memcmp(type,mng_sBIT,4) == 0)
6448           {
6449             if (length < 4)
6450               mng_info->have_global_sbit=MagickFalse;
6451
6452             else
6453               {
6454                 mng_info->global_sbit.gray=p[0];
6455                 mng_info->global_sbit.red=p[0];
6456                 mng_info->global_sbit.green=p[1];
6457                 mng_info->global_sbit.blue=p[2];
6458                 mng_info->global_sbit.alpha=p[3];
6459                 mng_info->have_global_sbit=MagickTrue;
6460              }
6461           }
6462         if (memcmp(type,mng_pHYs,4) == 0)
6463           {
6464             if (length > 8)
6465               {
6466                 mng_info->global_x_pixels_per_unit=
6467                     (size_t) mng_get_long(p);
6468                 mng_info->global_y_pixels_per_unit=
6469                     (size_t) mng_get_long(&p[4]);
6470                 mng_info->global_phys_unit_type=p[8];
6471                 mng_info->have_global_phys=MagickTrue;
6472               }
6473
6474             else
6475               mng_info->have_global_phys=MagickFalse;
6476           }
6477         if (memcmp(type,mng_pHYg,4) == 0)
6478           {
6479             if (mng_info->phyg_warning == 0)
6480               (void) ThrowMagickException(exception,GetMagickModule(),
6481                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6482
6483             mng_info->phyg_warning++;
6484           }
6485         if (memcmp(type,mng_BASI,4) == 0)
6486           {
6487             skip_to_iend=MagickTrue;
6488
6489             if (mng_info->basi_warning == 0)
6490               (void) ThrowMagickException(exception,GetMagickModule(),
6491                 CoderError,"BASI is not implemented yet","`%s'",
6492                 image->filename);
6493
6494             mng_info->basi_warning++;
6495 #ifdef MNG_BASI_SUPPORTED
6496             basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6497                (p[2] << 8) | p[3]);
6498             basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6499                (p[6] << 8) | p[7]);
6500             basi_color_type=p[8];
6501             basi_compression_method=p[9];
6502             basi_filter_type=p[10];
6503             basi_interlace_method=p[11];
6504             if (length > 11)
6505               basi_red=(p[12] << 8) & p[13];
6506
6507             else
6508               basi_red=0;
6509
6510             if (length > 13)
6511               basi_green=(p[14] << 8) & p[15];
6512
6513             else
6514               basi_green=0;
6515
6516             if (length > 15)
6517               basi_blue=(p[16] << 8) & p[17];
6518
6519             else
6520               basi_blue=0;
6521
6522             if (length > 17)
6523               basi_alpha=(p[18] << 8) & p[19];
6524
6525             else
6526               {
6527                 if (basi_sample_depth == 16)
6528                   basi_alpha=65535L;
6529                 else
6530                   basi_alpha=255;
6531               }
6532
6533             if (length > 19)
6534               basi_viewable=p[20];
6535
6536             else
6537               basi_viewable=0;
6538
6539 #endif
6540             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6541             continue;
6542           }
6543
6544         if (memcmp(type,mng_IHDR,4)
6545 #if defined(JNG_SUPPORTED)
6546             && memcmp(type,mng_JHDR,4)
6547 #endif
6548             )
6549           {
6550             /* Not an IHDR or JHDR chunk */
6551             if (length != 0)
6552               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6553
6554             continue;
6555           }
6556 /* Process IHDR */
6557         if (logging != MagickFalse)
6558           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6559             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6560
6561         mng_info->exists[object_id]=MagickTrue;
6562         mng_info->viewable[object_id]=MagickTrue;
6563
6564         if (mng_info->invisible[object_id])
6565           {
6566             if (logging != MagickFalse)
6567               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6568                 "  Skipping invisible object");
6569
6570             skip_to_iend=MagickTrue;
6571             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6572             continue;
6573           }
6574 #if defined(MNG_INSERT_LAYERS)
6575         if (length < 8)
6576           {
6577             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6578             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6579           }
6580
6581         image_width=(size_t) mng_get_long(p);
6582         image_height=(size_t) mng_get_long(&p[4]);
6583 #endif
6584         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6585
6586         /*
6587           Insert a transparent background layer behind the entire animation
6588           if it is not full screen.
6589         */
6590 #if defined(MNG_INSERT_LAYERS)
6591         if (insert_layers && mng_type && first_mng_object)
6592           {
6593             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6594                 (image_width < mng_info->mng_width) ||
6595                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6596                 (image_height < mng_info->mng_height) ||
6597                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6598               {
6599                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6600                   {
6601                     /*
6602                       Allocate next image structure.
6603                     */
6604                     AcquireNextImage(image_info,image,exception);
6605
6606                     if (GetNextImageInList(image) == (Image *) NULL)
6607                       return(DestroyImageList(image));
6608
6609                     image=SyncNextImageInList(image);
6610                   }
6611                 mng_info->image=image;
6612
6613                 if (term_chunk_found)
6614                   {
6615                     image->start_loop=MagickTrue;
6616                     image->iterations=mng_iterations;
6617                     term_chunk_found=MagickFalse;
6618                   }
6619
6620                 else
6621                     image->start_loop=MagickFalse;
6622
6623                 /* Make a background rectangle.  */
6624
6625                 image->delay=0;
6626                 image->columns=mng_info->mng_width;
6627                 image->rows=mng_info->mng_height;
6628                 image->page.width=mng_info->mng_width;
6629                 image->page.height=mng_info->mng_height;
6630                 image->page.x=0;
6631                 image->page.y=0;
6632                 image->background_color=mng_background_color;
6633                 (void) SetImageBackgroundColor(image,exception);
6634                 if (logging != MagickFalse)
6635                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6636                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6637                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6638               }
6639           }
6640         /*
6641           Insert a background layer behind the upcoming image if
6642           framing_mode is 3, and we haven't already inserted one.
6643         */
6644         if (insert_layers && (mng_info->framing_mode == 3) &&
6645                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6646                 (simplicity & 0x08)))
6647           {
6648             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6649             {
6650               /*
6651                 Allocate next image structure.
6652               */
6653               AcquireNextImage(image_info,image,exception);
6654
6655               if (GetNextImageInList(image) == (Image *) NULL)
6656                 return(DestroyImageList(image));
6657
6658               image=SyncNextImageInList(image);
6659             }
6660
6661             mng_info->image=image;
6662
6663             if (term_chunk_found)
6664               {
6665                 image->start_loop=MagickTrue;
6666                 image->iterations=mng_iterations;
6667                 term_chunk_found=MagickFalse;
6668               }
6669
6670             else
6671                 image->start_loop=MagickFalse;
6672
6673             image->delay=0;
6674             image->columns=subframe_width;
6675             image->rows=subframe_height;
6676             image->page.width=subframe_width;
6677             image->page.height=subframe_height;
6678             image->page.x=mng_info->clip.left;
6679             image->page.y=mng_info->clip.top;
6680             image->background_color=mng_background_color;
6681             image->alpha_trait=UndefinedPixelTrait;
6682             (void) SetImageBackgroundColor(image,exception);
6683
6684             if (logging != MagickFalse)
6685               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6686                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6687                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6688                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6689           }
6690 #endif /* MNG_INSERT_LAYERS */
6691         first_mng_object=MagickFalse;
6692
6693         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6694           {
6695             /*
6696               Allocate next image structure.
6697             */
6698             AcquireNextImage(image_info,image,exception);
6699
6700             if (GetNextImageInList(image) == (Image *) NULL)
6701               return(DestroyImageList(image));
6702
6703             image=SyncNextImageInList(image);
6704           }
6705         mng_info->image=image;
6706         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6707           GetBlobSize(image));
6708
6709         if (status == MagickFalse)
6710           break;
6711
6712         if (term_chunk_found)
6713           {
6714             image->start_loop=MagickTrue;
6715             term_chunk_found=MagickFalse;
6716           }
6717
6718         else
6719             image->start_loop=MagickFalse;
6720
6721         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6722           {
6723             image->delay=frame_delay;
6724             frame_delay=default_frame_delay;
6725           }
6726
6727         else
6728           image->delay=0;
6729
6730         image->page.width=mng_info->mng_width;
6731         image->page.height=mng_info->mng_height;
6732         image->page.x=mng_info->x_off[object_id];
6733         image->page.y=mng_info->y_off[object_id];
6734         image->iterations=mng_iterations;
6735
6736         /*
6737           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6738         */
6739
6740         if (logging != MagickFalse)
6741           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6742             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6743             type[2],type[3]);
6744
6745         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6746
6747         if (offset < 0)
6748           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6749       }
6750
6751     mng_info->image=image;
6752     mng_info->mng_type=mng_type;
6753     mng_info->object_id=object_id;
6754
6755     if (memcmp(type,mng_IHDR,4) == 0)
6756       image=ReadOnePNGImage(mng_info,image_info,exception);
6757
6758 #if defined(JNG_SUPPORTED)
6759     else
6760       image=ReadOneJNGImage(mng_info,image_info,exception);
6761 #endif
6762
6763     if (image == (Image *) NULL)
6764       {
6765         if (logging != MagickFalse)
6766           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6767             "exit ReadJNGImage() with error");
6768
6769         return((Image *) NULL);
6770       }
6771
6772     if (image->columns == 0 || image->rows == 0)
6773       {
6774         (void) CloseBlob(image);
6775         return(DestroyImageList(image));
6776       }
6777
6778     mng_info->image=image;
6779
6780     if (mng_type)
6781       {
6782         MngBox
6783           crop_box;
6784
6785         if (mng_info->magn_methx || mng_info->magn_methy)
6786           {
6787             png_uint_32
6788                magnified_height,
6789                magnified_width;
6790
6791             if (logging != MagickFalse)
6792               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6793                 "  Processing MNG MAGN chunk");
6794
6795             if (mng_info->magn_methx == 1)
6796               {
6797                 magnified_width=mng_info->magn_ml;
6798
6799                 if (image->columns > 1)
6800                    magnified_width += mng_info->magn_mr;
6801
6802                 if (image->columns > 2)
6803                    magnified_width += (png_uint_32)
6804                       ((image->columns-2)*(mng_info->magn_mx));
6805               }
6806
6807             else
6808               {
6809                 magnified_width=(png_uint_32) image->columns;
6810
6811                 if (image->columns > 1)
6812                    magnified_width += mng_info->magn_ml-1;
6813
6814                 if (image->columns > 2)
6815                    magnified_width += mng_info->magn_mr-1;
6816
6817                 if (image->columns > 3)
6818                    magnified_width += (png_uint_32)
6819                       ((image->columns-3)*(mng_info->magn_mx-1));
6820               }
6821
6822             if (mng_info->magn_methy == 1)
6823               {
6824                 magnified_height=mng_info->magn_mt;
6825
6826                 if (image->rows > 1)
6827                    magnified_height += mng_info->magn_mb;
6828
6829                 if (image->rows > 2)
6830                    magnified_height += (png_uint_32)
6831                       ((image->rows-2)*(mng_info->magn_my));
6832               }
6833
6834             else
6835               {
6836                 magnified_height=(png_uint_32) image->rows;
6837
6838                 if (image->rows > 1)
6839                    magnified_height += mng_info->magn_mt-1;
6840
6841                 if (image->rows > 2)
6842                    magnified_height += mng_info->magn_mb-1;
6843
6844                 if (image->rows > 3)
6845                    magnified_height += (png_uint_32)
6846                       ((image->rows-3)*(mng_info->magn_my-1));
6847               }
6848
6849             if (magnified_height > image->rows ||
6850                 magnified_width > image->columns)
6851               {
6852                 Image
6853                   *large_image;
6854
6855                 int
6856                   yy;
6857
6858                 Quantum
6859                   *next,
6860                   *prev;
6861
6862                 png_uint_16
6863                   magn_methx,
6864                   magn_methy;
6865
6866                 ssize_t
6867                   m,
6868                   y;
6869
6870                 register Quantum
6871                   *n,
6872                   *q;
6873
6874                 register ssize_t
6875                   x;
6876
6877                 /* Allocate next image structure.  */
6878
6879                 if (logging != MagickFalse)
6880                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6881                     "    Allocate magnified image");
6882
6883                 AcquireNextImage(image_info,image,exception);
6884
6885                 if (GetNextImageInList(image) == (Image *) NULL)
6886                   return(DestroyImageList(image));
6887
6888                 large_image=SyncNextImageInList(image);
6889
6890                 large_image->columns=magnified_width;
6891                 large_image->rows=magnified_height;
6892
6893                 magn_methx=mng_info->magn_methx;
6894                 magn_methy=mng_info->magn_methy;
6895
6896 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6897 #define QM unsigned short
6898                 if (magn_methx != 1 || magn_methy != 1)
6899                   {
6900                   /*
6901                      Scale pixels to unsigned shorts to prevent
6902                      overflow of intermediate values of interpolations
6903                   */
6904                      for (y=0; y < (ssize_t) image->rows; y++)
6905                      {
6906                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6907                           exception);
6908
6909                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6910                        {
6911                           SetPixelRed(image,ScaleQuantumToShort(
6912                             GetPixelRed(image,q)),q);
6913                           SetPixelGreen(image,ScaleQuantumToShort(
6914                             GetPixelGreen(image,q)),q);
6915                           SetPixelBlue(image,ScaleQuantumToShort(
6916                             GetPixelBlue(image,q)),q);
6917                           SetPixelAlpha(image,ScaleQuantumToShort(
6918                             GetPixelAlpha(image,q)),q);
6919                           q+=GetPixelChannels(image);
6920                        }
6921
6922                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6923                          break;
6924                      }
6925                   }
6926 #else
6927 #define QM Quantum
6928 #endif
6929
6930                 if (image->alpha_trait != UndefinedPixelTrait)
6931                    (void) SetImageBackgroundColor(large_image,exception);
6932
6933                 else
6934                   {
6935                     large_image->background_color.alpha=OpaqueAlpha;
6936                     (void) SetImageBackgroundColor(large_image,exception);
6937
6938                     if (magn_methx == 4)
6939                       magn_methx=2;
6940
6941                     if (magn_methx == 5)
6942                       magn_methx=3;
6943
6944                     if (magn_methy == 4)
6945                       magn_methy=2;
6946
6947                     if (magn_methy == 5)
6948                       magn_methy=3;
6949                   }
6950
6951                 /* magnify the rows into the right side of the large image */
6952
6953                 if (logging != MagickFalse)
6954                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6955                     "    Magnify the rows to %.20g",
6956                     (double) large_image->rows);
6957                 m=(ssize_t) mng_info->magn_mt;
6958                 yy=0;
6959                 length=(size_t) GetPixelChannels(image)*image->columns;
6960                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6961                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6962
6963                 if ((prev == (Quantum *) NULL) ||
6964                     (next == (Quantum *) NULL))
6965                   {
6966                      image=DestroyImageList(image);
6967                      ThrowReaderException(ResourceLimitError,
6968                        "MemoryAllocationFailed");
6969                   }
6970
6971                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6972                 (void) CopyMagickMemory(next,n,length);
6973
6974                 for (y=0; y < (ssize_t) image->rows; y++)
6975                 {
6976                   if (y == 0)
6977                     m=(ssize_t) mng_info->magn_mt;
6978
6979                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6980                     m=(ssize_t) mng_info->magn_mb;
6981
6982                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6983                     m=(ssize_t) mng_info->magn_mb;
6984
6985                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6986                     m=1;
6987
6988                   else
6989                     m=(ssize_t) mng_info->magn_my;
6990
6991                   n=prev;
6992                   prev=next;
6993                   next=n;
6994
6995                   if (y < (ssize_t) image->rows-1)
6996                     {
6997                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6998                           exception);
6999                       (void) CopyMagickMemory(next,n,length);
7000                     }
7001
7002                   for (i=0; i < m; i++, yy++)
7003                   {
7004                     register Quantum
7005                       *pixels;
7006
7007                     assert(yy < (ssize_t) large_image->rows);
7008                     pixels=prev;
7009                     n=next;
7010                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
7011                       1,exception);
7012                     q+=(large_image->columns-image->columns)*
7013                       GetPixelChannels(large_image);
7014
7015                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
7016                     {
7017                       /* To do: get color as function of indexes[x] */
7018                       /*
7019                       if (image->storage_class == PseudoClass)
7020                         {
7021                         }
7022                       */
7023
7024                       if (magn_methy <= 1)
7025                         {
7026                           /* replicate previous */
7027                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
7028                           SetPixelGreen(large_image,GetPixelGreen(image,
7029                              pixels),q);
7030                           SetPixelBlue(large_image,GetPixelBlue(image,
7031                              pixels),q);
7032                           SetPixelAlpha(large_image,GetPixelAlpha(image,
7033                              pixels),q);
7034                         }
7035
7036                       else if (magn_methy == 2 || magn_methy == 4)
7037                         {
7038                           if (i == 0)
7039                             {
7040                               SetPixelRed(large_image,GetPixelRed(image,
7041                                  pixels),q);
7042                               SetPixelGreen(large_image,GetPixelGreen(image,
7043                                  pixels),q);
7044                               SetPixelBlue(large_image,GetPixelBlue(image,
7045                                  pixels),q);
7046                               SetPixelAlpha(large_image,GetPixelAlpha(image,
7047                                  pixels),q);
7048                             }
7049
7050                           else
7051                             {
7052                               /* Interpolate */
7053                               SetPixelRed(large_image,((QM) (((ssize_t)
7054                                  (2*i*(GetPixelRed(image,n)
7055                                  -GetPixelRed(image,pixels)+m))/
7056                                  ((ssize_t) (m*2))
7057                                  +GetPixelRed(image,pixels)))),q);
7058                               SetPixelGreen(large_image,((QM) (((ssize_t)
7059                                  (2*i*(GetPixelGreen(image,n)
7060                                  -GetPixelGreen(image,pixels)+m))/
7061                                  ((ssize_t) (m*2))
7062                                  +GetPixelGreen(image,pixels)))),q);
7063                               SetPixelBlue(large_image,((QM) (((ssize_t)
7064                                  (2*i*(GetPixelBlue(image,n)
7065                                  -GetPixelBlue(image,pixels)+m))/
7066                                  ((ssize_t) (m*2))
7067                                  +GetPixelBlue(image,pixels)))),q);
7068
7069                               if (image->alpha_trait != UndefinedPixelTrait)
7070                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
7071                                     (2*i*(GetPixelAlpha(image,n)
7072                                     -GetPixelAlpha(image,pixels)+m))
7073                                     /((ssize_t) (m*2))+
7074                                    GetPixelAlpha(image,pixels)))),q);
7075                             }
7076
7077                           if (magn_methy == 4)
7078                             {
7079                               /* Replicate nearest */
7080                               if (i <= ((m+1) << 1))
7081                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
7082                                     pixels),q);
7083                               else
7084                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
7085                                     n),q);
7086                             }
7087                         }
7088
7089                       else /* if (magn_methy == 3 || magn_methy == 5) */
7090                         {
7091                           /* Replicate nearest */
7092                           if (i <= ((m+1) << 1))
7093                           {
7094                              SetPixelRed(large_image,GetPixelRed(image,
7095                                     pixels),q);
7096                              SetPixelGreen(large_image,GetPixelGreen(image,
7097                                     pixels),q);
7098                              SetPixelBlue(large_image,GetPixelBlue(image,
7099                                     pixels),q);
7100                              SetPixelAlpha(large_image,GetPixelAlpha(image,
7101                                     pixels),q);
7102                           }
7103
7104                           else
7105                           {
7106                              SetPixelRed(large_image,GetPixelRed(image,n),q);
7107                              SetPixelGreen(large_image,GetPixelGreen(image,n),
7108                                     q);
7109                              SetPixelBlue(large_image,GetPixelBlue(image,n),
7110                                     q);
7111                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
7112                                     q);
7113                           }
7114
7115                           if (magn_methy == 5)
7116                             {
7117                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
7118                                  (GetPixelAlpha(image,n)
7119                                  -GetPixelAlpha(image,pixels))
7120                                  +m))/((ssize_t) (m*2))
7121                                  +GetPixelAlpha(image,pixels)),q);
7122                             }
7123                         }
7124                       n+=GetPixelChannels(image);
7125                       q+=GetPixelChannels(large_image);
7126                       pixels+=GetPixelChannels(image);
7127                     } /* x */
7128
7129                     if (SyncAuthenticPixels(large_image,exception) == 0)
7130                       break;
7131
7132                   } /* i */
7133                 } /* y */
7134
7135                 prev=(Quantum *) RelinquishMagickMemory(prev);
7136                 next=(Quantum *) RelinquishMagickMemory(next);
7137
7138                 length=image->columns;
7139
7140                 if (logging != MagickFalse)
7141                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7142                     "    Delete original image");
7143
7144                 DeleteImageFromList(&image);
7145
7146                 image=large_image;
7147
7148                 mng_info->image=image;
7149
7150                 /* magnify the columns */
7151                 if (logging != MagickFalse)
7152                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7153                     "    Magnify the columns to %.20g",
7154                     (double) image->columns);
7155
7156                 for (y=0; y < (ssize_t) image->rows; y++)
7157                 {
7158                   register Quantum
7159                     *pixels;
7160
7161                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7162                   pixels=q+(image->columns-length)*GetPixelChannels(image);
7163                   n=pixels+GetPixelChannels(image);
7164
7165                   for (x=(ssize_t) (image->columns-length);
7166                     x < (ssize_t) image->columns; x++)
7167                   {
7168                     /* To do: Rewrite using Get/Set***PixelChannel() */
7169
7170                     if (x == (ssize_t) (image->columns-length))
7171                       m=(ssize_t) mng_info->magn_ml;
7172
7173                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
7174                       m=(ssize_t) mng_info->magn_mr;
7175
7176                     else if (magn_methx <= 1 &&
7177                         x == (ssize_t) image->columns-1)
7178                       m=(ssize_t) mng_info->magn_mr;
7179
7180                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
7181                       m=1;
7182
7183                     else
7184                       m=(ssize_t) mng_info->magn_mx;
7185
7186                     for (i=0; i < m; i++)
7187                     {
7188                       if (magn_methx <= 1)
7189                         {
7190                           /* replicate previous */
7191                           SetPixelRed(image,GetPixelRed(image,pixels),q);
7192                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7193                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7194                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7195                         }
7196
7197                       else if (magn_methx == 2 || magn_methx == 4)
7198                         {
7199                           if (i == 0)
7200                           {
7201                             SetPixelRed(image,GetPixelRed(image,pixels),q);
7202                             SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7203                             SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7204                             SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7205                           }
7206
7207                           /* To do: Rewrite using Get/Set***PixelChannel() */
7208                           else
7209                             {
7210                               /* Interpolate */
7211                               SetPixelRed(image,(QM) ((2*i*(
7212                                  GetPixelRed(image,n)
7213                                  -GetPixelRed(image,pixels))+m)
7214                                  /((ssize_t) (m*2))+
7215                                  GetPixelRed(image,pixels)),q);
7216
7217                               SetPixelGreen(image,(QM) ((2*i*(
7218                                  GetPixelGreen(image,n)
7219                                  -GetPixelGreen(image,pixels))+m)
7220                                  /((ssize_t) (m*2))+
7221                                  GetPixelGreen(image,pixels)),q);
7222
7223                               SetPixelBlue(image,(QM) ((2*i*(
7224                                  GetPixelBlue(image,n)
7225                                  -GetPixelBlue(image,pixels))+m)
7226                                  /((ssize_t) (m*2))+
7227                                  GetPixelBlue(image,pixels)),q);
7228                               if (image->alpha_trait != UndefinedPixelTrait)
7229                                  SetPixelAlpha(image,(QM) ((2*i*(
7230                                    GetPixelAlpha(image,n)
7231                                    -GetPixelAlpha(image,pixels))+m)
7232                                    /((ssize_t) (m*2))+
7233                                    GetPixelAlpha(image,pixels)),q);
7234                             }
7235
7236                           if (magn_methx == 4)
7237                             {
7238                               /* Replicate nearest */
7239                               if (i <= ((m+1) << 1))
7240                               {
7241                                  SetPixelAlpha(image,
7242                                    GetPixelAlpha(image,pixels)+0,q);
7243                               }
7244                               else
7245                               {
7246                                  SetPixelAlpha(image,
7247                                    GetPixelAlpha(image,n)+0,q);
7248                               }
7249                             }
7250                         }
7251
7252                       else /* if (magn_methx == 3 || magn_methx == 5) */
7253                         {
7254                           /* Replicate nearest */
7255                           if (i <= ((m+1) << 1))
7256                           {
7257                              SetPixelRed(image,GetPixelRed(image,pixels),q);
7258                              SetPixelGreen(image,GetPixelGreen(image,
7259                                  pixels),q);
7260                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7261                              SetPixelAlpha(image,GetPixelAlpha(image,
7262                                  pixels),q);
7263                           }
7264
7265                           else
7266                           {
7267                              SetPixelRed(image,GetPixelRed(image,n),q);
7268                              SetPixelGreen(image,GetPixelGreen(image,n),q);
7269                              SetPixelBlue(image,GetPixelBlue(image,n),q);
7270                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7271                           }
7272
7273                           if (magn_methx == 5)
7274                             {
7275                               /* Interpolate */
7276                               SetPixelAlpha(image,
7277                                  (QM) ((2*i*( GetPixelAlpha(image,n)
7278                                  -GetPixelAlpha(image,pixels))+m)/
7279                                  ((ssize_t) (m*2))
7280                                  +GetPixelAlpha(image,pixels)),q);
7281                             }
7282                         }
7283                       q+=GetPixelChannels(image);
7284                     }
7285                     n+=GetPixelChannels(image);
7286                   }
7287
7288                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
7289                     break;
7290                 }
7291 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7292               if (magn_methx != 1 || magn_methy != 1)
7293                 {
7294                 /*
7295                    Rescale pixels to Quantum
7296                 */
7297                    for (y=0; y < (ssize_t) image->rows; y++)
7298                    {
7299                      q=GetAuthenticPixels(image,0,y,image->columns,1,
7300                        exception);
7301
7302                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
7303                      {
7304                         SetPixelRed(image,ScaleShortToQuantum(
7305                           GetPixelRed(image,q)),q);
7306                         SetPixelGreen(image,ScaleShortToQuantum(
7307                           GetPixelGreen(image,q)),q);
7308                         SetPixelBlue(image,ScaleShortToQuantum(
7309                           GetPixelBlue(image,q)),q);
7310                         SetPixelAlpha(image,ScaleShortToQuantum(
7311                           GetPixelAlpha(image,q)),q);
7312                         q+=GetPixelChannels(image);
7313                      }
7314
7315                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
7316                        break;
7317                    }
7318                 }
7319 #endif
7320                 if (logging != MagickFalse)
7321                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7322                     "  Finished MAGN processing");
7323               }
7324           }
7325
7326         /*
7327           Crop_box is with respect to the upper left corner of the MNG.
7328         */
7329         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7330         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7331         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7332         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7333         crop_box=mng_minimum_box(crop_box,mng_info->clip);
7334         crop_box=mng_minimum_box(crop_box,mng_info->frame);
7335         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7336         if ((crop_box.left != (mng_info->image_box.left
7337             +mng_info->x_off[object_id])) ||
7338             (crop_box.right != (mng_info->image_box.right
7339             +mng_info->x_off[object_id])) ||
7340             (crop_box.top != (mng_info->image_box.top
7341             +mng_info->y_off[object_id])) ||
7342             (crop_box.bottom != (mng_info->image_box.bottom
7343             +mng_info->y_off[object_id])))
7344           {
7345             if (logging != MagickFalse)
7346               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7347                 "  Crop the PNG image");
7348
7349             if ((crop_box.left < crop_box.right) &&
7350                 (crop_box.top < crop_box.bottom))
7351               {
7352                 Image
7353                   *im;
7354
7355                 RectangleInfo
7356                   crop_info;
7357
7358                 /*
7359                   Crop_info is with respect to the upper left corner of
7360                   the image.
7361                 */
7362                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7363                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7364                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7365                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7366                 image->page.width=image->columns;
7367                 image->page.height=image->rows;
7368                 image->page.x=0;
7369                 image->page.y=0;
7370                 im=CropImage(image,&crop_info,exception);
7371
7372                 if (im != (Image *) NULL)
7373                   {
7374                     image->columns=im->columns;
7375                     image->rows=im->rows;
7376                     im=DestroyImage(im);
7377                     image->page.width=image->columns;
7378                     image->page.height=image->rows;
7379                     image->page.x=crop_box.left;
7380                     image->page.y=crop_box.top;
7381                   }
7382               }
7383
7384             else
7385               {
7386                 /*
7387                   No pixels in crop area.  The MNG spec still requires
7388                   a layer, though, so make a single transparent pixel in
7389                   the top left corner.
7390                 */
7391                 image->columns=1;
7392                 image->rows=1;
7393                 image->colors=2;
7394                 (void) SetImageBackgroundColor(image,exception);
7395                 image->page.width=1;
7396                 image->page.height=1;
7397                 image->page.x=0;
7398                 image->page.y=0;
7399               }
7400           }
7401 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7402         image=mng_info->image;
7403 #endif
7404       }
7405
7406 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7407       /* PNG does not handle depths greater than 16 so reduce it even
7408        * if lossy.
7409        */
7410       if (image->depth > 16)
7411          image->depth=16;
7412 #endif
7413
7414 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7415       if (image->depth > 8)
7416         {
7417           /* To do: fill low byte properly */
7418           image->depth=16;
7419         }
7420
7421       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7422          image->depth = 8;
7423 #endif
7424
7425       if (image_info->number_scenes != 0)
7426         {
7427           if (mng_info->scenes_found >
7428              (ssize_t) (image_info->first_scene+image_info->number_scenes))
7429             break;
7430         }
7431
7432       if (logging != MagickFalse)
7433         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7434           "  Finished reading image datastream.");
7435
7436   } while (LocaleCompare(image_info->magick,"MNG") == 0);
7437
7438   (void) CloseBlob(image);
7439
7440   if (logging != MagickFalse)
7441     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7442       "  Finished reading all image datastreams.");
7443
7444 #if defined(MNG_INSERT_LAYERS)
7445   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7446        (mng_info->mng_height))
7447     {
7448       /*
7449         Insert a background layer if nothing else was found.
7450       */
7451       if (logging != MagickFalse)
7452         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7453           "  No images found.  Inserting a background layer.");
7454
7455       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7456         {
7457           /*
7458             Allocate next image structure.
7459           */
7460           AcquireNextImage(image_info,image,exception);
7461           if (GetNextImageInList(image) == (Image *) NULL)
7462             {
7463               if (logging != MagickFalse)
7464                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7465                   "  Allocation failed, returning NULL.");
7466
7467               return(DestroyImageList(image));;
7468             }
7469           image=SyncNextImageInList(image);
7470         }
7471       image->columns=mng_info->mng_width;
7472       image->rows=mng_info->mng_height;
7473       image->page.width=mng_info->mng_width;
7474       image->page.height=mng_info->mng_height;
7475       image->page.x=0;
7476       image->page.y=0;
7477       image->background_color=mng_background_color;
7478       image->alpha_trait=UndefinedPixelTrait;
7479
7480       if (image_info->ping == MagickFalse)
7481         (void) SetImageBackgroundColor(image,exception);
7482
7483       mng_info->image_found++;
7484     }
7485 #endif
7486   image->iterations=mng_iterations;
7487
7488   if (mng_iterations == 1)
7489     image->start_loop=MagickTrue;
7490
7491   while (GetPreviousImageInList(image) != (Image *) NULL)
7492   {
7493     image_count++;
7494     if (image_count > 10*mng_info->image_found)
7495       {
7496         if (logging != MagickFalse)
7497           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7498
7499         (void) ThrowMagickException(exception,GetMagickModule(),
7500           CoderError,"Linked list is corrupted, beginning of list not found",
7501           "`%s'",image_info->filename);
7502
7503         return(DestroyImageList(image));
7504       }
7505
7506     image=GetPreviousImageInList(image);
7507
7508     if (GetNextImageInList(image) == (Image *) NULL)
7509       {
7510         if (logging != MagickFalse)
7511           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7512
7513         (void) ThrowMagickException(exception,GetMagickModule(),
7514           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7515           image_info->filename);
7516       }
7517   }
7518
7519   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7520              GetNextImageInList(image) ==
7521      (Image *) NULL)
7522     {
7523       if (logging != MagickFalse)
7524         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7525             "  First image null");
7526
7527       (void) ThrowMagickException(exception,GetMagickModule(),
7528         CoderError,"image->next for first image is NULL but shouldn't be.",
7529         "`%s'",image_info->filename);
7530     }
7531
7532   if (mng_info->image_found == 0)
7533     {
7534       if (logging != MagickFalse)
7535         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7536           "  No visible images found.");
7537
7538       (void) ThrowMagickException(exception,GetMagickModule(),
7539         CoderError,"No visible images in file","`%s'",image_info->filename);
7540
7541       return(DestroyImageList(image));
7542     }
7543
7544   if (mng_info->ticks_per_second)
7545     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7546             final_delay/mng_info->ticks_per_second;
7547
7548   else
7549     image->start_loop=MagickTrue;
7550
7551   /* Find final nonzero image delay */
7552   final_image_delay=0;
7553
7554   while (GetNextImageInList(image) != (Image *) NULL)
7555     {
7556       if (image->delay)
7557         final_image_delay=image->delay;
7558
7559       image=GetNextImageInList(image);
7560     }
7561
7562   if (final_delay < final_image_delay)
7563     final_delay=final_image_delay;
7564
7565   image->delay=final_delay;
7566
7567   if (logging != MagickFalse)
7568       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7569         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7570         (double) final_delay);
7571
7572   if (logging != MagickFalse)
7573     {
7574       int
7575         scene;
7576
7577       scene=0;
7578       image=GetFirstImageInList(image);
7579
7580       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7581         "  Before coalesce:");
7582
7583       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7584         "    scene 0 delay=%.20g",(double) image->delay);
7585
7586       while (GetNextImageInList(image) != (Image *) NULL)
7587       {
7588         image=GetNextImageInList(image);
7589         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7590           "    scene %.20g delay=%.20g",(double) scene++,
7591           (double) image->delay);
7592       }
7593     }
7594
7595   image=GetFirstImageInList(image);
7596 #ifdef MNG_COALESCE_LAYERS
7597   if (insert_layers)
7598     {
7599       Image
7600         *next_image,
7601         *next;
7602
7603       size_t
7604         scene;
7605
7606       if (logging != MagickFalse)
7607         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7608           "  Coalesce Images");
7609
7610       scene=image->scene;
7611       next_image=CoalesceImages(image,exception);
7612
7613       if (next_image == (Image *) NULL)
7614         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7615
7616       image=DestroyImageList(image);
7617       image=next_image;
7618
7619       for (next=image; next != (Image *) NULL; next=next_image)
7620       {
7621          next->page.width=mng_info->mng_width;
7622          next->page.height=mng_info->mng_height;
7623          next->page.x=0;
7624          next->page.y=0;
7625          next->scene=scene++;
7626          next_image=GetNextImageInList(next);
7627
7628          if (next_image == (Image *) NULL)
7629            break;
7630
7631          if (next->delay == 0)
7632            {
7633              scene--;
7634              next_image->previous=GetPreviousImageInList(next);
7635              if (GetPreviousImageInList(next) == (Image *) NULL)
7636                image=next_image;
7637              else
7638                next->previous->next=next_image;
7639              next=DestroyImage(next);
7640            }
7641       }
7642     }
7643 #endif
7644
7645   while (GetNextImageInList(image) != (Image *) NULL)
7646       image=GetNextImageInList(image);
7647
7648   image->dispose=BackgroundDispose;
7649
7650   if (logging != MagickFalse)
7651     {
7652       int
7653         scene;
7654
7655       scene=0;
7656       image=GetFirstImageInList(image);
7657
7658       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7659         "  After coalesce:");
7660
7661       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7662         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7663         (double) image->dispose);
7664
7665       while (GetNextImageInList(image) != (Image *) NULL)
7666       {
7667         image=GetNextImageInList(image);
7668
7669         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7670           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7671           (double) image->delay,(double) image->dispose);
7672       }
7673    }
7674
7675   if (logging != MagickFalse)
7676     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7677       "  exit ReadOneMNGImage();");
7678
7679   return(image);
7680 }
7681
7682 static Image *ReadMNGImage(const ImageInfo *image_info,
7683      ExceptionInfo *exception)
7684 {
7685   Image
7686     *image;
7687
7688   MagickBooleanType
7689     logging,
7690     status;
7691
7692   MngInfo
7693     *mng_info;
7694
7695   /* Open image file.  */
7696
7697   assert(image_info != (const ImageInfo *) NULL);
7698   assert(image_info->signature == MagickCoreSignature);
7699   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
7700      image_info->filename);
7701   assert(exception != (ExceptionInfo *) NULL);
7702   assert(exception->signature == MagickCoreSignature);
7703   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
7704   image=AcquireImage(image_info,exception);
7705   mng_info=(MngInfo *) NULL;
7706   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
7707
7708   if (status == MagickFalse)
7709     return((Image *) NULL);
7710
7711   /* Allocate a MngInfo structure.  */
7712
7713   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7714
7715   if (mng_info == (MngInfo *) NULL)
7716     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7717
7718   /* Initialize members of the MngInfo structure.  */
7719
7720   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
7721   mng_info->image=image;
7722   image=ReadOneMNGImage(mng_info,image_info,exception);
7723   mng_info=MngInfoFreeStruct(mng_info);
7724
7725   if (image == (Image *) NULL)
7726     {
7727       if (logging != MagickFalse)
7728         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7729           "exit ReadMNGImage() with error");
7730
7731       return((Image *) NULL);
7732     }
7733   (void) CloseBlob(image);
7734
7735   if (logging != MagickFalse)
7736     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7737
7738   return(GetFirstImageInList(image));
7739 }
7740 #else /* PNG_LIBPNG_VER > 10011 */
7741 static Image *ReadPNGImage(const ImageInfo *image_info,
7742    ExceptionInfo *exception)
7743 {
7744   printf("Your PNG library is too old: You have libpng-%s\n",
7745      PNG_LIBPNG_VER_STRING);
7746
7747   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7748     "PNG library is too old","`%s'",image_info->filename);
7749
7750   return(Image *) NULL;
7751 }
7752
7753 static Image *ReadMNGImage(const ImageInfo *image_info,
7754    ExceptionInfo *exception)
7755 {
7756   return(ReadPNGImage(image_info,exception));
7757 }
7758 #endif /* PNG_LIBPNG_VER > 10011 */
7759 #endif
7760 \f
7761 /*
7762 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7763 %                                                                             %
7764 %                                                                             %
7765 %                                                                             %
7766 %   R e g i s t e r P N G I m a g e                                           %
7767 %                                                                             %
7768 %                                                                             %
7769 %                                                                             %
7770 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7771 %
7772 %  RegisterPNGImage() adds properties for the PNG image format to
7773 %  the list of supported formats.  The properties include the image format
7774 %  tag, a method to read and/or write the format, whether the format
7775 %  supports the saving of more than one frame to the same file or blob,
7776 %  whether the format supports native in-memory I/O, and a brief
7777 %  description of the format.
7778 %
7779 %  The format of the RegisterPNGImage method is:
7780 %
7781 %      size_t RegisterPNGImage(void)
7782 %
7783 */
7784 ModuleExport size_t RegisterPNGImage(void)
7785 {
7786   char
7787     version[MagickPathExtent];
7788
7789   MagickInfo
7790     *entry;
7791
7792   static const char
7793     *PNGNote=
7794     {
7795       "See http://www.libpng.org/ for details about the PNG format."
7796     },
7797
7798     *JNGNote=
7799     {
7800       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7801       "format."
7802     },
7803
7804     *MNGNote=
7805     {
7806       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7807       "format."
7808     };
7809
7810   *version='\0';
7811
7812 #if defined(PNG_LIBPNG_VER_STRING)
7813   (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7814   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
7815    MagickPathExtent);
7816
7817   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7818     {
7819       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7820       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7821             MagickPathExtent);
7822     }
7823 #endif
7824
7825   entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7826   entry->flags|=CoderSeekableStreamFlag;  /* To do: eliminate this. */
7827
7828 #if defined(MAGICKCORE_PNG_DELEGATE)
7829   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7830   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7831 #endif
7832
7833   entry->magick=(IsImageFormatHandler *) IsMNG;
7834
7835   if (*version != '\0')
7836     entry->version=ConstantString(version);
7837
7838   entry->mime_type=ConstantString("video/x-mng");
7839   entry->note=ConstantString(MNGNote);
7840   (void) RegisterMagickInfo(entry);
7841
7842   entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7843
7844 #if defined(MAGICKCORE_PNG_DELEGATE)
7845   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7846   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7847 #endif
7848
7849   entry->magick=(IsImageFormatHandler *) IsPNG;
7850   entry->flags^=CoderAdjoinFlag;
7851   entry->mime_type=ConstantString("image/png");
7852
7853   if (*version != '\0')
7854     entry->version=ConstantString(version);
7855
7856   entry->note=ConstantString(PNGNote);
7857   (void) RegisterMagickInfo(entry);
7858
7859   entry=AcquireMagickInfo("PNG","PNG8",
7860     "8-bit indexed with optional binary transparency");
7861
7862 #if defined(MAGICKCORE_PNG_DELEGATE)
7863   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7864   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7865 #endif
7866
7867   entry->magick=(IsImageFormatHandler *) IsPNG;
7868   entry->flags^=CoderAdjoinFlag;
7869   entry->mime_type=ConstantString("image/png");
7870   (void) RegisterMagickInfo(entry);
7871
7872   entry=AcquireMagickInfo("PNG","PNG24",
7873     "opaque or binary transparent 24-bit RGB");
7874   *version='\0';
7875
7876 #if defined(ZLIB_VERSION)
7877   (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7878   (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7879
7880   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7881     {
7882       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7883       (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7884     }
7885 #endif
7886
7887   if (*version != '\0')
7888     entry->version=ConstantString(version);
7889
7890 #if defined(MAGICKCORE_PNG_DELEGATE)
7891   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7892   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7893 #endif
7894
7895   entry->magick=(IsImageFormatHandler *) IsPNG;
7896   entry->flags^=CoderAdjoinFlag;
7897   entry->mime_type=ConstantString("image/png");
7898   (void) RegisterMagickInfo(entry);
7899
7900   entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7901
7902 #if defined(MAGICKCORE_PNG_DELEGATE)
7903   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7904   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7905 #endif
7906
7907   entry->magick=(IsImageFormatHandler *) IsPNG;
7908   entry->flags^=CoderAdjoinFlag;
7909   entry->mime_type=ConstantString("image/png");
7910   (void) RegisterMagickInfo(entry);
7911
7912   entry=AcquireMagickInfo("PNG","PNG48",
7913     "opaque or binary transparent 48-bit RGB");
7914
7915 #if defined(MAGICKCORE_PNG_DELEGATE)
7916   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7917   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7918 #endif
7919
7920   entry->magick=(IsImageFormatHandler *) IsPNG;
7921   entry->flags^=CoderAdjoinFlag;
7922   entry->mime_type=ConstantString("image/png");
7923   (void) RegisterMagickInfo(entry);
7924
7925   entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7926
7927 #if defined(MAGICKCORE_PNG_DELEGATE)
7928   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7929   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7930 #endif
7931
7932   entry->magick=(IsImageFormatHandler *) IsPNG;
7933   entry->flags^=CoderAdjoinFlag;
7934   entry->mime_type=ConstantString("image/png");
7935   (void) RegisterMagickInfo(entry);
7936
7937   entry=AcquireMagickInfo("PNG","PNG00",
7938     "PNG inheriting bit-depth, color-type from original, if possible");
7939
7940 #if defined(MAGICKCORE_PNG_DELEGATE)
7941   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7942   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7943 #endif
7944
7945   entry->magick=(IsImageFormatHandler *) IsPNG;
7946   entry->flags^=CoderAdjoinFlag;
7947   entry->mime_type=ConstantString("image/png");
7948   (void) RegisterMagickInfo(entry);
7949
7950   entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7951
7952 #if defined(JNG_SUPPORTED)
7953 #if defined(MAGICKCORE_PNG_DELEGATE)
7954   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7955   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7956 #endif
7957 #endif
7958
7959   entry->magick=(IsImageFormatHandler *) IsJNG;
7960   entry->flags^=CoderAdjoinFlag;
7961   entry->mime_type=ConstantString("image/x-jng");
7962   entry->note=ConstantString(JNGNote);
7963   (void) RegisterMagickInfo(entry);
7964
7965 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7966   ping_semaphore=AcquireSemaphoreInfo();
7967 #endif
7968
7969   return(MagickImageCoderSignature);
7970 }
7971 \f
7972 /*
7973 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7974 %                                                                             %
7975 %                                                                             %
7976 %                                                                             %
7977 %   U n r e g i s t e r P N G I m a g e                                       %
7978 %                                                                             %
7979 %                                                                             %
7980 %                                                                             %
7981 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7982 %
7983 %  UnregisterPNGImage() removes format registrations made by the
7984 %  PNG module from the list of supported formats.
7985 %
7986 %  The format of the UnregisterPNGImage method is:
7987 %
7988 %      UnregisterPNGImage(void)
7989 %
7990 */
7991 ModuleExport void UnregisterPNGImage(void)
7992 {
7993   (void) UnregisterMagickInfo("MNG");
7994   (void) UnregisterMagickInfo("PNG");
7995   (void) UnregisterMagickInfo("PNG8");
7996   (void) UnregisterMagickInfo("PNG24");
7997   (void) UnregisterMagickInfo("PNG32");
7998   (void) UnregisterMagickInfo("PNG48");
7999   (void) UnregisterMagickInfo("PNG64");
8000   (void) UnregisterMagickInfo("PNG00");
8001   (void) UnregisterMagickInfo("JNG");
8002
8003 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
8004   if (ping_semaphore != (SemaphoreInfo *) NULL)
8005     RelinquishSemaphoreInfo(&ping_semaphore);
8006 #endif
8007 }
8008 \f
8009 #if defined(MAGICKCORE_PNG_DELEGATE)
8010 #if PNG_LIBPNG_VER > 10011
8011 /*
8012 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8013 %                                                                             %
8014 %                                                                             %
8015 %                                                                             %
8016 %   W r i t e M N G I m a g e                                                 %
8017 %                                                                             %
8018 %                                                                             %
8019 %                                                                             %
8020 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8021 %
8022 %  WriteMNGImage() writes an image in the Portable Network Graphics
8023 %  Group's "Multiple-image Network Graphics" encoded image format.
8024 %
8025 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
8026 %
8027 %  The format of the WriteMNGImage method is:
8028 %
8029 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
8030 %        Image *image,ExceptionInfo *exception)
8031 %
8032 %  A description of each parameter follows.
8033 %
8034 %    o image_info: the image info.
8035 %
8036 %    o image:  The image.
8037 %
8038 %    o exception: return any errors or warnings in this structure.
8039 %
8040 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
8041 %    "To do" under ReadPNGImage):
8042 %
8043 %    Preserve all unknown and not-yet-handled known chunks found in input
8044 %    PNG file and copy them  into output PNG files according to the PNG
8045 %    copying rules.
8046 %
8047 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
8048 %
8049 %    Improve selection of color type (use indexed-colour or indexed-colour
8050 %    with tRNS when 256 or fewer unique RGBA values are present).
8051 %
8052 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
8053 %    This will be complicated if we limit ourselves to generating MNG-LC
8054 %    files.  For now we ignore disposal method 3 and simply overlay the next
8055 %    image on it.
8056 %
8057 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
8058 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
8059 %    [mostly done 15 June 1999 but still need to take care of tRNS]
8060 %
8061 %    Check for identical sRGB and replace with a global sRGB (and remove
8062 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
8063 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
8064 %    local gAMA/cHRM with local sRGB if appropriate).
8065 %
8066 %    Check for identical sBIT chunks and write global ones.
8067 %
8068 %    Provide option to skip writing the signature tEXt chunks.
8069 %
8070 %    Use signatures to detect identical objects and reuse the first
8071 %    instance of such objects instead of writing duplicate objects.
8072 %
8073 %    Use a smaller-than-32k value of compression window size when
8074 %    appropriate.
8075 %
8076 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
8077 %    ancillary text chunks and save profiles.
8078 %
8079 %    Provide an option to force LC files (to ensure exact framing rate)
8080 %    instead of VLC.
8081 %
8082 %    Provide an option to force VLC files instead of LC, even when offsets
8083 %    are present.  This will involve expanding the embedded images with a
8084 %    transparent region at the top and/or left.
8085 */
8086
8087 static void
8088 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
8089    png_info *ping_info, unsigned char *profile_type, unsigned char
8090    *profile_description, unsigned char *profile_data, png_uint_32 length)
8091 {
8092    png_textp
8093      text;
8094
8095    register ssize_t
8096      i;
8097
8098    unsigned char
8099      *sp;
8100
8101    png_charp
8102      dp;
8103
8104    png_uint_32
8105      allocated_length,
8106      description_length;
8107
8108    unsigned char
8109      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
8110
8111    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
8112       return;
8113
8114    if (image_info->verbose)
8115      {
8116        (void) printf("writing raw profile: type=%s, length=%.20g\n",
8117          (char *) profile_type, (double) length);
8118      }
8119
8120 #if PNG_LIBPNG_VER >= 10400
8121    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
8122 #else
8123    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
8124 #endif
8125    description_length=(png_uint_32) strlen((const char *) profile_description);
8126    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
8127       + description_length);
8128 #if PNG_LIBPNG_VER >= 10400
8129    text[0].text=(png_charp) png_malloc(ping,
8130       (png_alloc_size_t) allocated_length);
8131    text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
8132 #else
8133    text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
8134    text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
8135 #endif
8136    text[0].key[0]='\0';
8137    (void) ConcatenateMagickString(text[0].key,
8138       "Raw profile type ",MagickPathExtent);
8139    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
8140    sp=profile_data;
8141    dp=text[0].text;
8142    *dp++='\n';
8143    (void) CopyMagickString(dp,(const char *) profile_description,
8144      allocated_length);
8145    dp+=description_length;
8146    *dp++='\n';
8147    (void) FormatLocaleString(dp,allocated_length-
8148      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
8149    dp+=8;
8150
8151    for (i=0; i < (ssize_t) length; i++)
8152    {
8153      if (i%36 == 0)
8154        *dp++='\n';
8155      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
8156      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
8157    }
8158
8159    *dp++='\n';
8160    *dp='\0';
8161    text[0].text_length=(png_size_t) (dp-text[0].text);
8162    text[0].compression=image_info->compression == NoCompression ||
8163      (image_info->compression == UndefinedCompression &&
8164      text[0].text_length < 128) ? -1 : 0;
8165
8166    if (text[0].text_length <= allocated_length)
8167      png_set_text(ping,ping_info,text,1);
8168
8169    png_free(ping,text[0].text);
8170    png_free(ping,text[0].key);
8171    png_free(ping,text);
8172 }
8173
8174 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
8175   const char *string, MagickBooleanType logging)
8176 {
8177   char
8178     *name;
8179
8180   const StringInfo
8181     *profile;
8182
8183   unsigned char
8184     *data;
8185
8186   png_uint_32 length;
8187
8188   ResetImageProfileIterator(image);
8189
8190   for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8191   {
8192     profile=GetImageProfile(image,name);
8193
8194     if (profile != (const StringInfo *) NULL)
8195       {
8196         StringInfo
8197           *ping_profile;
8198
8199         if (LocaleNCompare(name,string,11) == 0)
8200           {
8201             if (logging != MagickFalse)
8202                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8203                    "  Found %s profile",name);
8204
8205             ping_profile=CloneStringInfo(profile);
8206             data=GetStringInfoDatum(ping_profile),
8207             length=(png_uint_32) GetStringInfoLength(ping_profile);
8208             data[4]=data[3];
8209             data[3]=data[2];
8210             data[2]=data[1];
8211             data[1]=data[0];
8212             (void) WriteBlobMSBULong(image,length-5);  /* data length */
8213             (void) WriteBlob(image,length-1,data+1);
8214             (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
8215             ping_profile=DestroyStringInfo(ping_profile);
8216           }
8217       }
8218
8219       name=GetNextImageProfile(image);
8220    }
8221
8222    return(MagickTrue);
8223 }
8224
8225 static inline MagickBooleanType Magick_png_color_equal(const Image *image,
8226   const Quantum *p, const PixelInfo *q)
8227 {
8228   MagickRealType
8229     value;
8230
8231   value=(MagickRealType) p[image->channel_map[RedPixelChannel].offset];
8232   if (AbsolutePixelValue(value-q->red) >= MagickEpsilon)
8233     return(MagickFalse);
8234   value=(MagickRealType) p[image->channel_map[GreenPixelChannel].offset];
8235   if (AbsolutePixelValue(value-q->green) >= MagickEpsilon)
8236     return(MagickFalse);
8237   value=(MagickRealType) p[image->channel_map[BluePixelChannel].offset];
8238   if (AbsolutePixelValue(value-q->blue) >= MagickEpsilon)
8239     return(MagickFalse);
8240
8241   return(MagickTrue);
8242 }
8243
8244 #if defined(PNG_tIME_SUPPORTED)
8245 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
8246   const char *date,ExceptionInfo *exception)
8247 {
8248   unsigned int
8249     day,
8250     hour,
8251     minute,
8252     month,
8253     second,
8254     year;
8255
8256   png_time
8257     ptime;
8258
8259   time_t
8260     ttime;
8261
8262   if (date != (const char *) NULL)
8263     {
8264       if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
8265           &second) != 6)
8266         {
8267           (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8268             "Invalid date format specified for png:tIME","`%s'",
8269             image->filename);
8270           return;
8271         }
8272       ptime.year=(png_uint_16) year;
8273       ptime.month=(png_byte) month;
8274       ptime.day=(png_byte) day;
8275       ptime.hour=(png_byte) hour;
8276       ptime.minute=(png_byte) minute;
8277       ptime.second=(png_byte) second;
8278     }
8279   else
8280   {
8281     time(&ttime);
8282     png_convert_from_time_t(&ptime,ttime);
8283   }
8284   png_set_tIME(ping,info,&ptime);
8285 }
8286 #endif
8287
8288 /* Write one PNG image */
8289 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
8290   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
8291 {
8292   char
8293     im_vers[32],
8294     libpng_runv[32],
8295     libpng_vers[32],
8296     zlib_runv[32],
8297     zlib_vers[32];
8298
8299   Image
8300     *image;
8301
8302   ImageInfo
8303     *image_info;
8304
8305   char
8306     s[2];
8307
8308   const char
8309     *name,
8310     *property,
8311     *value;
8312
8313   const StringInfo
8314     *profile;
8315
8316   int
8317     num_passes,
8318     pass,
8319     ping_wrote_caNv;
8320
8321   png_byte
8322      ping_trans_alpha[256];
8323
8324   png_color
8325      palette[257];
8326
8327   png_color_16
8328     ping_background,
8329     ping_trans_color;
8330
8331   png_info
8332     *ping_info;
8333
8334   png_struct
8335     *ping;
8336
8337   png_uint_32
8338     ping_height,
8339     ping_width;
8340
8341   ssize_t
8342     y;
8343
8344   MagickBooleanType
8345     image_matte,
8346     logging,
8347     matte,
8348
8349     ping_have_blob,
8350     ping_have_cheap_transparency,
8351     ping_have_color,
8352     ping_have_non_bw,
8353     ping_have_PLTE,
8354     ping_have_bKGD,
8355     ping_have_eXIf,
8356     ping_have_iCCP,
8357     ping_have_pHYs,
8358     ping_have_sRGB,
8359     ping_have_tRNS,
8360
8361     ping_exclude_bKGD,
8362     ping_exclude_cHRM,
8363     ping_exclude_date,
8364     /* ping_exclude_EXIF, */
8365     ping_exclude_eXIf,
8366     ping_exclude_gAMA,
8367     ping_exclude_iCCP,
8368     /* ping_exclude_iTXt, */
8369     ping_exclude_oFFs,
8370     ping_exclude_pHYs,
8371     ping_exclude_sRGB,
8372     ping_exclude_tEXt,
8373     ping_exclude_tIME,
8374     /* ping_exclude_tRNS, */
8375     ping_exclude_vpAg,
8376     ping_exclude_caNv,
8377     ping_exclude_zCCP, /* hex-encoded iCCP */
8378     ping_exclude_zTXt,
8379
8380     ping_preserve_colormap,
8381     ping_preserve_iCCP,
8382     ping_need_colortype_warning,
8383
8384     status,
8385     tried_332,
8386     tried_333,
8387     tried_444;
8388
8389   MemoryInfo
8390     *volatile pixel_info;
8391
8392   QuantumInfo
8393     *quantum_info;
8394
8395   PNGErrorInfo
8396     error_info;
8397
8398   register ssize_t
8399     i,
8400     x;
8401
8402   unsigned char
8403     *ping_pixels;
8404
8405   volatile int
8406     image_colors,
8407     ping_bit_depth,
8408     ping_color_type,
8409     ping_interlace_method,
8410     ping_compression_method,
8411     ping_filter_method,
8412     ping_num_trans;
8413
8414   volatile size_t
8415     image_depth,
8416     old_bit_depth;
8417
8418   size_t
8419     quality,
8420     rowbytes,
8421     save_image_depth;
8422
8423   int
8424     j,
8425     number_colors,
8426     number_opaque,
8427     number_semitransparent,
8428     number_transparent,
8429     ping_pHYs_unit_type;
8430
8431   png_uint_32
8432     ping_pHYs_x_resolution,
8433     ping_pHYs_y_resolution;
8434
8435   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8436     "  Enter WriteOnePNGImage()");
8437
8438   image = CloneImage(IMimage,0,0,MagickFalse,exception);
8439   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8440   if (image_info == (ImageInfo *) NULL)
8441      ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8442
8443   /* Define these outside of the following "if logging()" block so they will
8444    * show in debuggers.
8445    */
8446   *im_vers='\0';
8447   (void) ConcatenateMagickString(im_vers,
8448          MagickLibVersionText,MagickPathExtent);
8449   (void) ConcatenateMagickString(im_vers,
8450          MagickLibAddendum,MagickPathExtent);
8451
8452   *libpng_vers='\0';
8453   (void) ConcatenateMagickString(libpng_vers,
8454          PNG_LIBPNG_VER_STRING,32);
8455   *libpng_runv='\0';
8456   (void) ConcatenateMagickString(libpng_runv,
8457          png_get_libpng_ver(NULL),32);
8458
8459   *zlib_vers='\0';
8460   (void) ConcatenateMagickString(zlib_vers,
8461          ZLIB_VERSION,32);
8462   *zlib_runv='\0';
8463   (void) ConcatenateMagickString(zlib_runv,
8464          zlib_version,32);
8465
8466   if (logging != MagickFalse)
8467     {
8468        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"    IM version     = %s",
8469            im_vers);
8470        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"    Libpng version = %s",
8471            libpng_vers);
8472        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8473        {
8474        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8475            libpng_runv);
8476        }
8477        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
8478            zlib_vers);
8479        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8480        {
8481        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8482            zlib_runv);
8483        }
8484     }
8485
8486   /* Initialize some stuff */
8487   ping_bit_depth=0,
8488   ping_color_type=0,
8489   ping_interlace_method=0,
8490   ping_compression_method=0,
8491   ping_filter_method=0,
8492   ping_num_trans = 0;
8493
8494   ping_background.red = 0;
8495   ping_background.green = 0;
8496   ping_background.blue = 0;
8497   ping_background.gray = 0;
8498   ping_background.index = 0;
8499
8500   ping_trans_color.red=0;
8501   ping_trans_color.green=0;
8502   ping_trans_color.blue=0;
8503   ping_trans_color.gray=0;
8504
8505   ping_pHYs_unit_type = 0;
8506   ping_pHYs_x_resolution = 0;
8507   ping_pHYs_y_resolution = 0;
8508
8509   ping_have_blob=MagickFalse;
8510   ping_have_cheap_transparency=MagickFalse;
8511   ping_have_color=MagickTrue;
8512   ping_have_non_bw=MagickTrue;
8513   ping_have_PLTE=MagickFalse;
8514   ping_have_bKGD=MagickFalse;
8515   ping_have_eXIf=MagickTrue;
8516   ping_have_iCCP=MagickFalse;
8517   ping_have_pHYs=MagickFalse;
8518   ping_have_sRGB=MagickFalse;
8519   ping_have_tRNS=MagickFalse;
8520
8521   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8522   ping_exclude_caNv=mng_info->ping_exclude_caNv;
8523   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8524   ping_exclude_date=mng_info->ping_exclude_date;
8525   /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8526   ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
8527   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8528   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8529   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8530   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8531   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8532   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8533   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8534   ping_exclude_tIME=mng_info->ping_exclude_tIME;
8535   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8536   ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8537   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8538   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8539
8540   ping_preserve_colormap = mng_info->ping_preserve_colormap;
8541   ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8542   ping_need_colortype_warning = MagickFalse;
8543
8544   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8545    * i.e., eliminate the ICC profile and set image->rendering_intent.
8546    * Note that this will not involve any changes to the actual pixels
8547    * but merely passes information to applications that read the resulting
8548    * PNG image.
8549    *
8550    * To do: recognize other variants of the sRGB profile, using the CRC to
8551    * verify all recognized variants including the 7 already known.
8552    *
8553    * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8554    *
8555    * Use something other than image->rendering_intent to record the fact
8556    * that the sRGB profile was found.
8557    *
8558    * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8559    * profile.  Record the Blackpoint Compensation, if any.
8560    */
8561    if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8562    {
8563       char
8564         *name;
8565
8566       const StringInfo
8567         *profile;
8568
8569       ResetImageProfileIterator(image);
8570       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8571       {
8572         profile=GetImageProfile(image,name);
8573
8574         if (profile != (StringInfo *) NULL)
8575           {
8576             if ((LocaleCompare(name,"ICC") == 0) ||
8577                 (LocaleCompare(name,"ICM") == 0))
8578
8579              {
8580                  int
8581                    icheck,
8582                    got_crc=0;
8583
8584
8585                  png_uint_32
8586                    length,
8587                    profile_crc=0;
8588
8589                  unsigned char
8590                    *data;
8591
8592                  length=(png_uint_32) GetStringInfoLength(profile);
8593
8594                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8595                  {
8596                    if (length == sRGB_info[icheck].len)
8597                    {
8598                      if (got_crc == 0)
8599                      {
8600                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8601                          "    Got a %lu-byte ICC profile (potentially sRGB)",
8602                          (unsigned long) length);
8603
8604                        data=GetStringInfoDatum(profile);
8605                        profile_crc=crc32(0,data,length);
8606
8607                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8608                            "      with crc=%8x",(unsigned int) profile_crc);
8609                        got_crc++;
8610                      }
8611
8612                      if (profile_crc == sRGB_info[icheck].crc)
8613                      {
8614                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8615                             "      It is sRGB with rendering intent = %s",
8616                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
8617                              sRGB_info[icheck].intent));
8618                         if (image->rendering_intent==UndefinedIntent)
8619                         {
8620                           image->rendering_intent=
8621                           Magick_RenderingIntent_from_PNG_RenderingIntent(
8622                              sRGB_info[icheck].intent);
8623                         }
8624                         ping_exclude_iCCP = MagickTrue;
8625                         ping_exclude_zCCP = MagickTrue;
8626                         ping_have_sRGB = MagickTrue;
8627                         break;
8628                      }
8629                    }
8630                  }
8631                  if (sRGB_info[icheck].len == 0)
8632                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8633                         "    Got %lu-byte ICC profile not recognized as sRGB",
8634                         (unsigned long) length);
8635               }
8636           }
8637         name=GetNextImageProfile(image);
8638       }
8639   }
8640
8641   number_opaque = 0;
8642   number_semitransparent = 0;
8643   number_transparent = 0;
8644
8645   if (logging != MagickFalse)
8646     {
8647       if (image->storage_class == UndefinedClass)
8648           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8649           "    image->storage_class=UndefinedClass");
8650       if (image->storage_class == DirectClass)
8651           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8652           "    image->storage_class=DirectClass");
8653       if (image->storage_class == PseudoClass)
8654           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8655           "    image->storage_class=PseudoClass");
8656       (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8657           "    image->taint=MagickTrue":
8658           "    image->taint=MagickFalse");
8659     }
8660
8661   if (image->storage_class == PseudoClass &&
8662      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8663      mng_info->write_png48 || mng_info->write_png64 ||
8664      (mng_info->write_png_colortype != 1 &&
8665      mng_info->write_png_colortype != 5)))
8666     {
8667       (void) SyncImage(image,exception);
8668       image->storage_class = DirectClass;
8669     }
8670
8671   if (ping_preserve_colormap == MagickFalse)
8672     {
8673       if (image->storage_class != PseudoClass && image->colormap != NULL)
8674         {
8675           /* Free the bogus colormap; it can cause trouble later */
8676            if (logging != MagickFalse)
8677               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8678               "    Freeing bogus colormap");
8679            (void) RelinquishMagickMemory(image->colormap);
8680            image->colormap=NULL;
8681         }
8682     }
8683
8684   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8685     (void) TransformImageColorspace(image,sRGBColorspace,exception);
8686
8687   /*
8688     Sometimes we get PseudoClass images whose RGB values don't match
8689     the colors in the colormap.  This code syncs the RGB values.
8690   */
8691   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8692      (void) SyncImage(image,exception);
8693
8694 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8695   if (image->depth > 8)
8696     {
8697       if (logging != MagickFalse)
8698         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8699           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8700
8701       image->depth=8;
8702     }
8703 #endif
8704
8705   /* Respect the -depth option */
8706   if (image->depth < 4)
8707     {
8708        register Quantum
8709          *r;
8710
8711        if (image->depth > 2)
8712          {
8713            /* Scale to 4-bit */
8714            LBR04PacketRGBO(image->background_color);
8715
8716            for (y=0; y < (ssize_t) image->rows; y++)
8717            {
8718              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8719
8720              if (r == (Quantum *) NULL)
8721                break;
8722
8723              for (x=0; x < (ssize_t) image->columns; x++)
8724              {
8725                 LBR04PixelRGBA(r);
8726                 r+=GetPixelChannels(image);
8727              }
8728
8729              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8730                 break;
8731            }
8732
8733            if (image->storage_class == PseudoClass && image->colormap != NULL)
8734            {
8735              for (i=0; i < (ssize_t) image->colors; i++)
8736              {
8737                LBR04PacketRGBO(image->colormap[i]);
8738              }
8739            }
8740          }
8741        else if (image->depth > 1)
8742          {
8743            /* Scale to 2-bit */
8744            LBR02PacketRGBO(image->background_color);
8745
8746            for (y=0; y < (ssize_t) image->rows; y++)
8747            {
8748              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8749
8750              if (r == (Quantum *) NULL)
8751                break;
8752
8753              for (x=0; x < (ssize_t) image->columns; x++)
8754              {
8755                 LBR02PixelRGBA(r);
8756                 r+=GetPixelChannels(image);
8757              }
8758
8759              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8760                 break;
8761            }
8762
8763            if (image->storage_class == PseudoClass && image->colormap != NULL)
8764            {
8765              for (i=0; i < (ssize_t) image->colors; i++)
8766              {
8767                LBR02PacketRGBO(image->colormap[i]);
8768              }
8769            }
8770          }
8771        else
8772          {
8773            /* Scale to 1-bit */
8774            LBR01PacketRGBO(image->background_color);
8775
8776            for (y=0; y < (ssize_t) image->rows; y++)
8777            {
8778              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8779
8780              if (r == (Quantum *) NULL)
8781                break;
8782
8783              for (x=0; x < (ssize_t) image->columns; x++)
8784              {
8785                 LBR01PixelRGBA(r);
8786                 r+=GetPixelChannels(image);
8787              }
8788
8789              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8790                 break;
8791            }
8792
8793            if (image->storage_class == PseudoClass && image->colormap != NULL)
8794            {
8795              for (i=0; i < (ssize_t) image->colors; i++)
8796              {
8797                LBR01PacketRGBO(image->colormap[i]);
8798              }
8799            }
8800          }
8801     }
8802
8803   /* To do: set to next higher multiple of 8 */
8804   if (image->depth < 8)
8805      image->depth=8;
8806
8807 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8808   /* PNG does not handle depths greater than 16 so reduce it even
8809    * if lossy
8810    */
8811   if (image->depth > 8)
8812       image->depth=16;
8813 #endif
8814
8815 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8816   if (image->depth > 8)
8817     {
8818       /* To do: fill low byte properly */
8819       image->depth=16;
8820     }
8821
8822   if (image->depth == 16 && mng_info->write_png_depth != 16)
8823     if (mng_info->write_png8 ||
8824         LosslessReduceDepthOK(image,exception) != MagickFalse)
8825       image->depth = 8;
8826 #endif
8827
8828   image_colors = (int) image->colors;
8829   number_opaque = (int) image->colors;
8830   number_transparent = 0;
8831   number_semitransparent = 0;
8832
8833   if (mng_info->write_png_colortype &&
8834      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8835      mng_info->write_png_colortype < 4 &&
8836      image->alpha_trait == UndefinedPixelTrait)))
8837   {
8838      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8839       * are not going to need the result.
8840       */
8841      if (mng_info->write_png_colortype == 1 ||
8842         mng_info->write_png_colortype == 5)
8843        ping_have_color=MagickFalse;
8844
8845      if (image->alpha_trait != UndefinedPixelTrait)
8846        {
8847          number_transparent = 2;
8848          number_semitransparent = 1;
8849        }
8850   }
8851
8852   if (mng_info->write_png_colortype < 7)
8853   {
8854   /* BUILD_PALETTE
8855    *
8856    * Normally we run this just once, but in the case of writing PNG8
8857    * we reduce the transparency to binary and run again, then if there
8858    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8859    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8860    * palette.  Then (To do) we take care of a final reduction that is only
8861    * needed if there are still 256 colors present and one of them has both
8862    * transparent and opaque instances.
8863    */
8864
8865   tried_332 = MagickFalse;
8866   tried_333 = MagickFalse;
8867   tried_444 = MagickFalse;
8868
8869   for (j=0; j<6; j++)
8870   {
8871     /*
8872      * Sometimes we get DirectClass images that have 256 colors or fewer.
8873      * This code will build a colormap.
8874      *
8875      * Also, sometimes we get PseudoClass images with an out-of-date
8876      * colormap.  This code will replace the colormap with a new one.
8877      * Sometimes we get PseudoClass images that have more than 256 colors.
8878      * This code will delete the colormap and change the image to
8879      * DirectClass.
8880      *
8881      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8882      * even though it sometimes contains left-over non-opaque values.
8883      *
8884      * Also we gather some information (number of opaque, transparent,
8885      * and semitransparent pixels, and whether the image has any non-gray
8886      * pixels or only black-and-white pixels) that we might need later.
8887      *
8888      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8889      * we need to check for bogus non-opaque values, at least.
8890      */
8891
8892    int
8893      n;
8894
8895    PixelInfo
8896      opaque[260],
8897      semitransparent[260],
8898      transparent[260];
8899
8900    register const Quantum
8901      *s;
8902
8903    register Quantum
8904      *q,
8905      *r;
8906
8907    if (logging != MagickFalse)
8908      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8909          "    Enter BUILD_PALETTE:");
8910
8911    if (logging != MagickFalse)
8912      {
8913        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8914              "      image->columns=%.20g",(double) image->columns);
8915        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8916              "      image->rows=%.20g",(double) image->rows);
8917        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8918              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
8919        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8920              "      image->depth=%.20g",(double) image->depth);
8921
8922        if (image->storage_class == PseudoClass && image->colormap != NULL)
8923        {
8924          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8925              "      Original colormap:");
8926          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8927              "        i    (red,green,blue,alpha)");
8928
8929          for (i=0; i < 256; i++)
8930          {
8931                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8932                    "        %d    (%d,%d,%d,%d)",
8933                     (int) i,
8934                     (int) image->colormap[i].red,
8935                     (int) image->colormap[i].green,
8936                     (int) image->colormap[i].blue,
8937                     (int) image->colormap[i].alpha);
8938          }
8939
8940          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8941          {
8942            if (i > 255)
8943              {
8944                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8945                    "        %d    (%d,%d,%d,%d)",
8946                     (int) i,
8947                     (int) image->colormap[i].red,
8948                     (int) image->colormap[i].green,
8949                     (int) image->colormap[i].blue,
8950                     (int) image->colormap[i].alpha);
8951              }
8952          }
8953        }
8954
8955        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8956            "      image->colors=%d",(int) image->colors);
8957
8958        if (image->colors == 0)
8959          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8960              "        (zero means unknown)");
8961
8962        if (ping_preserve_colormap == MagickFalse)
8963          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8964               "      Regenerate the colormap");
8965      }
8966
8967      image_colors=0;
8968      number_opaque = 0;
8969      number_semitransparent = 0;
8970      number_transparent = 0;
8971
8972      for (y=0; y < (ssize_t) image->rows; y++)
8973      {
8974        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8975
8976        if (q == (Quantum *) NULL)
8977          break;
8978
8979        for (x=0; x < (ssize_t) image->columns; x++)
8980        {
8981            if (image->alpha_trait == UndefinedPixelTrait ||
8982               GetPixelAlpha(image,q) == OpaqueAlpha)
8983              {
8984                if (number_opaque < 259)
8985                  {
8986                    if (number_opaque == 0)
8987                      {
8988                        GetPixelInfoPixel(image, q, opaque);
8989                        opaque[0].alpha=OpaqueAlpha;
8990                        number_opaque=1;
8991                      }
8992
8993                    for (i=0; i< (ssize_t) number_opaque; i++)
8994                      {
8995                        if (Magick_png_color_equal(image,q,opaque+i))
8996                          break;
8997                      }
8998
8999                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
9000                      {
9001                        number_opaque++;
9002                        GetPixelInfoPixel(image, q, opaque+i);
9003                        opaque[i].alpha=OpaqueAlpha;
9004                      }
9005                  }
9006              }
9007            else if (GetPixelAlpha(image,q) == TransparentAlpha)
9008              {
9009                if (number_transparent < 259)
9010                  {
9011                    if (number_transparent == 0)
9012                      {
9013                        GetPixelInfoPixel(image, q, transparent);
9014                        ping_trans_color.red=(unsigned short)
9015                          GetPixelRed(image,q);
9016                        ping_trans_color.green=(unsigned short)
9017                          GetPixelGreen(image,q);
9018                        ping_trans_color.blue=(unsigned short)
9019                          GetPixelBlue(image,q);
9020                        ping_trans_color.gray=(unsigned short)
9021                          GetPixelGray(image,q);
9022                        number_transparent = 1;
9023                      }
9024
9025                    for (i=0; i< (ssize_t) number_transparent; i++)
9026                      {
9027                        if (Magick_png_color_equal(image,q,transparent+i))
9028                          break;
9029                      }
9030
9031                    if (i ==  (ssize_t) number_transparent &&
9032                        number_transparent < 259)
9033                      {
9034                        number_transparent++;
9035                        GetPixelInfoPixel(image,q,transparent+i);
9036                      }
9037                  }
9038              }
9039            else
9040              {
9041                if (number_semitransparent < 259)
9042                  {
9043                    if (number_semitransparent == 0)
9044                      {
9045                        GetPixelInfoPixel(image,q,semitransparent);
9046                        number_semitransparent = 1;
9047                      }
9048
9049                    for (i=0; i< (ssize_t) number_semitransparent; i++)
9050                      {
9051                        if (Magick_png_color_equal(image,q,semitransparent+i)
9052                            && GetPixelAlpha(image,q) ==
9053                            semitransparent[i].alpha)
9054                          break;
9055                      }
9056
9057                    if (i ==  (ssize_t) number_semitransparent &&
9058                        number_semitransparent < 259)
9059                      {
9060                        number_semitransparent++;
9061                        GetPixelInfoPixel(image, q, semitransparent+i);
9062                      }
9063                  }
9064              }
9065            q+=GetPixelChannels(image);
9066         }
9067      }
9068
9069      if (mng_info->write_png8 == MagickFalse &&
9070          ping_exclude_bKGD == MagickFalse)
9071        {
9072          /* Add the background color to the palette, if it
9073           * isn't already there.
9074           */
9075           if (logging != MagickFalse)
9076             {
9077               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9078                   "      Check colormap for background (%d,%d,%d)",
9079                   (int) image->background_color.red,
9080                   (int) image->background_color.green,
9081                   (int) image->background_color.blue);
9082             }
9083           for (i=0; i<number_opaque; i++)
9084           {
9085              if (opaque[i].red == image->background_color.red &&
9086                  opaque[i].green == image->background_color.green &&
9087                  opaque[i].blue == image->background_color.blue)
9088                break;
9089           }
9090           if (number_opaque < 259 && i == number_opaque)
9091             {
9092                opaque[i] = image->background_color;
9093                ping_background.index = i;
9094                number_opaque++;
9095                if (logging != MagickFalse)
9096                  {
9097                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9098                        "      background_color index is %d",(int) i);
9099                  }
9100
9101             }
9102           else if (logging != MagickFalse)
9103               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9104                   "      No room in the colormap to add background color");
9105        }
9106
9107      image_colors=number_opaque+number_transparent+number_semitransparent;
9108
9109      if (logging != MagickFalse)
9110        {
9111          if (image_colors > 256)
9112             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9113                   "      image has more than 256 colors");
9114
9115          else
9116             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9117                   "      image has %d colors",image_colors);
9118        }
9119
9120      if (ping_preserve_colormap != MagickFalse)
9121        break;
9122
9123      if (mng_info->write_png_colortype != 7) /* We won't need this info */
9124        {
9125          ping_have_color=MagickFalse;
9126          ping_have_non_bw=MagickFalse;
9127
9128          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
9129          {
9130            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9131               "incompatible colorspace");
9132            ping_have_color=MagickTrue;
9133            ping_have_non_bw=MagickTrue;
9134          }
9135
9136          if(image_colors > 256)
9137            {
9138              for (y=0; y < (ssize_t) image->rows; y++)
9139              {
9140                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9141
9142                if (q == (Quantum *) NULL)
9143                  break;
9144
9145                s=q;
9146                for (x=0; x < (ssize_t) image->columns; x++)
9147                {
9148                  if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
9149                      GetPixelRed(image,s) != GetPixelBlue(image,s))
9150                    {
9151                       ping_have_color=MagickTrue;
9152                       ping_have_non_bw=MagickTrue;
9153                       break;
9154                    }
9155                  s+=GetPixelChannels(image);
9156                }
9157
9158                if (ping_have_color != MagickFalse)
9159                  break;
9160
9161                /* Worst case is black-and-white; we are looking at every
9162                 * pixel twice.
9163                 */
9164
9165                if (ping_have_non_bw == MagickFalse)
9166                  {
9167                    s=q;
9168                    for (x=0; x < (ssize_t) image->columns; x++)
9169                    {
9170                      if (GetPixelRed(image,s) != 0 &&
9171                          GetPixelRed(image,s) != QuantumRange)
9172                        {
9173                          ping_have_non_bw=MagickTrue;
9174                          break;
9175                        }
9176                      s+=GetPixelChannels(image);
9177                    }
9178                }
9179              }
9180            }
9181        }
9182
9183      if (image_colors < 257)
9184        {
9185          PixelInfo
9186            colormap[260];
9187
9188          /*
9189           * Initialize image colormap.
9190           */
9191
9192          if (logging != MagickFalse)
9193             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9194                   "      Sort the new colormap");
9195
9196         /* Sort palette, transparent first */;
9197
9198          n = 0;
9199
9200          for (i=0; i<number_transparent; i++)
9201             colormap[n++] = transparent[i];
9202
9203          for (i=0; i<number_semitransparent; i++)
9204             colormap[n++] = semitransparent[i];
9205
9206          for (i=0; i<number_opaque; i++)
9207             colormap[n++] = opaque[i];
9208
9209          ping_background.index +=
9210            (number_transparent + number_semitransparent);
9211
9212          /* image_colors < 257; search the colormap instead of the pixels
9213           * to get ping_have_color and ping_have_non_bw
9214           */
9215          for (i=0; i<n; i++)
9216          {
9217            if (ping_have_color == MagickFalse)
9218              {
9219                 if (colormap[i].red != colormap[i].green ||
9220                     colormap[i].red != colormap[i].blue)
9221                   {
9222                      ping_have_color=MagickTrue;
9223                      ping_have_non_bw=MagickTrue;
9224                      break;
9225                   }
9226               }
9227
9228            if (ping_have_non_bw == MagickFalse)
9229              {
9230                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
9231                    ping_have_non_bw=MagickTrue;
9232              }
9233           }
9234
9235         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
9236             (number_transparent == 0 && number_semitransparent == 0)) &&
9237             (((mng_info->write_png_colortype-1) ==
9238             PNG_COLOR_TYPE_PALETTE) ||
9239             (mng_info->write_png_colortype == 0)))
9240           {
9241             if (logging != MagickFalse)
9242               {
9243                 if (n !=  (ssize_t) image_colors)
9244                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9245                    "   image_colors (%d) and n (%d)  don't match",
9246                    image_colors, n);
9247
9248                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9249                    "      AcquireImageColormap");
9250               }
9251
9252             image->colors = image_colors;
9253
9254             if (AcquireImageColormap(image,image_colors,exception) ==
9255                 MagickFalse)
9256                ThrowWriterException(ResourceLimitError,
9257                    "MemoryAllocationFailed");
9258
9259             for (i=0; i< (ssize_t) image_colors; i++)
9260                image->colormap[i] = colormap[i];
9261
9262             if (logging != MagickFalse)
9263               {
9264                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9265                       "      image->colors=%d (%d)",
9266                       (int) image->colors, image_colors);
9267
9268                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9269                       "      Update the pixel indexes");
9270               }
9271
9272             /* Sync the pixel indices with the new colormap */
9273
9274             for (y=0; y < (ssize_t) image->rows; y++)
9275             {
9276               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9277
9278               if (q == (Quantum *) NULL)
9279                 break;
9280
9281               for (x=0; x < (ssize_t) image->columns; x++)
9282               {
9283                 for (i=0; i< (ssize_t) image_colors; i++)
9284                 {
9285                   if ((image->alpha_trait == UndefinedPixelTrait ||
9286                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
9287                       image->colormap[i].red == GetPixelRed(image,q) &&
9288                       image->colormap[i].green == GetPixelGreen(image,q) &&
9289                       image->colormap[i].blue == GetPixelBlue(image,q))
9290                   {
9291                     SetPixelIndex(image,i,q);
9292                     break;
9293                   }
9294                 }
9295                 q+=GetPixelChannels(image);
9296               }
9297
9298               if (SyncAuthenticPixels(image,exception) == MagickFalse)
9299                  break;
9300             }
9301           }
9302        }
9303
9304      if (logging != MagickFalse)
9305        {
9306          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9307             "      image->colors=%d", (int) image->colors);
9308
9309          if (image->colormap != NULL)
9310            {
9311              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9312                  "       i     (red,green,blue,alpha)");
9313
9314              for (i=0; i < (ssize_t) image->colors; i++)
9315              {
9316                if (i < 300 || i >= (ssize_t) image->colors - 10)
9317                  {
9318                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9319                        "       %d     (%d,%d,%d,%d)",
9320                         (int) i,
9321                         (int) image->colormap[i].red,
9322                         (int) image->colormap[i].green,
9323                         (int) image->colormap[i].blue,
9324                         (int) image->colormap[i].alpha);
9325                  }
9326              }
9327            }
9328
9329            if (number_transparent < 257)
9330              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9331                    "      number_transparent     = %d",
9332                    number_transparent);
9333            else
9334
9335              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9336                    "      number_transparent     > 256");
9337
9338            if (number_opaque < 257)
9339              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9340                    "      number_opaque          = %d",
9341                    number_opaque);
9342
9343            else
9344              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9345                    "      number_opaque          > 256");
9346
9347            if (number_semitransparent < 257)
9348              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9349                    "      number_semitransparent = %d",
9350                    number_semitransparent);
9351
9352            else
9353              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9354                    "      number_semitransparent > 256");
9355
9356            if (ping_have_non_bw == MagickFalse)
9357               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9358                     "      All pixels and the background are black or white");
9359
9360            else if (ping_have_color == MagickFalse)
9361               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9362                     "      All pixels and the background are gray");
9363
9364            else
9365               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9366                     "      At least one pixel or the background is non-gray");
9367
9368            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9369                "    Exit BUILD_PALETTE:");
9370        }
9371
9372    if (mng_info->write_png8 == MagickFalse)
9373       break;
9374
9375    /* Make any reductions necessary for the PNG8 format */
9376     if (image_colors <= 256 &&
9377         image_colors != 0 && image->colormap != NULL &&
9378         number_semitransparent == 0 &&
9379         number_transparent <= 1)
9380       break;
9381
9382     /* PNG8 can't have semitransparent colors so we threshold the
9383      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9384      * transparent color so if more than one is transparent we merge
9385      * them into image->background_color.
9386      */
9387     if (number_semitransparent != 0 || number_transparent > 1)
9388       {
9389         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9390             "    Thresholding the alpha channel to binary");
9391
9392         for (y=0; y < (ssize_t) image->rows; y++)
9393         {
9394           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9395
9396           if (r == (Quantum *) NULL)
9397             break;
9398
9399           for (x=0; x < (ssize_t) image->columns; x++)
9400           {
9401               if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9402                 {
9403                   SetPixelViaPixelInfo(image,&image->background_color,r);
9404                   SetPixelAlpha(image,TransparentAlpha,r);
9405                 }
9406               else
9407                   SetPixelAlpha(image,OpaqueAlpha,r);
9408               r+=GetPixelChannels(image);
9409           }
9410
9411           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9412              break;
9413
9414           if (image_colors != 0 && image_colors <= 256 &&
9415              image->colormap != NULL)
9416             for (i=0; i<image_colors; i++)
9417                 image->colormap[i].alpha =
9418                     (image->colormap[i].alpha > TransparentAlpha/2 ?
9419                     TransparentAlpha : OpaqueAlpha);
9420         }
9421       continue;
9422     }
9423
9424     /* PNG8 can't have more than 256 colors so we quantize the pixels and
9425      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
9426      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9427      * colors or less.
9428      */
9429     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9430       {
9431         if (logging != MagickFalse)
9432            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9433                "    Quantizing the background color to 4-4-4");
9434
9435         tried_444 = MagickTrue;
9436
9437         LBR04PacketRGB(image->background_color);
9438
9439         if (logging != MagickFalse)
9440           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9441               "    Quantizing the pixel colors to 4-4-4");
9442
9443         if (image->colormap == NULL)
9444         {
9445           for (y=0; y < (ssize_t) image->rows; y++)
9446           {
9447             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9448
9449             if (r == (Quantum *) NULL)
9450               break;
9451
9452             for (x=0; x < (ssize_t) image->columns; x++)
9453             {
9454               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9455                   LBR04PixelRGB(r);
9456               r+=GetPixelChannels(image);
9457             }
9458
9459             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9460                break;
9461           }
9462         }
9463
9464         else /* Should not reach this; colormap already exists and
9465                 must be <= 256 */
9466         {
9467           if (logging != MagickFalse)
9468               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9469               "    Quantizing the colormap to 4-4-4");
9470
9471           for (i=0; i<image_colors; i++)
9472           {
9473             LBR04PacketRGB(image->colormap[i]);
9474           }
9475         }
9476         continue;
9477       }
9478
9479     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9480       {
9481         if (logging != MagickFalse)
9482            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9483                "    Quantizing the background color to 3-3-3");
9484
9485         tried_333 = MagickTrue;
9486
9487         LBR03PacketRGB(image->background_color);
9488
9489         if (logging != MagickFalse)
9490           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9491               "    Quantizing the pixel colors to 3-3-3-1");
9492
9493         if (image->colormap == NULL)
9494         {
9495           for (y=0; y < (ssize_t) image->rows; y++)
9496           {
9497             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9498
9499             if (r == (Quantum *) NULL)
9500               break;
9501
9502             for (x=0; x < (ssize_t) image->columns; x++)
9503             {
9504               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9505                   LBR03RGB(r);
9506               r+=GetPixelChannels(image);
9507             }
9508
9509             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9510                break;
9511           }
9512         }
9513
9514         else /* Should not reach this; colormap already exists and
9515                 must be <= 256 */
9516         {
9517           if (logging != MagickFalse)
9518               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9519               "    Quantizing the colormap to 3-3-3-1");
9520           for (i=0; i<image_colors; i++)
9521           {
9522               LBR03PacketRGB(image->colormap[i]);
9523           }
9524         }
9525         continue;
9526       }
9527
9528     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9529       {
9530         if (logging != MagickFalse)
9531            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9532                "    Quantizing the background color to 3-3-2");
9533
9534         tried_332 = MagickTrue;
9535
9536         /* Red and green were already done so we only quantize the blue
9537          * channel
9538          */
9539
9540         LBR02PacketBlue(image->background_color);
9541
9542         if (logging != MagickFalse)
9543           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9544               "    Quantizing the pixel colors to 3-3-2-1");
9545
9546         if (image->colormap == NULL)
9547         {
9548           for (y=0; y < (ssize_t) image->rows; y++)
9549           {
9550             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9551
9552             if (r == (Quantum *) NULL)
9553               break;
9554
9555             for (x=0; x < (ssize_t) image->columns; x++)
9556             {
9557               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9558                   LBR02PixelBlue(r);
9559               r+=GetPixelChannels(image);
9560             }
9561
9562             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9563                break;
9564           }
9565         }
9566
9567         else /* Should not reach this; colormap already exists and
9568                 must be <= 256 */
9569         {
9570           if (logging != MagickFalse)
9571               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9572               "    Quantizing the colormap to 3-3-2-1");
9573           for (i=0; i<image_colors; i++)
9574           {
9575               LBR02PacketBlue(image->colormap[i]);
9576           }
9577       }
9578       continue;
9579     }
9580
9581     if (image_colors == 0 || image_colors > 256)
9582     {
9583       /* Take care of special case with 256 opaque colors + 1 transparent
9584        * color.  We don't need to quantize to 2-3-2-1; we only need to
9585        * eliminate one color, so we'll merge the two darkest red
9586        * colors (0x49, 0, 0) -> (0x24, 0, 0).
9587        */
9588       if (logging != MagickFalse)
9589         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9590             "    Merging two dark red background colors to 3-3-2-1");
9591
9592       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9593           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9594           ScaleQuantumToChar(image->background_color.blue) == 0x00)
9595       {
9596          image->background_color.red=ScaleCharToQuantum(0x24);
9597       }
9598
9599       if (logging != MagickFalse)
9600         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9601             "    Merging two dark red pixel colors to 3-3-2-1");
9602
9603       if (image->colormap == NULL)
9604       {
9605         for (y=0; y < (ssize_t) image->rows; y++)
9606         {
9607           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9608
9609           if (r == (Quantum *) NULL)
9610             break;
9611
9612           for (x=0; x < (ssize_t) image->columns; x++)
9613           {
9614             if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9615                 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9616                 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9617                 GetPixelAlpha(image,r) == OpaqueAlpha)
9618               {
9619                 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9620               }
9621             r+=GetPixelChannels(image);
9622           }
9623
9624           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9625              break;
9626
9627         }
9628       }
9629
9630       else
9631       {
9632          for (i=0; i<image_colors; i++)
9633          {
9634             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9635                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9636                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9637             {
9638                image->colormap[i].red=ScaleCharToQuantum(0x24);
9639             }
9640          }
9641       }
9642     }
9643   }
9644   }
9645   /* END OF BUILD_PALETTE */
9646
9647   /* If we are excluding the tRNS chunk and there is transparency,
9648    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9649    * PNG.
9650    */
9651   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9652      (number_transparent != 0 || number_semitransparent != 0))
9653     {
9654       unsigned int colortype=mng_info->write_png_colortype;
9655
9656       if (ping_have_color == MagickFalse)
9657         mng_info->write_png_colortype = 5;
9658
9659       else
9660         mng_info->write_png_colortype = 7;
9661
9662       if (colortype != 0 &&
9663          mng_info->write_png_colortype != colortype)
9664         ping_need_colortype_warning=MagickTrue;
9665
9666     }
9667
9668   /* See if cheap transparency is possible.  It is only possible
9669    * when there is a single transparent color, no semitransparent
9670    * color, and no opaque color that has the same RGB components
9671    * as the transparent color.  We only need this information if
9672    * we are writing a PNG with colortype 0 or 2, and we have not
9673    * excluded the tRNS chunk.
9674    */
9675   if (number_transparent == 1 &&
9676       mng_info->write_png_colortype < 4)
9677     {
9678        ping_have_cheap_transparency = MagickTrue;
9679
9680        if (number_semitransparent != 0)
9681          ping_have_cheap_transparency = MagickFalse;
9682
9683        else if (image_colors == 0 || image_colors > 256 ||
9684            image->colormap == NULL)
9685          {
9686            register const Quantum
9687              *q;
9688
9689            for (y=0; y < (ssize_t) image->rows; y++)
9690            {
9691              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9692
9693              if (q == (Quantum *) NULL)
9694                break;
9695
9696              for (x=0; x < (ssize_t) image->columns; x++)
9697              {
9698                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9699                      (unsigned short) GetPixelRed(image,q) ==
9700                                      ping_trans_color.red &&
9701                      (unsigned short) GetPixelGreen(image,q) ==
9702                                      ping_trans_color.green &&
9703                      (unsigned short) GetPixelBlue(image,q) ==
9704                                      ping_trans_color.blue)
9705                    {
9706                      ping_have_cheap_transparency = MagickFalse;
9707                      break;
9708                    }
9709
9710                  q+=GetPixelChannels(image);
9711              }
9712
9713              if (ping_have_cheap_transparency == MagickFalse)
9714                 break;
9715            }
9716          }
9717        else
9718          {
9719             /* Assuming that image->colormap[0] is the one transparent color
9720              * and that all others are opaque.
9721              */
9722             if (image_colors > 1)
9723               for (i=1; i<image_colors; i++)
9724                 if (image->colormap[i].red == image->colormap[0].red &&
9725                     image->colormap[i].green == image->colormap[0].green &&
9726                     image->colormap[i].blue == image->colormap[0].blue)
9727                   {
9728                      ping_have_cheap_transparency = MagickFalse;
9729                      break;
9730                   }
9731          }
9732
9733        if (logging != MagickFalse)
9734          {
9735            if (ping_have_cheap_transparency == MagickFalse)
9736              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9737                  "   Cheap transparency is not possible.");
9738
9739            else
9740              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9741                  "   Cheap transparency is possible.");
9742          }
9743      }
9744   else
9745     ping_have_cheap_transparency = MagickFalse;
9746
9747   image_depth=image->depth;
9748
9749   quantum_info = (QuantumInfo *) NULL;
9750   number_colors=0;
9751   image_colors=(int) image->colors;
9752   image_matte=image->alpha_trait !=
9753         UndefinedPixelTrait ? MagickTrue : MagickFalse;
9754
9755   if (mng_info->write_png_colortype < 5)
9756     mng_info->IsPalette=image->storage_class == PseudoClass &&
9757       image_colors <= 256 && image->colormap != NULL;
9758   else
9759     mng_info->IsPalette = MagickFalse;
9760
9761   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9762      (image->colors == 0 || image->colormap == NULL))
9763     {
9764       image_info=DestroyImageInfo(image_info);
9765       image=DestroyImage(image);
9766       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9767           "Cannot write PNG8 or color-type 3; colormap is NULL",
9768           "`%s'",IMimage->filename);
9769       return(MagickFalse);
9770     }
9771
9772   /*
9773     Allocate the PNG structures
9774   */
9775 #ifdef PNG_USER_MEM_SUPPORTED
9776  error_info.image=image;
9777  error_info.exception=exception;
9778   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9779     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9780     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9781
9782 #else
9783   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9784     MagickPNGErrorHandler,MagickPNGWarningHandler);
9785
9786 #endif
9787   if (ping == (png_struct *) NULL)
9788     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9789
9790   ping_info=png_create_info_struct(ping);
9791
9792   if (ping_info == (png_info *) NULL)
9793     {
9794       png_destroy_write_struct(&ping,(png_info **) NULL);
9795       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9796     }
9797
9798   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9799   pixel_info=(MemoryInfo *) NULL;
9800
9801   if (setjmp(png_jmpbuf(ping)))
9802     {
9803       /*
9804         PNG write failed.
9805       */
9806 #ifdef PNG_DEBUG
9807      if (image_info->verbose)
9808         (void) printf("PNG write has failed.\n");
9809 #endif
9810       png_destroy_write_struct(&ping,&ping_info);
9811 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9812       UnlockSemaphoreInfo(ping_semaphore);
9813 #endif
9814
9815       if (pixel_info != (MemoryInfo *) NULL)
9816         pixel_info=RelinquishVirtualMemory(pixel_info);
9817
9818       if (quantum_info != (QuantumInfo *) NULL)
9819         quantum_info=DestroyQuantumInfo(quantum_info);
9820
9821       if (ping_have_blob != MagickFalse)
9822           (void) CloseBlob(image);
9823       image_info=DestroyImageInfo(image_info);
9824       image=DestroyImage(image);
9825       return(MagickFalse);
9826     }
9827
9828   /* {  For navigation to end of SETJMP-protected block.  Within this
9829    *    block, use png_error() instead of Throwing an Exception, to ensure
9830    *    that libpng is able to clean up, and that the semaphore is unlocked.
9831    */
9832
9833 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9834   LockSemaphoreInfo(ping_semaphore);
9835 #endif
9836
9837 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9838   /* Allow benign errors */
9839   png_set_benign_errors(ping, 1);
9840 #endif
9841
9842 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9843   /* Reject images with too many rows or columns */
9844   png_set_user_limits(ping,
9845     (png_uint_32) MagickMin(0x7fffffffL,
9846         GetMagickResourceLimit(WidthResource)),
9847     (png_uint_32) MagickMin(0x7fffffffL,
9848         GetMagickResourceLimit(HeightResource)));
9849 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9850
9851   /*
9852     Prepare PNG for writing.
9853   */
9854
9855 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9856   if (mng_info->write_mng)
9857   {
9858      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9859 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9860      /* Disable new libpng-1.5.10 feature when writing a MNG because
9861       * zero-length PLTE is OK
9862       */
9863      png_set_check_for_invalid_index (ping, 0);
9864 # endif
9865   }
9866
9867 #else
9868 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9869   if (mng_info->write_mng)
9870      png_permit_empty_plte(ping,MagickTrue);
9871
9872 # endif
9873 #endif
9874
9875   x=0;
9876
9877   ping_width=(png_uint_32) image->columns;
9878   ping_height=(png_uint_32) image->rows;
9879
9880   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9881      image_depth=8;
9882
9883   if (mng_info->write_png48 || mng_info->write_png64)
9884      image_depth=16;
9885
9886   if (mng_info->write_png_depth != 0)
9887      image_depth=mng_info->write_png_depth;
9888
9889   /* Adjust requested depth to next higher valid depth if necessary */
9890   if (image_depth > 8)
9891      image_depth=16;
9892
9893   if ((image_depth > 4) && (image_depth < 8))
9894      image_depth=8;
9895
9896   if (image_depth == 3)
9897      image_depth=4;
9898
9899   if (logging != MagickFalse)
9900     {
9901      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9902         "    width=%.20g",(double) ping_width);
9903      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9904         "    height=%.20g",(double) ping_height);
9905      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9906         "    image_matte=%.20g",(double) image->alpha_trait);
9907      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9908         "    image->depth=%.20g",(double) image->depth);
9909      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9910         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
9911     }
9912
9913   save_image_depth=image_depth;
9914   ping_bit_depth=(png_byte) save_image_depth;
9915
9916
9917 #if defined(PNG_pHYs_SUPPORTED)
9918   if (ping_exclude_pHYs == MagickFalse)
9919   {
9920   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9921       (!mng_info->write_mng || !mng_info->equal_physs))
9922     {
9923       if (logging != MagickFalse)
9924         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9925             "    Setting up pHYs chunk");
9926
9927       if (image->units == PixelsPerInchResolution)
9928         {
9929           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9930           ping_pHYs_x_resolution=
9931              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9932           ping_pHYs_y_resolution=
9933              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9934         }
9935
9936       else if (image->units == PixelsPerCentimeterResolution)
9937         {
9938           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9939           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9940           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9941         }
9942
9943       else
9944         {
9945           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9946           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9947           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9948         }
9949
9950       if (logging != MagickFalse)
9951         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9952           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9953           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9954           (int) ping_pHYs_unit_type);
9955        ping_have_pHYs = MagickTrue;
9956     }
9957   }
9958 #endif
9959
9960   if (ping_exclude_bKGD == MagickFalse)
9961   {
9962   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9963     {
9964        unsigned int
9965          mask;
9966
9967        mask=0xffff;
9968        if (ping_bit_depth == 8)
9969           mask=0x00ff;
9970
9971        if (ping_bit_depth == 4)
9972           mask=0x000f;
9973
9974        if (ping_bit_depth == 2)
9975           mask=0x0003;
9976
9977        if (ping_bit_depth == 1)
9978           mask=0x0001;
9979
9980        ping_background.red=(png_uint_16)
9981          (ScaleQuantumToShort(image->background_color.red) & mask);
9982
9983        ping_background.green=(png_uint_16)
9984          (ScaleQuantumToShort(image->background_color.green) & mask);
9985
9986        ping_background.blue=(png_uint_16)
9987          (ScaleQuantumToShort(image->background_color.blue) & mask);
9988
9989        ping_background.gray=(png_uint_16) ping_background.green;
9990     }
9991
9992   if (logging != MagickFalse)
9993     {
9994       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9995           "    Setting up bKGD chunk (1)");
9996       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9997           "      background_color index is %d",
9998           (int) ping_background.index);
9999
10000       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10001           "    ping_bit_depth=%d",ping_bit_depth);
10002     }
10003
10004   ping_have_bKGD = MagickTrue;
10005   }
10006
10007   /*
10008     Select the color type.
10009   */
10010   matte=image_matte;
10011   old_bit_depth=0;
10012
10013   if (mng_info->IsPalette && mng_info->write_png8)
10014     {
10015       /* To do: make this a function cause it's used twice, except
10016          for reducing the sample depth from 8. */
10017
10018       number_colors=image_colors;
10019
10020       ping_have_tRNS=MagickFalse;
10021
10022       /*
10023         Set image palette.
10024       */
10025       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10026
10027       if (logging != MagickFalse)
10028         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10029             "  Setting up PLTE chunk with %d colors (%d)",
10030             number_colors, image_colors);
10031
10032       for (i=0; i < (ssize_t) number_colors; i++)
10033       {
10034         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10035         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10036         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10037         if (logging != MagickFalse)
10038           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10039 #if MAGICKCORE_QUANTUM_DEPTH == 8
10040             "    %3ld (%3d,%3d,%3d)",
10041 #else
10042             "    %5ld (%5d,%5d,%5d)",
10043 #endif
10044             (long) i,palette[i].red,palette[i].green,palette[i].blue);
10045
10046       }
10047
10048       ping_have_PLTE=MagickTrue;
10049       image_depth=ping_bit_depth;
10050       ping_num_trans=0;
10051
10052       if (matte != MagickFalse)
10053       {
10054           /*
10055             Identify which colormap entry is transparent.
10056           */
10057           assert(number_colors <= 256);
10058           assert(image->colormap != NULL);
10059
10060           for (i=0; i < (ssize_t) number_transparent; i++)
10061              ping_trans_alpha[i]=0;
10062
10063
10064           ping_num_trans=(unsigned short) (number_transparent +
10065              number_semitransparent);
10066
10067           if (ping_num_trans == 0)
10068              ping_have_tRNS=MagickFalse;
10069
10070           else
10071              ping_have_tRNS=MagickTrue;
10072       }
10073
10074       if (ping_exclude_bKGD == MagickFalse)
10075       {
10076        /*
10077         * Identify which colormap entry is the background color.
10078         */
10079
10080         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
10081           if (IsPNGColorEqual(ping_background,image->colormap[i]))
10082             break;
10083
10084         ping_background.index=(png_byte) i;
10085
10086         if (logging != MagickFalse)
10087           {
10088             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10089                  "      background_color index is %d",
10090                  (int) ping_background.index);
10091           }
10092       }
10093     } /* end of write_png8 */
10094
10095   else if (mng_info->write_png_colortype == 1)
10096     {
10097       image_matte=MagickFalse;
10098       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10099     }
10100
10101   else if (mng_info->write_png24 || mng_info->write_png48 ||
10102       mng_info->write_png_colortype == 3)
10103     {
10104       image_matte=MagickFalse;
10105       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10106     }
10107
10108   else if (mng_info->write_png32 || mng_info->write_png64 ||
10109       mng_info->write_png_colortype == 7)
10110     {
10111       image_matte=MagickTrue;
10112       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10113     }
10114
10115   else /* mng_info->write_pngNN not specified */
10116     {
10117       image_depth=ping_bit_depth;
10118
10119       if (mng_info->write_png_colortype != 0)
10120         {
10121           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
10122
10123           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10124               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10125             image_matte=MagickTrue;
10126
10127           else
10128             image_matte=MagickFalse;
10129
10130           if (logging != MagickFalse)
10131              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10132              "   PNG colortype %d was specified:",(int) ping_color_type);
10133         }
10134
10135       else /* write_png_colortype not specified */
10136         {
10137           if (logging != MagickFalse)
10138              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10139              "  Selecting PNG colortype:");
10140
10141           ping_color_type=(png_byte) ((matte != MagickFalse)?
10142             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
10143
10144           if (image_info->type == TrueColorType)
10145             {
10146               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10147               image_matte=MagickFalse;
10148             }
10149
10150           if (image_info->type == TrueColorAlphaType)
10151             {
10152               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10153               image_matte=MagickTrue;
10154             }
10155
10156           if (image_info->type == PaletteType ||
10157               image_info->type == PaletteAlphaType)
10158             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10159
10160           if (mng_info->write_png_colortype == 0 &&
10161              image_info->type == UndefinedType)
10162             {
10163               if (ping_have_color == MagickFalse)
10164                 {
10165                   if (image_matte == MagickFalse)
10166                     {
10167                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10168                       image_matte=MagickFalse;
10169                     }
10170
10171                   else
10172                     {
10173                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
10174                       image_matte=MagickTrue;
10175                     }
10176                 }
10177               else
10178                 {
10179                   if (image_matte == MagickFalse)
10180                     {
10181                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10182                       image_matte=MagickFalse;
10183                     }
10184
10185                   else
10186                     {
10187                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
10188                       image_matte=MagickTrue;
10189                     }
10190                  }
10191             }
10192
10193         }
10194
10195       if (logging != MagickFalse)
10196          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10197          "    Selected PNG colortype=%d",ping_color_type);
10198
10199       if (ping_bit_depth < 8)
10200         {
10201           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10202               ping_color_type == PNG_COLOR_TYPE_RGB ||
10203               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10204             ping_bit_depth=8;
10205         }
10206
10207       old_bit_depth=ping_bit_depth;
10208
10209       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10210         {
10211           if (image->alpha_trait == UndefinedPixelTrait &&
10212                ping_have_non_bw == MagickFalse)
10213              ping_bit_depth=1;
10214         }
10215
10216       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
10217         {
10218            size_t one = 1;
10219            ping_bit_depth=1;
10220
10221            if (image->colors == 0)
10222            {
10223               /* DO SOMETHING */
10224                 png_error(ping,"image has 0 colors");
10225            }
10226
10227            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
10228              ping_bit_depth <<= 1;
10229         }
10230
10231       if (logging != MagickFalse)
10232          {
10233            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10234             "    Number of colors: %.20g",(double) image_colors);
10235
10236            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10237             "    Tentative PNG bit depth: %d",ping_bit_depth);
10238          }
10239
10240       if (ping_bit_depth < (int) mng_info->write_png_depth)
10241          ping_bit_depth = mng_info->write_png_depth;
10242     }
10243
10244   image_depth=ping_bit_depth;
10245
10246   if (logging != MagickFalse)
10247     {
10248       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10249         "    Tentative PNG color type: %s (%.20g)",
10250         PngColorTypeToString(ping_color_type),
10251         (double) ping_color_type);
10252
10253       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10254         "    image_info->type: %.20g",(double) image_info->type);
10255
10256       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10257         "    image_depth: %.20g",(double) image_depth);
10258
10259       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10260
10261         "    image->depth: %.20g",(double) image->depth);
10262
10263       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10264         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
10265     }
10266
10267   if (matte != MagickFalse)
10268     {
10269       if (mng_info->IsPalette)
10270         {
10271           if (mng_info->write_png_colortype == 0)
10272             {
10273               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10274
10275               if (ping_have_color != MagickFalse)
10276                  ping_color_type=PNG_COLOR_TYPE_RGBA;
10277             }
10278
10279           /*
10280            * Determine if there is any transparent color.
10281           */
10282           if (number_transparent + number_semitransparent == 0)
10283             {
10284               /*
10285                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
10286               */
10287
10288               image_matte=MagickFalse;
10289
10290               if (mng_info->write_png_colortype == 0)
10291                 ping_color_type&=0x03;
10292             }
10293
10294           else
10295             {
10296               unsigned int
10297                 mask;
10298
10299               mask=0xffff;
10300
10301               if (ping_bit_depth == 8)
10302                  mask=0x00ff;
10303
10304               if (ping_bit_depth == 4)
10305                  mask=0x000f;
10306
10307               if (ping_bit_depth == 2)
10308                  mask=0x0003;
10309
10310               if (ping_bit_depth == 1)
10311                  mask=0x0001;
10312
10313               ping_trans_color.red=(png_uint_16)
10314                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10315
10316               ping_trans_color.green=(png_uint_16)
10317                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10318
10319               ping_trans_color.blue=(png_uint_16)
10320                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10321
10322               ping_trans_color.gray=(png_uint_16)
10323                 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
10324                    image->colormap)) & mask);
10325
10326               ping_trans_color.index=(png_byte) 0;
10327
10328               ping_have_tRNS=MagickTrue;
10329             }
10330
10331           if (ping_have_tRNS != MagickFalse)
10332             {
10333               /*
10334                * Determine if there is one and only one transparent color
10335                * and if so if it is fully transparent.
10336                */
10337               if (ping_have_cheap_transparency == MagickFalse)
10338                 ping_have_tRNS=MagickFalse;
10339             }
10340
10341           if (ping_have_tRNS != MagickFalse)
10342             {
10343               if (mng_info->write_png_colortype == 0)
10344                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
10345
10346               if (image_depth == 8)
10347                 {
10348                   ping_trans_color.red&=0xff;
10349                   ping_trans_color.green&=0xff;
10350                   ping_trans_color.blue&=0xff;
10351                   ping_trans_color.gray&=0xff;
10352                 }
10353             }
10354         }
10355       else
10356         {
10357           if (image_depth == 8)
10358             {
10359               ping_trans_color.red&=0xff;
10360               ping_trans_color.green&=0xff;
10361               ping_trans_color.blue&=0xff;
10362               ping_trans_color.gray&=0xff;
10363             }
10364         }
10365     }
10366
10367     matte=image_matte;
10368
10369     if (ping_have_tRNS != MagickFalse)
10370       image_matte=MagickFalse;
10371
10372     if ((mng_info->IsPalette) &&
10373         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10374         ping_have_color == MagickFalse &&
10375         (image_matte == MagickFalse || image_depth >= 8))
10376       {
10377         size_t one=1;
10378
10379         if (image_matte != MagickFalse)
10380           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10381
10382         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10383           {
10384             ping_color_type=PNG_COLOR_TYPE_GRAY;
10385
10386             if (save_image_depth == 16 && image_depth == 8)
10387               {
10388                 if (logging != MagickFalse)
10389                   {
10390                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10391                         "  Scaling ping_trans_color (0)");
10392                   }
10393                     ping_trans_color.gray*=0x0101;
10394               }
10395           }
10396
10397         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10398           image_depth=MAGICKCORE_QUANTUM_DEPTH;
10399
10400         if ((image_colors == 0) ||
10401              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10402           image_colors=(int) (one << image_depth);
10403
10404         if (image_depth > 8)
10405           ping_bit_depth=16;
10406
10407         else
10408           {
10409             ping_bit_depth=8;
10410             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10411               {
10412                 if(!mng_info->write_png_depth)
10413                   {
10414                     ping_bit_depth=1;
10415
10416                     while ((int) (one << ping_bit_depth)
10417                         < (ssize_t) image_colors)
10418                       ping_bit_depth <<= 1;
10419                   }
10420               }
10421
10422             else if (ping_color_type ==
10423                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10424                 mng_info->IsPalette)
10425               {
10426               /* Check if grayscale is reducible */
10427
10428                 int
10429                   depth_4_ok=MagickTrue,
10430                   depth_2_ok=MagickTrue,
10431                   depth_1_ok=MagickTrue;
10432
10433                 for (i=0; i < (ssize_t) image_colors; i++)
10434                 {
10435                    unsigned char
10436                      intensity;
10437
10438                    intensity=ScaleQuantumToChar(image->colormap[i].red);
10439
10440                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10441                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10442                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10443                      depth_2_ok=depth_1_ok=MagickFalse;
10444                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10445                      depth_1_ok=MagickFalse;
10446                 }
10447
10448                 if (depth_1_ok && mng_info->write_png_depth <= 1)
10449                   ping_bit_depth=1;
10450
10451                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10452                   ping_bit_depth=2;
10453
10454                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10455                   ping_bit_depth=4;
10456               }
10457           }
10458
10459           image_depth=ping_bit_depth;
10460       }
10461
10462     else
10463
10464       if (mng_info->IsPalette)
10465       {
10466         number_colors=image_colors;
10467
10468         if (image_depth <= 8)
10469           {
10470             /*
10471               Set image palette.
10472             */
10473             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10474
10475             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10476               {
10477                 for (i=0; i < (ssize_t) number_colors; i++)
10478                 {
10479                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10480                   palette[i].green=
10481                     ScaleQuantumToChar(image->colormap[i].green);
10482                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10483                 }
10484
10485                 if (logging != MagickFalse)
10486                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10487                     "  Setting up PLTE chunk with %d colors",
10488                     number_colors);
10489
10490                 ping_have_PLTE=MagickTrue;
10491               }
10492
10493             /* color_type is PNG_COLOR_TYPE_PALETTE */
10494             if (mng_info->write_png_depth == 0)
10495               {
10496                 size_t
10497                   one;
10498
10499                 ping_bit_depth=1;
10500                 one=1;
10501
10502                 while ((one << ping_bit_depth) < (size_t) number_colors)
10503                   ping_bit_depth <<= 1;
10504               }
10505
10506             ping_num_trans=0;
10507
10508             if (matte != MagickFalse)
10509               {
10510                 /*
10511                  * Set up trans_colors array.
10512                  */
10513                 assert(number_colors <= 256);
10514
10515                 ping_num_trans=(unsigned short) (number_transparent +
10516                   number_semitransparent);
10517
10518                 if (ping_num_trans == 0)
10519                   ping_have_tRNS=MagickFalse;
10520
10521                 else
10522                   {
10523                     if (logging != MagickFalse)
10524                       {
10525                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10526                           "  Scaling ping_trans_color (1)");
10527                       }
10528                     ping_have_tRNS=MagickTrue;
10529
10530                     for (i=0; i < ping_num_trans; i++)
10531                     {
10532                        ping_trans_alpha[i]= (png_byte)
10533                          ScaleQuantumToChar(image->colormap[i].alpha);
10534                     }
10535                   }
10536               }
10537           }
10538       }
10539
10540     else
10541       {
10542
10543         if (image_depth < 8)
10544           image_depth=8;
10545
10546         if ((save_image_depth == 16) && (image_depth == 8))
10547           {
10548             if (logging != MagickFalse)
10549               {
10550                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10551                   "    Scaling ping_trans_color from (%d,%d,%d)",
10552                   (int) ping_trans_color.red,
10553                   (int) ping_trans_color.green,
10554                   (int) ping_trans_color.blue);
10555               }
10556
10557             ping_trans_color.red*=0x0101;
10558             ping_trans_color.green*=0x0101;
10559             ping_trans_color.blue*=0x0101;
10560             ping_trans_color.gray*=0x0101;
10561
10562             if (logging != MagickFalse)
10563               {
10564                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10565                   "    to (%d,%d,%d)",
10566                   (int) ping_trans_color.red,
10567                   (int) ping_trans_color.green,
10568                   (int) ping_trans_color.blue);
10569               }
10570           }
10571       }
10572
10573     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
10574          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
10575
10576     /*
10577       Adjust background and transparency samples in sub-8-bit grayscale files.
10578     */
10579     if (ping_bit_depth < 8 && ping_color_type ==
10580         PNG_COLOR_TYPE_GRAY)
10581       {
10582          png_uint_16
10583            maxval;
10584
10585          size_t
10586            one=1;
10587
10588          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10589
10590          if (ping_exclude_bKGD == MagickFalse)
10591          {
10592
10593          ping_background.gray=(png_uint_16) ((maxval/65535.)*
10594            (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10595            &image->background_color))) +.5)));
10596
10597          if (logging != MagickFalse)
10598            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10599              "  Setting up bKGD chunk (2)");
10600          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10601              "      background_color index is %d",
10602              (int) ping_background.index);
10603
10604          ping_have_bKGD = MagickTrue;
10605          }
10606
10607          if (logging != MagickFalse)
10608            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10609              "  Scaling ping_trans_color.gray from %d",
10610              (int)ping_trans_color.gray);
10611
10612          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10613            ping_trans_color.gray)+.5);
10614
10615          if (logging != MagickFalse)
10616            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10617              "      to %d", (int)ping_trans_color.gray);
10618       }
10619
10620   if (ping_exclude_bKGD == MagickFalse)
10621   {
10622     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10623       {
10624         /*
10625            Identify which colormap entry is the background color.
10626         */
10627
10628         number_colors=image_colors;
10629
10630         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10631           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10632             break;
10633
10634         ping_background.index=(png_byte) i;
10635
10636         if (logging != MagickFalse)
10637           {
10638             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10639               "  Setting up bKGD chunk with index=%d",(int) i);
10640           }
10641
10642         if (i < (ssize_t) number_colors)
10643           {
10644             ping_have_bKGD = MagickTrue;
10645
10646             if (logging != MagickFalse)
10647               {
10648                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10649                   "     background   =(%d,%d,%d)",
10650                         (int) ping_background.red,
10651                         (int) ping_background.green,
10652                         (int) ping_background.blue);
10653               }
10654           }
10655
10656         else  /* Can't happen */
10657           {
10658             if (logging != MagickFalse)
10659               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10660                   "      No room in PLTE to add bKGD color");
10661             ping_have_bKGD = MagickFalse;
10662           }
10663       }
10664   }
10665
10666   if (logging != MagickFalse)
10667     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10668       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10669       ping_color_type);
10670   /*
10671     Initialize compression level and filtering.
10672   */
10673   if (logging != MagickFalse)
10674     {
10675       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10676         "  Setting up deflate compression");
10677
10678       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10679         "    Compression buffer size: 32768");
10680     }
10681
10682   png_set_compression_buffer_size(ping,32768L);
10683
10684   if (logging != MagickFalse)
10685     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10686       "    Compression mem level: 9");
10687
10688   png_set_compression_mem_level(ping, 9);
10689
10690   /* Untangle the "-quality" setting:
10691
10692      Undefined is 0; the default is used.
10693      Default is 75
10694
10695      10's digit:
10696
10697         0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10698            zlib default compression level
10699
10700         1-9: the zlib compression level
10701
10702      1's digit:
10703
10704         0-4: the PNG filter method
10705
10706         5:   libpng adaptive filtering if compression level > 5
10707              libpng filter type "none" if compression level <= 5
10708                 or if image is grayscale or palette
10709
10710         6:   libpng adaptive filtering
10711
10712         7:   "LOCO" filtering (intrapixel differing) if writing
10713              a MNG, otherwise "none".  Did not work in IM-6.7.0-9
10714              and earlier because of a missing "else".
10715
10716         8:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10717              filtering. Unused prior to IM-6.7.0-10, was same as 6
10718
10719         9:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10720              Unused prior to IM-6.7.0-10, was same as 6
10721
10722     Note that using the -quality option, not all combinations of
10723     PNG filter type, zlib compression level, and zlib compression
10724     strategy are possible.  This will be addressed soon in a
10725     release that accomodates "-define png:compression-strategy", etc.
10726
10727    */
10728
10729   quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10730      image_info->quality;
10731
10732   if (quality <= 9)
10733     {
10734       if (mng_info->write_png_compression_strategy == 0)
10735         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10736     }
10737
10738   else if (mng_info->write_png_compression_level == 0)
10739     {
10740       int
10741         level;
10742
10743       level=(int) MagickMin((ssize_t) quality/10,9);
10744
10745       mng_info->write_png_compression_level = level+1;
10746     }
10747
10748   if (mng_info->write_png_compression_strategy == 0)
10749     {
10750         if ((quality %10) == 8 || (quality %10) == 9)
10751 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10752           mng_info->write_png_compression_strategy=Z_RLE+1;
10753 #else
10754           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10755 #endif
10756     }
10757
10758   if (mng_info->write_png_compression_filter == 0)
10759         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10760
10761   if (logging != MagickFalse)
10762     {
10763         if (mng_info->write_png_compression_level)
10764           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10765             "    Compression level:    %d",
10766             (int) mng_info->write_png_compression_level-1);
10767
10768         if (mng_info->write_png_compression_strategy)
10769           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10770             "    Compression strategy: %d",
10771             (int) mng_info->write_png_compression_strategy-1);
10772
10773         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10774           "  Setting up filtering");
10775
10776         if (mng_info->write_png_compression_filter == 6)
10777           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10778             "    Base filter method: ADAPTIVE");
10779         else if (mng_info->write_png_compression_filter == 0 ||
10780                  mng_info->write_png_compression_filter == 1)
10781           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10782             "    Base filter method: NONE");
10783         else
10784           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10785             "    Base filter method: %d",
10786             (int) mng_info->write_png_compression_filter-1);
10787     }
10788
10789   if (mng_info->write_png_compression_level != 0)
10790     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10791
10792   if (mng_info->write_png_compression_filter == 6)
10793     {
10794       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10795          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10796          (quality < 50))
10797         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10798       else
10799         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10800      }
10801   else if (mng_info->write_png_compression_filter == 7 ||
10802       mng_info->write_png_compression_filter == 10)
10803     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10804
10805   else if (mng_info->write_png_compression_filter == 8)
10806     {
10807 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10808       if (mng_info->write_mng)
10809       {
10810          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10811              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10812         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10813       }
10814 #endif
10815       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10816     }
10817
10818   else if (mng_info->write_png_compression_filter == 9)
10819     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10820
10821   else if (mng_info->write_png_compression_filter != 0)
10822     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10823        mng_info->write_png_compression_filter-1);
10824
10825   if (mng_info->write_png_compression_strategy != 0)
10826     png_set_compression_strategy(ping,
10827        mng_info->write_png_compression_strategy-1);
10828
10829   ping_interlace_method=image_info->interlace != NoInterlace;
10830
10831   if (mng_info->write_mng)
10832     png_set_sig_bytes(ping,8);
10833
10834   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10835
10836   if (mng_info->write_png_colortype != 0)
10837     {
10838      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10839        if (ping_have_color != MagickFalse)
10840          {
10841            ping_color_type = PNG_COLOR_TYPE_RGB;
10842
10843            if (ping_bit_depth < 8)
10844              ping_bit_depth=8;
10845          }
10846
10847      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10848        if (ping_have_color != MagickFalse)
10849          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10850     }
10851
10852   if (ping_need_colortype_warning != MagickFalse ||
10853      ((mng_info->write_png_depth &&
10854      (int) mng_info->write_png_depth != ping_bit_depth) ||
10855      (mng_info->write_png_colortype &&
10856      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10857       mng_info->write_png_colortype != 7 &&
10858       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10859     {
10860       if (logging != MagickFalse)
10861         {
10862           if (ping_need_colortype_warning != MagickFalse)
10863             {
10864               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10865                  "  Image has transparency but tRNS chunk was excluded");
10866             }
10867
10868           if (mng_info->write_png_depth)
10869             {
10870               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10871                   "  Defined png:bit-depth=%u, Computed depth=%u",
10872                   mng_info->write_png_depth,
10873                   ping_bit_depth);
10874             }
10875
10876           if (mng_info->write_png_colortype)
10877             {
10878               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10879                   "  Defined png:color-type=%u, Computed color type=%u",
10880                   mng_info->write_png_colortype-1,
10881                   ping_color_type);
10882             }
10883         }
10884
10885       png_warning(ping,
10886         "Cannot write image with defined png:bit-depth or png:color-type.");
10887     }
10888
10889   if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10890     {
10891       /* Add an opaque matte channel */
10892       image->alpha_trait = BlendPixelTrait;
10893       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10894
10895       if (logging != MagickFalse)
10896         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10897           "  Added an opaque matte channel");
10898     }
10899
10900   if (number_transparent != 0 || number_semitransparent != 0)
10901     {
10902       if (ping_color_type < 4)
10903         {
10904            ping_have_tRNS=MagickTrue;
10905            if (logging != MagickFalse)
10906              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10907                "  Setting ping_have_tRNS=MagickTrue.");
10908         }
10909     }
10910
10911   if (logging != MagickFalse)
10912     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10913       "  Writing PNG header chunks");
10914
10915   png_set_IHDR(ping,ping_info,ping_width,ping_height,
10916                ping_bit_depth,ping_color_type,
10917                ping_interlace_method,ping_compression_method,
10918                ping_filter_method);
10919
10920   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10921     {
10922       png_set_PLTE(ping,ping_info,palette,number_colors);
10923
10924       if (logging != MagickFalse)
10925         {
10926           for (i=0; i< (ssize_t) number_colors; i++)
10927           {
10928             if (i < ping_num_trans)
10929               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10930                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10931                       (int) i,
10932                       (int) palette[i].red,
10933                       (int) palette[i].green,
10934                       (int) palette[i].blue,
10935                       (int) i,
10936                       (int) ping_trans_alpha[i]);
10937              else
10938               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10939                 "     PLTE[%d] = (%d,%d,%d)",
10940                       (int) i,
10941                       (int) palette[i].red,
10942                       (int) palette[i].green,
10943                       (int) palette[i].blue);
10944            }
10945          }
10946     }
10947
10948   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10949   if (ping_exclude_sRGB != MagickFalse ||
10950      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10951   {
10952     if ((ping_exclude_tEXt == MagickFalse ||
10953        ping_exclude_zTXt == MagickFalse) &&
10954        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10955     {
10956       ResetImageProfileIterator(image);
10957       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10958       {
10959         profile=GetImageProfile(image,name);
10960
10961         if (profile != (StringInfo *) NULL)
10962           {
10963 #ifdef PNG_WRITE_iCCP_SUPPORTED
10964             if ((LocaleCompare(name,"ICC") == 0) ||
10965                 (LocaleCompare(name,"ICM") == 0))
10966               {
10967                 ping_have_iCCP = MagickTrue;
10968                 if (ping_exclude_iCCP == MagickFalse)
10969                   {
10970                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10971                         "  Setting up iCCP chunk");
10972
10973                     png_set_iCCP(ping,ping_info,(png_charp) name,0,
10974 #if (PNG_LIBPNG_VER < 10500)
10975                     (png_charp) GetStringInfoDatum(profile),
10976 #else
10977                     (const png_byte *) GetStringInfoDatum(profile),
10978 #endif
10979                     (png_uint_32) GetStringInfoLength(profile));
10980                   }
10981                 else
10982                   {
10983                     /* Do not write hex-encoded ICC chunk */
10984                        name=GetNextImageProfile(image);
10985                        continue;
10986                   }
10987               }
10988 #endif /* WRITE_iCCP */
10989
10990 #if defined(eXIf_SUPPORTED) || defined(exIf_SUPPORTED) || \
10991     defined(zxIf_SUPPORTED)
10992             if (LocaleCompare(name,"exif") == 0)
10993               {
10994                    /* Do not write hex-encoded ICC chunk; we will
10995                       write it later as an eXIf or zxIf chunk */
10996                    name=GetNextImageProfile(image);
10997                    continue;
10998               }
10999 #endif
11000
11001               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11002                  "  Setting up zTXt chunk with uuencoded %s profile",
11003                  name);
11004               Magick_png_write_raw_profile(image_info,ping,ping_info,
11005                 (unsigned char *) name,(unsigned char *) name,
11006                 GetStringInfoDatum(profile),
11007                 (png_uint_32) GetStringInfoLength(profile));
11008           }
11009         name=GetNextImageProfile(image);
11010       }
11011     }
11012   }
11013
11014 #if defined(PNG_WRITE_sRGB_SUPPORTED)
11015   if ((mng_info->have_write_global_srgb == 0) &&
11016       ping_have_iCCP != MagickTrue &&
11017       (ping_have_sRGB != MagickFalse ||
11018       png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11019     {
11020       if (ping_exclude_sRGB == MagickFalse)
11021         {
11022           /*
11023             Note image rendering intent.
11024           */
11025           if (logging != MagickFalse)
11026             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11027                 "  Setting up sRGB chunk");
11028
11029           (void) png_set_sRGB(ping,ping_info,(
11030             Magick_RenderingIntent_to_PNG_RenderingIntent(
11031               image->rendering_intent)));
11032
11033           ping_have_sRGB = MagickTrue;
11034         }
11035     }
11036
11037   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11038 #endif
11039     {
11040       if (ping_exclude_gAMA == MagickFalse &&
11041           ping_have_iCCP == MagickFalse &&
11042           ping_have_sRGB == MagickFalse &&
11043           (ping_exclude_sRGB == MagickFalse ||
11044           (image->gamma < .45 || image->gamma > .46)))
11045       {
11046       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
11047         {
11048           /*
11049             Note image gamma.
11050             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11051           */
11052           if (logging != MagickFalse)
11053             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11054               "  Setting up gAMA chunk");
11055
11056           png_set_gAMA(ping,ping_info,image->gamma);
11057         }
11058       }
11059
11060       if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
11061         {
11062           if ((mng_info->have_write_global_chrm == 0) &&
11063               (image->chromaticity.red_primary.x != 0.0))
11064             {
11065               /*
11066                 Note image chromaticity.
11067                 Note: if cHRM+gAMA == sRGB write sRGB instead.
11068               */
11069                PrimaryInfo
11070                  bp,
11071                  gp,
11072                  rp,
11073                  wp;
11074
11075                wp=image->chromaticity.white_point;
11076                rp=image->chromaticity.red_primary;
11077                gp=image->chromaticity.green_primary;
11078                bp=image->chromaticity.blue_primary;
11079
11080                if (logging != MagickFalse)
11081                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11082                    "  Setting up cHRM chunk");
11083
11084                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
11085                    bp.x,bp.y);
11086            }
11087         }
11088     }
11089
11090   if (ping_exclude_bKGD == MagickFalse)
11091     {
11092       if (ping_have_bKGD != MagickFalse)
11093         {
11094           png_set_bKGD(ping,ping_info,&ping_background);
11095           if (logging != MagickFalse)
11096             {
11097               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11098                    "    Setting up bKGD chunk");
11099               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11100                    "      background color = (%d,%d,%d)",
11101                         (int) ping_background.red,
11102                         (int) ping_background.green,
11103                         (int) ping_background.blue);
11104               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11105                    "      index = %d, gray=%d",
11106                         (int) ping_background.index,
11107                         (int) ping_background.gray);
11108             }
11109          }
11110     }
11111
11112   if (ping_exclude_pHYs == MagickFalse)
11113     {
11114       if (ping_have_pHYs != MagickFalse)
11115         {
11116           png_set_pHYs(ping,ping_info,
11117              ping_pHYs_x_resolution,
11118              ping_pHYs_y_resolution,
11119              ping_pHYs_unit_type);
11120
11121           if (logging != MagickFalse)
11122             {
11123               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11124                    "    Setting up pHYs chunk");
11125               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11126                    "      x_resolution=%lu",
11127                    (unsigned long) ping_pHYs_x_resolution);
11128               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11129                    "      y_resolution=%lu",
11130                    (unsigned long) ping_pHYs_y_resolution);
11131               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11132                    "      unit_type=%lu",
11133                    (unsigned long) ping_pHYs_unit_type);
11134             }
11135         }
11136     }
11137
11138 #if defined(PNG_tIME_SUPPORTED)
11139   if (ping_exclude_tIME == MagickFalse)
11140     {
11141       const char
11142         *timestamp;
11143
11144       if (image->taint == MagickFalse)
11145         {
11146           timestamp=GetImageOption(image_info,"png:tIME");
11147
11148           if (timestamp == (const char *) NULL)
11149             timestamp=GetImageProperty(image,"png:tIME",exception);
11150         }
11151
11152       else
11153         {
11154           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11155              "  Reset tIME in tainted image");
11156
11157           timestamp=GetImageProperty(image,"date:modify",exception);
11158         }
11159
11160       if (timestamp != (const char *) NULL)
11161           write_tIME_chunk(image,ping,ping_info,timestamp,exception);
11162     }
11163 #endif
11164
11165   if (mng_info->need_blob != MagickFalse)
11166   {
11167     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
11168        MagickFalse)
11169        png_error(ping,"WriteBlob Failed");
11170
11171      ping_have_blob=MagickTrue;
11172   }
11173
11174   png_write_info_before_PLTE(ping, ping_info);
11175
11176   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
11177     {
11178       if (logging != MagickFalse)
11179         {
11180           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11181               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
11182         }
11183
11184       if (ping_color_type == 3)
11185          (void) png_set_tRNS(ping, ping_info,
11186                 ping_trans_alpha,
11187                 ping_num_trans,
11188                 NULL);
11189
11190       else
11191         {
11192            (void) png_set_tRNS(ping, ping_info,
11193                   NULL,
11194                   0,
11195                   &ping_trans_color);
11196
11197            if (logging != MagickFalse)
11198              {
11199                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11200                  "     tRNS color   =(%d,%d,%d)",
11201                        (int) ping_trans_color.red,
11202                        (int) ping_trans_color.green,
11203                        (int) ping_trans_color.blue);
11204              }
11205          }
11206     }
11207
11208   /* write any png-chunk-b profiles */
11209   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
11210
11211   png_write_info(ping,ping_info);
11212
11213   /* write any PNG-chunk-m profiles */
11214   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
11215
11216   ping_wrote_caNv = MagickFalse;
11217
11218   /* write caNv chunk */
11219   if (ping_exclude_caNv == MagickFalse)
11220     {
11221       if ((image->page.width != 0 && image->page.width != image->columns) ||
11222           (image->page.height != 0 && image->page.height != image->rows) ||
11223           image->page.x != 0 || image->page.y != 0)
11224         {
11225           unsigned char
11226             chunk[20];
11227
11228           (void) WriteBlobMSBULong(image,16L);  /* data length=8 */
11229           PNGType(chunk,mng_caNv);
11230           LogPNGChunk(logging,mng_caNv,16L);
11231           PNGLong(chunk+4,(png_uint_32) image->page.width);
11232           PNGLong(chunk+8,(png_uint_32) image->page.height);
11233           PNGsLong(chunk+12,(png_int_32) image->page.x);
11234           PNGsLong(chunk+16,(png_int_32) image->page.y);
11235           (void) WriteBlob(image,20,chunk);
11236           (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11237           ping_wrote_caNv = MagickTrue;
11238         }
11239     }
11240
11241 #if defined(PNG_oFFs_SUPPORTED)
11242   if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
11243     {
11244       if (image->page.x || image->page.y)
11245         {
11246            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
11247               (png_int_32) image->page.y, 0);
11248
11249            if (logging != MagickFalse)
11250              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11251                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
11252                  (int) image->page.x, (int) image->page.y);
11253         }
11254     }
11255 #endif
11256
11257   /* write vpAg chunk (deprecated, replaced by caNv) */
11258   if (ping_exclude_vpAg == MagickFalse && ping_wrote_caNv == MagickFalse)
11259     {
11260       if ((image->page.width != 0 && image->page.width != image->columns) ||
11261           (image->page.height != 0 && image->page.height != image->rows))
11262         {
11263           unsigned char
11264             chunk[14];
11265
11266           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
11267           PNGType(chunk,mng_vpAg);
11268           LogPNGChunk(logging,mng_vpAg,9L);
11269           PNGLong(chunk+4,(png_uint_32) image->page.width);
11270           PNGLong(chunk+8,(png_uint_32) image->page.height);
11271           chunk[12]=0;   /* unit = pixels */
11272           (void) WriteBlob(image,13,chunk);
11273           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11274         }
11275     }
11276
11277 #if (PNG_LIBPNG_VER == 10206)
11278     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
11279 #define PNG_HAVE_IDAT               0x04
11280     ping->mode |= PNG_HAVE_IDAT;
11281 #undef PNG_HAVE_IDAT
11282 #endif
11283
11284   png_set_packing(ping);
11285   /*
11286     Allocate memory.
11287   */
11288   rowbytes=image->columns;
11289   if (image_depth > 8)
11290     rowbytes*=2;
11291   switch (ping_color_type)
11292     {
11293       case PNG_COLOR_TYPE_RGB:
11294         rowbytes*=3;
11295         break;
11296
11297       case PNG_COLOR_TYPE_GRAY_ALPHA:
11298         rowbytes*=2;
11299         break;
11300
11301       case PNG_COLOR_TYPE_RGBA:
11302         rowbytes*=4;
11303         break;
11304
11305       default:
11306         break;
11307     }
11308
11309   if (logging != MagickFalse)
11310     {
11311       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11312         "  Writing PNG image data");
11313
11314       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11315         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
11316     }
11317   pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
11318   if (pixel_info == (MemoryInfo *) NULL)
11319     png_error(ping,"Allocation of memory for pixels failed");
11320   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
11321
11322   /*
11323     Initialize image scanlines.
11324   */
11325   quantum_info=AcquireQuantumInfo(image_info,image);
11326   if (quantum_info == (QuantumInfo *) NULL)
11327     png_error(ping,"Memory allocation for quantum_info failed");
11328   quantum_info->format=UndefinedQuantumFormat;
11329   SetQuantumDepth(image,quantum_info,image_depth);
11330   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
11331   num_passes=png_set_interlace_handling(ping);
11332
11333   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11334        !mng_info->write_png48 && !mng_info->write_png64 &&
11335        !mng_info->write_png32) &&
11336        (mng_info->IsPalette ||
11337        (image_info->type == BilevelType)) &&
11338        image_matte == MagickFalse &&
11339        ping_have_non_bw == MagickFalse)
11340     {
11341       /* Palette, Bilevel, or Opaque Monochrome */
11342       register const Quantum
11343         *p;
11344
11345       SetQuantumDepth(image,quantum_info,8);
11346       for (pass=0; pass < num_passes; pass++)
11347       {
11348         /*
11349           Convert PseudoClass image to a PNG monochrome image.
11350         */
11351         for (y=0; y < (ssize_t) image->rows; y++)
11352         {
11353           if (logging != MagickFalse && y == 0)
11354              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11355                  "    Writing row of pixels (0)");
11356
11357           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11358
11359           if (p == (const Quantum *) NULL)
11360             break;
11361
11362           if (mng_info->IsPalette)
11363             {
11364               (void) ExportQuantumPixels(image,(CacheView *) NULL,
11365                 quantum_info,GrayQuantum,ping_pixels,exception);
11366               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
11367                   mng_info->write_png_depth &&
11368                   mng_info->write_png_depth != old_bit_depth)
11369                 {
11370                   /* Undo pixel scaling */
11371                   for (i=0; i < (ssize_t) image->columns; i++)
11372                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
11373                      >> (8-old_bit_depth));
11374                 }
11375             }
11376
11377           else
11378             {
11379               (void) ExportQuantumPixels(image,(CacheView *) NULL,
11380                 quantum_info,RedQuantum,ping_pixels,exception);
11381             }
11382
11383           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11384             for (i=0; i < (ssize_t) image->columns; i++)
11385                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11386                       255 : 0);
11387
11388           if (logging != MagickFalse && y == 0)
11389             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11390                 "    Writing row of pixels (1)");
11391
11392           png_write_row(ping,ping_pixels);
11393
11394           status=SetImageProgress(image,SaveImageTag,
11395               (MagickOffsetType) (pass * image->rows + y),
11396               num_passes * image->rows);
11397
11398           if (status == MagickFalse)
11399             break;
11400         }
11401       }
11402     }
11403
11404   else   /* Not Palette, Bilevel, or Opaque Monochrome */
11405     {
11406       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11407           !mng_info->write_png48 && !mng_info->write_png64 &&
11408           !mng_info->write_png32) && (image_matte != MagickFalse ||
11409           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11410           (mng_info->IsPalette) && ping_have_color == MagickFalse)
11411         {
11412           register const Quantum
11413             *p;
11414
11415           for (pass=0; pass < num_passes; pass++)
11416           {
11417
11418           for (y=0; y < (ssize_t) image->rows; y++)
11419           {
11420             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11421
11422             if (p == (const Quantum *) NULL)
11423               break;
11424
11425             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11426               {
11427                 if (mng_info->IsPalette)
11428                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11429                     quantum_info,GrayQuantum,ping_pixels,exception);
11430
11431                 else
11432                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11433                     quantum_info,RedQuantum,ping_pixels,exception);
11434
11435                 if (logging != MagickFalse && y == 0)
11436                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11437                        "    Writing GRAY PNG pixels (2)");
11438               }
11439
11440             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11441               {
11442                 if (logging != MagickFalse && y == 0)
11443                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11444                          "    Writing GRAY_ALPHA PNG pixels (2)");
11445
11446                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11447                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11448               }
11449
11450             if (logging != MagickFalse && y == 0)
11451               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11452                   "    Writing row of pixels (2)");
11453
11454             png_write_row(ping,ping_pixels);
11455
11456             status=SetImageProgress(image,SaveImageTag,
11457               (MagickOffsetType) (pass * image->rows + y),
11458               num_passes * image->rows);
11459
11460             if (status == MagickFalse)
11461               break;
11462             }
11463           }
11464         }
11465
11466       else
11467         {
11468           register const Quantum
11469             *p;
11470
11471           for (pass=0; pass < num_passes; pass++)
11472           {
11473             if ((image_depth > 8) ||
11474                 mng_info->write_png24 ||
11475                 mng_info->write_png32 ||
11476                 mng_info->write_png48 ||
11477                 mng_info->write_png64 ||
11478                 (!mng_info->write_png8 && !mng_info->IsPalette))
11479             {
11480               for (y=0; y < (ssize_t) image->rows; y++)
11481               {
11482                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11483
11484                 if (p == (const Quantum *) NULL)
11485                   break;
11486
11487                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11488                   {
11489                     if (image->storage_class == DirectClass)
11490                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11491                         quantum_info,RedQuantum,ping_pixels,exception);
11492
11493                     else
11494                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11495                         quantum_info,GrayQuantum,ping_pixels,exception);
11496                   }
11497
11498                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11499                   {
11500                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11501                       quantum_info,GrayAlphaQuantum,ping_pixels,
11502                       exception);
11503
11504                     if (logging != MagickFalse && y == 0)
11505                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11506                            "    Writing GRAY_ALPHA PNG pixels (3)");
11507                   }
11508
11509                 else if (image_matte != MagickFalse)
11510                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11511                     quantum_info,RGBAQuantum,ping_pixels,exception);
11512
11513                 else
11514                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11515                     quantum_info,RGBQuantum,ping_pixels,exception);
11516
11517                 if (logging != MagickFalse && y == 0)
11518                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11519                       "    Writing row of pixels (3)");
11520
11521                 png_write_row(ping,ping_pixels);
11522
11523                 status=SetImageProgress(image,SaveImageTag,
11524                   (MagickOffsetType) (pass * image->rows + y),
11525                   num_passes * image->rows);
11526
11527                 if (status == MagickFalse)
11528                   break;
11529               }
11530             }
11531
11532           else
11533             /* not ((image_depth > 8) ||
11534                 mng_info->write_png24 || mng_info->write_png32 ||
11535                 mng_info->write_png48 || mng_info->write_png64 ||
11536                 (!mng_info->write_png8 && !mng_info->IsPalette))
11537              */
11538             {
11539               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11540                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11541                 {
11542                   if (logging != MagickFalse)
11543                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11544                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11545
11546                   SetQuantumDepth(image,quantum_info,8);
11547                   image_depth=8;
11548                 }
11549
11550               for (y=0; y < (ssize_t) image->rows; y++)
11551               {
11552                 if (logging != MagickFalse && y == 0)
11553                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11554                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
11555                     pass);
11556
11557                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11558
11559                 if (p == (const Quantum *) NULL)
11560                   break;
11561
11562                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11563                   {
11564                     SetQuantumDepth(image,quantum_info,image->depth);
11565
11566                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11567                        quantum_info,GrayQuantum,ping_pixels,exception);
11568                   }
11569
11570                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11571                   {
11572                     if (logging != MagickFalse && y == 0)
11573                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11574                            "  Writing GRAY_ALPHA PNG pixels (4)");
11575
11576                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11577                          quantum_info,GrayAlphaQuantum,ping_pixels,
11578                          exception);
11579                   }
11580
11581                 else
11582                   {
11583                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11584                       quantum_info,IndexQuantum,ping_pixels,exception);
11585
11586                     if (logging != MagickFalse && y <= 2)
11587                     {
11588                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11589                           "  Writing row of non-gray pixels (4)");
11590
11591                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11592                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
11593                           (int)ping_pixels[0],(int)ping_pixels[1]);
11594                     }
11595                   }
11596                 png_write_row(ping,ping_pixels);
11597
11598                 status=SetImageProgress(image,SaveImageTag,
11599                   (MagickOffsetType) (pass * image->rows + y),
11600                   num_passes * image->rows);
11601
11602                 if (status == MagickFalse)
11603                   break;
11604               }
11605             }
11606           }
11607         }
11608     }
11609
11610   if (quantum_info != (QuantumInfo *) NULL)
11611     quantum_info=DestroyQuantumInfo(quantum_info);
11612
11613   if (logging != MagickFalse)
11614     {
11615       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11616         "  Wrote PNG image data");
11617
11618       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11619         "    Width: %.20g",(double) ping_width);
11620
11621       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11622         "    Height: %.20g",(double) ping_height);
11623
11624       if (mng_info->write_png_depth)
11625         {
11626           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11627             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
11628         }
11629
11630       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11631         "    PNG bit-depth written: %d",ping_bit_depth);
11632
11633       if (mng_info->write_png_colortype)
11634         {
11635           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11636             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
11637         }
11638
11639       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11640         "    PNG color-type written: %d",ping_color_type);
11641
11642       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11643         "    PNG Interlace method: %d",ping_interlace_method);
11644     }
11645   /*
11646     Generate text chunks after IDAT.
11647   */
11648   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11649   {
11650     ResetImagePropertyIterator(image);
11651     property=GetNextImageProperty(image);
11652     while (property != (const char *) NULL)
11653     {
11654       png_textp
11655         text;
11656
11657       value=GetImageProperty(image,property,exception);
11658
11659       /* Don't write any "png:" or "jpeg:" properties; those are just for
11660        * "identify" or for passing through to another JPEG
11661        */
11662       if ((LocaleNCompare(property,"png:",4) != 0 &&
11663            LocaleNCompare(property,"jpeg:",5) != 0) &&
11664
11665
11666           /* Suppress density and units if we wrote a pHYs chunk */
11667           (ping_exclude_pHYs != MagickFalse      ||
11668           LocaleCompare(property,"density") != 0 ||
11669           LocaleCompare(property,"units") != 0) &&
11670
11671           /* Suppress the IM-generated Date:create and Date:modify */
11672           (ping_exclude_date == MagickFalse      ||
11673           LocaleNCompare(property, "Date:",5) != 0))
11674         {
11675         if (value != (const char *) NULL)
11676           {
11677
11678 #if PNG_LIBPNG_VER >= 10400
11679             text=(png_textp) png_malloc(ping,
11680                  (png_alloc_size_t) sizeof(png_text));
11681 #else
11682             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11683 #endif
11684             text[0].key=(char *) property;
11685             text[0].text=(char *) value;
11686             text[0].text_length=strlen(value);
11687
11688             if (ping_exclude_tEXt != MagickFalse)
11689                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11690
11691             else if (ping_exclude_zTXt != MagickFalse)
11692                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11693
11694             else
11695             {
11696                text[0].compression=image_info->compression == NoCompression ||
11697                  (image_info->compression == UndefinedCompression &&
11698                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11699                  PNG_TEXT_COMPRESSION_zTXt ;
11700             }
11701
11702             if (logging != MagickFalse)
11703               {
11704                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11705                   "  Setting up text chunk");
11706
11707                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11708                   "    keyword: '%s'",text[0].key);
11709               }
11710
11711             png_set_text(ping,ping_info,text,1);
11712             png_free(ping,text);
11713           }
11714         }
11715       property=GetNextImageProperty(image);
11716     }
11717   }
11718
11719   /* write any PNG-chunk-e profiles */
11720   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11721
11722 #if defined(exIf_SUPPORTED) || defined(zxIf_SUPPORTED)
11723   /* write exIf profile */
11724 #ifdef IM
11725   if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
11726 #endif
11727   {
11728 #ifdef GM
11729  ImageProfileIterator
11730    *profile_iterator;
11731
11732  profile_iterator=AllocateImageProfileIterator(image);
11733  if (profile_iterator)
11734    {
11735      const char
11736        *profile_name;
11737
11738      const unsigned char
11739        *profile_info;
11740
11741      size_t
11742        profile_length;
11743
11744      while (NextImageProfile(profile_iterator,&profile_name,&profile_info,
11745                              &profile_length) != MagickFail)
11746        {
11747          if (LocaleCompare(profile_name,"exif") == 0)
11748          {
11749 #else /* IM */
11750       char
11751         *name;
11752
11753       ResetImageProfileIterator(image);
11754
11755       for (name=GetNextImageProfile(image);
11756            name != (const char *) NULL; )
11757         {
11758           if (LocaleCompare(name,"exif") == 0)
11759             {
11760               const StringInfo
11761                 *profile;
11762
11763               profile=GetImageProfile(image,name);
11764
11765               if (profile != (StringInfo *) NULL)
11766                 {
11767 #endif /* GM */
11768                png_uint_32
11769                  length;
11770                unsigned char
11771                  chunk[4],
11772                  *data;
11773
11774 #ifdef IM
11775                StringInfo
11776                  *ping_profile;
11777 #endif
11778                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11779                   "  Have eXIf profile");
11780
11781 #ifdef GM
11782                 data=profile_info;
11783                 length=(png_uint_32) profile_length;
11784 #else /* IM */
11785                ping_profile=CloneStringInfo(profile);
11786                data=GetStringInfoDatum(ping_profile),
11787                length=(png_uint_32) GetStringInfoLength(ping_profile);
11788 #endif /* GM */
11789
11790 #if defined(zxIf_write_SUPPORTED)
11791                /* For now, write uncompressed exIf/eXIf chunk */
11792 #endif
11793
11794 #if 0 /* eXIf chunk is registered */
11795                PNGType(chunk,mng_eXIf);
11796 #else /* eXIf chunk not yet registered; write exIf instead */
11797                PNGType(chunk,mng_exIf);
11798 #endif
11799                if (length < 7)
11800                  break;  /* othewise crashes */
11801
11802                /* skip the "Exif\0\0" JFIF Exif Header ID */
11803                length -= 6;
11804
11805                LogPNGChunk(logging,chunk,length);
11806                (void) WriteBlobMSBULong(image,length);
11807                (void) WriteBlob(image,4,chunk);
11808                (void) WriteBlob(image,length,data+6);
11809                (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),
11810                  data+6, (uInt) length));
11811                break;
11812 #ifdef IM
11813    name=GetNextImageProfile(image);
11814 #endif
11815              }
11816          }
11817      }
11818   }
11819 #endif /* exIf_SUPPORTED) || zxIf_SUPPORTED */
11820
11821   if (logging != MagickFalse)
11822     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11823       "  Writing PNG end info");
11824
11825   png_write_end(ping,ping_info);
11826
11827   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11828     {
11829       if (mng_info->page.x || mng_info->page.y ||
11830           (ping_width != mng_info->page.width) ||
11831           (ping_height != mng_info->page.height))
11832         {
11833           unsigned char
11834             chunk[32];
11835
11836           /*
11837             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11838           */
11839           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11840           PNGType(chunk,mng_FRAM);
11841           LogPNGChunk(logging,mng_FRAM,27L);
11842           chunk[4]=4;
11843           chunk[5]=0;  /* frame name separator (no name) */
11844           chunk[6]=1;  /* flag for changing delay, for next frame only */
11845           chunk[7]=0;  /* flag for changing frame timeout */
11846           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11847           chunk[9]=0;  /* flag for changing frame sync_id */
11848           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11849           chunk[14]=0; /* clipping boundaries delta type */
11850           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11851           PNGLong(chunk+19,
11852              (png_uint_32) (mng_info->page.x + ping_width));
11853           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11854           PNGLong(chunk+27,
11855              (png_uint_32) (mng_info->page.y + ping_height));
11856           (void) WriteBlob(image,31,chunk);
11857           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11858           mng_info->old_framing_mode=4;
11859           mng_info->framing_mode=1;
11860         }
11861
11862       else
11863         mng_info->framing_mode=3;
11864     }
11865   if (mng_info->write_mng && !mng_info->need_fram &&
11866       ((int) image->dispose == 3))
11867      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11868
11869   /*
11870     Free PNG resources.
11871   */
11872
11873   png_destroy_write_struct(&ping,&ping_info);
11874
11875   pixel_info=RelinquishVirtualMemory(pixel_info);
11876
11877   if (ping_have_blob != MagickFalse)
11878      (void) CloseBlob(image);
11879
11880   image_info=DestroyImageInfo(image_info);
11881   image=DestroyImage(image);
11882
11883   /* Store bit depth actually written */
11884   s[0]=(char) ping_bit_depth;
11885   s[1]='\0';
11886
11887   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11888
11889   if (logging != MagickFalse)
11890     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11891       "  exit WriteOnePNGImage()");
11892
11893 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11894   UnlockSemaphoreInfo(ping_semaphore);
11895 #endif
11896
11897    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11898     *    Throwing an Exception when an error occurs.
11899     */
11900
11901   return(MagickTrue);
11902 /*  End write one PNG image */
11903
11904 }
11905
11906 /*
11907 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11908 %                                                                             %
11909 %                                                                             %
11910 %                                                                             %
11911 %   W r i t e P N G I m a g e                                                 %
11912 %                                                                             %
11913 %                                                                             %
11914 %                                                                             %
11915 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11916 %
11917 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11918 %  Multiple-image Network Graphics (MNG) image file.
11919 %
11920 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11921 %
11922 %  The format of the WritePNGImage method is:
11923 %
11924 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11925 %        Image *image,ExceptionInfo *exception)
11926 %
11927 %  A description of each parameter follows:
11928 %
11929 %    o image_info: the image info.
11930 %
11931 %    o image:  The image.
11932 %
11933 %    o exception: return any errors or warnings in this structure.
11934 %
11935 %  Returns MagickTrue on success, MagickFalse on failure.
11936 %
11937 %  Communicating with the PNG encoder:
11938 %
11939 %  While the datastream written is always in PNG format and normally would
11940 %  be given the "png" file extension, this method also writes the following
11941 %  pseudo-formats which are subsets of png:
11942 %
11943 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11944 %               a depth greater than 8, the depth is reduced. If transparency
11945 %               is present, the tRNS chunk must only have values 0 and 255
11946 %               (i.e., transparency is binary: fully opaque or fully
11947 %               transparent).  If other values are present they will be
11948 %               50%-thresholded to binary transparency.  If more than 256
11949 %               colors are present, they will be quantized to the 4-4-4-1,
11950 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11951 %               of any resulting fully-transparent pixels is changed to
11952 %               the image's background color.
11953 %
11954 %               If you want better quantization or dithering of the colors
11955 %               or alpha than that, you need to do it before calling the
11956 %               PNG encoder. The pixels contain 8-bit indices even if
11957 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11958 %               images will be written as indexed PNG files even though the
11959 %               PNG grayscale type might be slightly more efficient.  Please
11960 %               note that writing to the PNG8 format may result in loss
11961 %               of color and alpha data.
11962 %
11963 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11964 %               chunk can be present to convey binary transparency by naming
11965 %               one of the colors as transparent.  The only loss incurred
11966 %               is reduction of sample depth to 8.  If the image has more
11967 %               than one transparent color, has semitransparent pixels, or
11968 %               has an opaque pixel with the same RGB components as the
11969 %               transparent color, an image is not written.
11970 %
11971 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
11972 %               transparency is permitted, i.e., the alpha sample for
11973 %               each pixel can have any value from 0 to 255. The alpha
11974 %               channel is present even if the image is fully opaque.
11975 %               The only loss in data is the reduction of the sample depth
11976 %               to 8.
11977 %
11978 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
11979 %               chunk can be present to convey binary transparency by naming
11980 %               one of the colors as transparent.  If the image has more
11981 %               than one transparent color, has semitransparent pixels, or
11982 %               has an opaque pixel with the same RGB components as the
11983 %               transparent color, an image is not written.
11984 %
11985 %    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
11986 %               transparency is permitted, i.e., the alpha sample for
11987 %               each pixel can have any value from 0 to 65535. The alpha
11988 %               channel is present even if the image is fully opaque.
11989 %
11990 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
11991 %               image, if the input was a PNG, is written.  If these values
11992 %               cannot be found, or if the pixels have been changed in a way
11993 %               that makes this impossible, then "PNG00" falls back to the
11994 %               regular "PNG" format.
11995 %
11996 %    o -define: For more precise control of the PNG output, you can use the
11997 %               Image options "png:bit-depth" and "png:color-type".  These
11998 %               can be set from the commandline with "-define" and also
11999 %               from the application programming interfaces.  The options
12000 %               are case-independent and are converted to lowercase before
12001 %               being passed to this encoder.
12002 %
12003 %               png:color-type can be 0, 2, 3, 4, or 6.
12004 %
12005 %               When png:color-type is 0 (Grayscale), png:bit-depth can
12006 %               be 1, 2, 4, 8, or 16.
12007 %
12008 %               When png:color-type is 2 (RGB), png:bit-depth can
12009 %               be 8 or 16.
12010 %
12011 %               When png:color-type is 3 (Indexed), png:bit-depth can
12012 %               be 1, 2, 4, or 8.  This refers to the number of bits
12013 %               used to store the index.  The color samples always have
12014 %               bit-depth 8 in indexed PNG files.
12015 %
12016 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
12017 %               png:bit-depth can be 8 or 16.
12018 %
12019 %               If the image cannot be written without loss with the
12020 %               requested bit-depth and color-type, a PNG file will not
12021 %               be written, a warning will be issued, and the encoder will
12022 %               return MagickFalse.
12023 %
12024 %  Since image encoders should not be responsible for the "heavy lifting",
12025 %  the user should make sure that ImageMagick has already reduced the
12026 %  image depth and number of colors and limit transparency to binary
12027 %  transparency prior to attempting to write the image with depth, color,
12028 %  or transparency limitations.
12029 %
12030 %  Note that another definition, "png:bit-depth-written" exists, but it
12031 %  is not intended for external use.  It is only used internally by the
12032 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
12033 %
12034 %  It is possible to request that the PNG encoder write previously-formatted
12035 %  ancillary chunks in the output PNG file, using the "-profile" commandline
12036 %  option as shown below or by setting the profile via a programming
12037 %  interface:
12038 %
12039 %     -profile PNG-chunk-x:<file>
12040 %
12041 %  where x is a location flag and <file> is a file containing the chunk
12042 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
12043 %  This encoder will compute the chunk length and CRC, so those must not
12044 %  be included in the file.
12045 %
12046 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
12047 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
12048 %  of the same type, then add a short unique string after the "x" to prevent
12049 %  subsequent profiles from overwriting the preceding ones, e.g.,
12050 %
12051 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
12052 %
12053 %  As of version 6.6.6 the following optimizations are always done:
12054 %
12055 %   o  32-bit depth is reduced to 16.
12056 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
12057 %      high byte and low byte are identical.
12058 %   o  Palette is sorted to remove unused entries and to put a
12059 %      transparent color first, if BUILD_PNG_PALETTE is defined.
12060 %   o  Opaque matte channel is removed when writing an indexed PNG.
12061 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
12062 %      this can be done without loss and a larger bit depth N was not
12063 %      requested via the "-define png:bit-depth=N" option.
12064 %   o  If matte channel is present but only one transparent color is
12065 %      present, RGB+tRNS is written instead of RGBA
12066 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
12067 %      was requested when converting an opaque image).
12068 %
12069 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12070 */
12071 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
12072   Image *image,ExceptionInfo *exception)
12073 {
12074   MagickBooleanType
12075     excluding,
12076     logging,
12077     status;
12078
12079   MngInfo
12080     *mng_info;
12081
12082   const char
12083     *value;
12084
12085   int
12086     source;
12087
12088   /*
12089     Open image file.
12090   */
12091   assert(image_info != (const ImageInfo *) NULL);
12092   assert(image_info->signature == MagickCoreSignature);
12093   assert(image != (Image *) NULL);
12094   assert(image->signature == MagickCoreSignature);
12095   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12096   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
12097   /*
12098     Allocate a MngInfo structure.
12099   */
12100   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12101
12102   if (mng_info == (MngInfo *) NULL)
12103     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12104
12105   /*
12106     Initialize members of the MngInfo structure.
12107   */
12108   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12109   mng_info->image=image;
12110   mng_info->equal_backgrounds=MagickTrue;
12111
12112   /* See if user has requested a specific PNG subformat */
12113
12114   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12115   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12116   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12117   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
12118   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
12119
12120   value=GetImageOption(image_info,"png:format");
12121
12122   if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
12123     {
12124       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12125          "  Format=%s",value);
12126
12127       mng_info->write_png8 = MagickFalse;
12128       mng_info->write_png24 = MagickFalse;
12129       mng_info->write_png32 = MagickFalse;
12130       mng_info->write_png48 = MagickFalse;
12131       mng_info->write_png64 = MagickFalse;
12132
12133       if (LocaleCompare(value,"png8") == 0)
12134         mng_info->write_png8 = MagickTrue;
12135
12136       else if (LocaleCompare(value,"png24") == 0)
12137         mng_info->write_png24 = MagickTrue;
12138
12139       else if (LocaleCompare(value,"png32") == 0)
12140         mng_info->write_png32 = MagickTrue;
12141
12142       else if (LocaleCompare(value,"png48") == 0)
12143         mng_info->write_png48 = MagickTrue;
12144
12145       else if (LocaleCompare(value,"png64") == 0)
12146         mng_info->write_png64 = MagickTrue;
12147
12148       else if ((LocaleCompare(value,"png00") == 0) ||
12149          LocaleCompare(image_info->magick,"PNG00") == 0)
12150         {
12151           /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
12152           value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
12153
12154           if (value != (char *) NULL)
12155             {
12156               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12157                  "  png00 inherited bit depth=%s",value);
12158
12159               if (LocaleCompare(value,"1") == 0)
12160                 mng_info->write_png_depth = 1;
12161
12162               else if (LocaleCompare(value,"2") == 0)
12163                 mng_info->write_png_depth = 2;
12164
12165               else if (LocaleCompare(value,"4") == 0)
12166                 mng_info->write_png_depth = 4;
12167
12168               else if (LocaleCompare(value,"8") == 0)
12169                 mng_info->write_png_depth = 8;
12170
12171               else if (LocaleCompare(value,"16") == 0)
12172                 mng_info->write_png_depth = 16;
12173             }
12174
12175           value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
12176
12177           if (value != (char *) NULL)
12178             {
12179               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12180                  "  png00 inherited color type=%s",value);
12181
12182               if (LocaleCompare(value,"0") == 0)
12183                 mng_info->write_png_colortype = 1;
12184
12185               else if (LocaleCompare(value,"2") == 0)
12186                 mng_info->write_png_colortype = 3;
12187
12188               else if (LocaleCompare(value,"3") == 0)
12189                 mng_info->write_png_colortype = 4;
12190
12191               else if (LocaleCompare(value,"4") == 0)
12192                 mng_info->write_png_colortype = 5;
12193
12194               else if (LocaleCompare(value,"6") == 0)
12195                 mng_info->write_png_colortype = 7;
12196             }
12197         }
12198     }
12199
12200   if (mng_info->write_png8)
12201     {
12202       mng_info->write_png_colortype = /* 3 */ 4;
12203       mng_info->write_png_depth = 8;
12204       image->depth = 8;
12205     }
12206
12207   if (mng_info->write_png24)
12208     {
12209       mng_info->write_png_colortype = /* 2 */ 3;
12210       mng_info->write_png_depth = 8;
12211       image->depth = 8;
12212
12213       if (image->alpha_trait != UndefinedPixelTrait)
12214         (void) SetImageType(image,TrueColorAlphaType,exception);
12215
12216       else
12217         (void) SetImageType(image,TrueColorType,exception);
12218
12219       (void) SyncImage(image,exception);
12220     }
12221
12222   if (mng_info->write_png32)
12223     {
12224       mng_info->write_png_colortype = /* 6 */  7;
12225       mng_info->write_png_depth = 8;
12226       image->depth = 8;
12227       image->alpha_trait = BlendPixelTrait;
12228
12229       (void) SetImageType(image,TrueColorAlphaType,exception);
12230       (void) SyncImage(image,exception);
12231     }
12232
12233   if (mng_info->write_png48)
12234     {
12235       mng_info->write_png_colortype = /* 2 */ 3;
12236       mng_info->write_png_depth = 16;
12237       image->depth = 16;
12238
12239       if (image->alpha_trait != UndefinedPixelTrait)
12240         (void) SetImageType(image,TrueColorAlphaType,exception);
12241
12242       else
12243         (void) SetImageType(image,TrueColorType,exception);
12244
12245       (void) SyncImage(image,exception);
12246     }
12247
12248   if (mng_info->write_png64)
12249     {
12250       mng_info->write_png_colortype = /* 6 */  7;
12251       mng_info->write_png_depth = 16;
12252       image->depth = 16;
12253       image->alpha_trait = BlendPixelTrait;
12254
12255       (void) SetImageType(image,TrueColorAlphaType,exception);
12256       (void) SyncImage(image,exception);
12257     }
12258
12259   value=GetImageOption(image_info,"png:bit-depth");
12260
12261   if (value != (char *) NULL)
12262     {
12263       if (LocaleCompare(value,"1") == 0)
12264         mng_info->write_png_depth = 1;
12265
12266       else if (LocaleCompare(value,"2") == 0)
12267         mng_info->write_png_depth = 2;
12268
12269       else if (LocaleCompare(value,"4") == 0)
12270         mng_info->write_png_depth = 4;
12271
12272       else if (LocaleCompare(value,"8") == 0)
12273         mng_info->write_png_depth = 8;
12274
12275       else if (LocaleCompare(value,"16") == 0)
12276         mng_info->write_png_depth = 16;
12277
12278       else
12279         (void) ThrowMagickException(exception,
12280              GetMagickModule(),CoderWarning,
12281              "ignoring invalid defined png:bit-depth",
12282              "=%s",value);
12283
12284       if (logging != MagickFalse)
12285         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12286           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
12287     }
12288
12289   value=GetImageOption(image_info,"png:color-type");
12290
12291   if (value != (char *) NULL)
12292     {
12293       /* We must store colortype+1 because 0 is a valid colortype */
12294       if (LocaleCompare(value,"0") == 0)
12295         mng_info->write_png_colortype = 1;
12296
12297       else if (LocaleCompare(value,"1") == 0)
12298         mng_info->write_png_colortype = 2;
12299
12300       else if (LocaleCompare(value,"2") == 0)
12301         mng_info->write_png_colortype = 3;
12302
12303       else if (LocaleCompare(value,"3") == 0)
12304         mng_info->write_png_colortype = 4;
12305
12306       else if (LocaleCompare(value,"4") == 0)
12307         mng_info->write_png_colortype = 5;
12308
12309       else if (LocaleCompare(value,"6") == 0)
12310         mng_info->write_png_colortype = 7;
12311
12312       else
12313         (void) ThrowMagickException(exception,
12314              GetMagickModule(),CoderWarning,
12315              "ignoring invalid defined png:color-type",
12316              "=%s",value);
12317
12318       if (logging != MagickFalse)
12319         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12320           "  png:color-type=%d was defined.\n",
12321           mng_info->write_png_colortype-1);
12322     }
12323
12324   /* Check for chunks to be excluded:
12325    *
12326    * The default is to not exclude any known chunks except for any
12327    * listed in the "unused_chunks" array, above.
12328    *
12329    * Chunks can be listed for exclusion via a "png:exclude-chunk"
12330    * define (in the image properties or in the image artifacts)
12331    * or via a mng_info member.  For convenience, in addition
12332    * to or instead of a comma-separated list of chunks, the
12333    * "exclude-chunk" string can be simply "all" or "none".
12334    *
12335    * The exclude-chunk define takes priority over the mng_info.
12336    *
12337    * A "png:include-chunk" define takes  priority over both the
12338    * mng_info and the "png:exclude-chunk" define.  Like the
12339    * "exclude-chunk" string, it can define "all" or "none" as
12340    * well as a comma-separated list.  Chunks that are unknown to
12341    * ImageMagick are always excluded, regardless of their "copy-safe"
12342    * status according to the PNG specification, and even if they
12343    * appear in the "include-chunk" list. Such defines appearing among
12344    * the image options take priority over those found among the image
12345    * artifacts.
12346    *
12347    * Finally, all chunks listed in the "unused_chunks" array are
12348    * automatically excluded, regardless of the other instructions
12349    * or lack thereof.
12350    *
12351    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
12352    * will not be written and the gAMA chunk will only be written if it
12353    * is not between .45 and .46, or approximately (1.0/2.2).
12354    *
12355    * If you exclude tRNS and the image has transparency, the colortype
12356    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
12357    *
12358    * The -strip option causes StripImage() to set the png:include-chunk
12359    * artifact to "none,trns,gama".
12360    */
12361
12362   mng_info->ping_exclude_bKGD=MagickFalse;
12363   mng_info->ping_exclude_caNv=MagickFalse;
12364   mng_info->ping_exclude_cHRM=MagickFalse;
12365   mng_info->ping_exclude_date=MagickFalse;
12366   mng_info->ping_exclude_eXIf=MagickFalse;
12367   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
12368   mng_info->ping_exclude_gAMA=MagickFalse;
12369   mng_info->ping_exclude_iCCP=MagickFalse;
12370   /* mng_info->ping_exclude_iTXt=MagickFalse; */
12371   mng_info->ping_exclude_oFFs=MagickFalse;
12372   mng_info->ping_exclude_pHYs=MagickFalse;
12373   mng_info->ping_exclude_sRGB=MagickFalse;
12374   mng_info->ping_exclude_tEXt=MagickFalse;
12375   mng_info->ping_exclude_tIME=MagickFalse;
12376   mng_info->ping_exclude_tRNS=MagickFalse;
12377   mng_info->ping_exclude_vpAg=MagickFalse;
12378   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
12379   mng_info->ping_exclude_zTXt=MagickFalse;
12380
12381   mng_info->ping_preserve_colormap=MagickFalse;
12382
12383   value=GetImageOption(image_info,"png:preserve-colormap");
12384   if (value == NULL)
12385      value=GetImageArtifact(image,"png:preserve-colormap");
12386   if (value != NULL)
12387      mng_info->ping_preserve_colormap=MagickTrue;
12388
12389   mng_info->ping_preserve_iCCP=MagickFalse;
12390
12391   value=GetImageOption(image_info,"png:preserve-iCCP");
12392   if (value == NULL)
12393      value=GetImageArtifact(image,"png:preserve-iCCP");
12394   if (value != NULL)
12395      mng_info->ping_preserve_iCCP=MagickTrue;
12396
12397   /* These compression-level, compression-strategy, and compression-filter
12398    * defines take precedence over values from the -quality option.
12399    */
12400   value=GetImageOption(image_info,"png:compression-level");
12401   if (value == NULL)
12402      value=GetImageArtifact(image,"png:compression-level");
12403   if (value != NULL)
12404   {
12405       /* We have to add 1 to everything because 0 is a valid input,
12406        * and we want to use 0 (the default) to mean undefined.
12407        */
12408       if (LocaleCompare(value,"0") == 0)
12409         mng_info->write_png_compression_level = 1;
12410
12411       else if (LocaleCompare(value,"1") == 0)
12412         mng_info->write_png_compression_level = 2;
12413
12414       else if (LocaleCompare(value,"2") == 0)
12415         mng_info->write_png_compression_level = 3;
12416
12417       else if (LocaleCompare(value,"3") == 0)
12418         mng_info->write_png_compression_level = 4;
12419
12420       else if (LocaleCompare(value,"4") == 0)
12421         mng_info->write_png_compression_level = 5;
12422
12423       else if (LocaleCompare(value,"5") == 0)
12424         mng_info->write_png_compression_level = 6;
12425
12426       else if (LocaleCompare(value,"6") == 0)
12427         mng_info->write_png_compression_level = 7;
12428
12429       else if (LocaleCompare(value,"7") == 0)
12430         mng_info->write_png_compression_level = 8;
12431
12432       else if (LocaleCompare(value,"8") == 0)
12433         mng_info->write_png_compression_level = 9;
12434
12435       else if (LocaleCompare(value,"9") == 0)
12436         mng_info->write_png_compression_level = 10;
12437
12438       else
12439         (void) ThrowMagickException(exception,
12440              GetMagickModule(),CoderWarning,
12441              "ignoring invalid defined png:compression-level",
12442              "=%s",value);
12443     }
12444
12445   value=GetImageOption(image_info,"png:compression-strategy");
12446   if (value == NULL)
12447      value=GetImageArtifact(image,"png:compression-strategy");
12448   if (value != NULL)
12449   {
12450       if (LocaleCompare(value,"0") == 0)
12451         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12452
12453       else if (LocaleCompare(value,"1") == 0)
12454         mng_info->write_png_compression_strategy = Z_FILTERED+1;
12455
12456       else if (LocaleCompare(value,"2") == 0)
12457         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
12458
12459       else if (LocaleCompare(value,"3") == 0)
12460 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
12461         mng_info->write_png_compression_strategy = Z_RLE+1;
12462 #else
12463         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12464 #endif
12465
12466       else if (LocaleCompare(value,"4") == 0)
12467 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
12468         mng_info->write_png_compression_strategy = Z_FIXED+1;
12469 #else
12470         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12471 #endif
12472
12473       else
12474         (void) ThrowMagickException(exception,
12475              GetMagickModule(),CoderWarning,
12476              "ignoring invalid defined png:compression-strategy",
12477              "=%s",value);
12478     }
12479
12480   value=GetImageOption(image_info,"png:compression-filter");
12481   if (value == NULL)
12482      value=GetImageArtifact(image,"png:compression-filter");
12483   if (value != NULL)
12484   {
12485       /* To do: combinations of filters allowed by libpng
12486        * masks 0x08 through 0xf8
12487        *
12488        * Implement this as a comma-separated list of 0,1,2,3,4,5
12489        * where 5 is a special case meaning PNG_ALL_FILTERS.
12490        */
12491
12492       if (LocaleCompare(value,"0") == 0)
12493         mng_info->write_png_compression_filter = 1;
12494
12495       else if (LocaleCompare(value,"1") == 0)
12496         mng_info->write_png_compression_filter = 2;
12497
12498       else if (LocaleCompare(value,"2") == 0)
12499         mng_info->write_png_compression_filter = 3;
12500
12501       else if (LocaleCompare(value,"3") == 0)
12502         mng_info->write_png_compression_filter = 4;
12503
12504       else if (LocaleCompare(value,"4") == 0)
12505         mng_info->write_png_compression_filter = 5;
12506
12507       else if (LocaleCompare(value,"5") == 0)
12508         mng_info->write_png_compression_filter = 6;
12509
12510       else
12511         (void) ThrowMagickException(exception,
12512              GetMagickModule(),CoderWarning,
12513              "ignoring invalid defined png:compression-filter",
12514              "=%s",value);
12515   }
12516
12517   for (source=0; source<8; source++)
12518   {
12519     value = NULL;
12520
12521     if (source == 0)
12522       value=GetImageOption(image_info,"png:exclude-chunks");
12523
12524     if (source == 1)
12525       value=GetImageArtifact(image,"png:exclude-chunks");
12526
12527     if (source == 2)
12528       value=GetImageOption(image_info,"png:exclude-chunk");
12529
12530     if (source == 3)
12531       value=GetImageArtifact(image,"png:exclude-chunk");
12532
12533     if (source == 4)
12534       value=GetImageOption(image_info,"png:include-chunks");
12535
12536     if (source == 5)
12537       value=GetImageArtifact(image,"png:include-chunks");
12538
12539     if (source == 6)
12540       value=GetImageOption(image_info,"png:include-chunk");
12541
12542     if (source == 7)
12543       value=GetImageArtifact(image,"png:include-chunk");
12544
12545     if (value == NULL)
12546        continue;
12547
12548     if (source < 4)
12549       excluding = MagickTrue;
12550     else
12551       excluding = MagickFalse;
12552
12553     if (logging != MagickFalse)
12554       {
12555         if (source == 0 || source == 2)
12556            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12557               "  png:exclude-chunk=%s found in image options.\n", value);
12558         else if (source == 1 || source == 3)
12559            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12560               "  png:exclude-chunk=%s found in image artifacts.\n", value);
12561         else if (source == 4 || source == 6)
12562            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12563               "  png:include-chunk=%s found in image options.\n", value);
12564         else /* if (source == 5 || source == 7) */
12565            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12566               "  png:include-chunk=%s found in image artifacts.\n", value);
12567       }
12568
12569     if (IsOptionMember("all",value) != MagickFalse)
12570       {
12571         mng_info->ping_exclude_bKGD=excluding;
12572         mng_info->ping_exclude_caNv=excluding;
12573         mng_info->ping_exclude_cHRM=excluding;
12574         mng_info->ping_exclude_date=excluding;
12575         mng_info->ping_exclude_EXIF=excluding;
12576         mng_info->ping_exclude_eXIf=excluding;
12577         mng_info->ping_exclude_gAMA=excluding;
12578         mng_info->ping_exclude_iCCP=excluding;
12579         /* mng_info->ping_exclude_iTXt=excluding; */
12580         mng_info->ping_exclude_oFFs=excluding;
12581         mng_info->ping_exclude_pHYs=excluding;
12582         mng_info->ping_exclude_sRGB=excluding;
12583         mng_info->ping_exclude_tEXt=excluding;
12584         mng_info->ping_exclude_tIME=excluding;
12585         mng_info->ping_exclude_tRNS=excluding;
12586         mng_info->ping_exclude_vpAg=excluding;
12587         mng_info->ping_exclude_zCCP=excluding;
12588         mng_info->ping_exclude_zTXt=excluding;
12589       }
12590
12591     if (IsOptionMember("none",value) != MagickFalse)
12592       {
12593         mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12594           MagickTrue;
12595         mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
12596           MagickTrue;
12597         mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12598           MagickTrue;
12599         mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12600           MagickTrue;
12601         mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
12602           MagickTrue;
12603         mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12604           MagickTrue;
12605         mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12606           MagickTrue;
12607         mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12608           MagickTrue;
12609         /* mng_info->ping_exclude_iTXt=!excluding; */
12610         mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12611           MagickTrue;
12612         mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12613           MagickTrue;
12614         mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12615           MagickTrue;
12616         mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12617           MagickTrue;
12618         mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12619           MagickTrue;
12620         mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12621           MagickTrue;
12622         mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
12623           MagickTrue;
12624         mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12625           MagickTrue;
12626         mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12627           MagickTrue;
12628       }
12629
12630     if (IsOptionMember("bkgd",value) != MagickFalse)
12631       mng_info->ping_exclude_bKGD=excluding;
12632
12633     if (IsOptionMember("caNv",value) != MagickFalse)
12634       mng_info->ping_exclude_caNv=excluding;
12635
12636     if (IsOptionMember("chrm",value) != MagickFalse)
12637       mng_info->ping_exclude_cHRM=excluding;
12638
12639     if (IsOptionMember("date",value) != MagickFalse)
12640       mng_info->ping_exclude_date=excluding;
12641
12642     if (IsOptionMember("exif",value) != MagickFalse)
12643       {
12644         mng_info->ping_exclude_EXIF=excluding;
12645         mng_info->ping_exclude_eXIf=excluding;
12646       }
12647
12648     if (IsOptionMember("gama",value) != MagickFalse)
12649       mng_info->ping_exclude_gAMA=excluding;
12650
12651     if (IsOptionMember("iccp",value) != MagickFalse)
12652       mng_info->ping_exclude_iCCP=excluding;
12653
12654 #if 0
12655     if (IsOptionMember("itxt",value) != MagickFalse)
12656       mng_info->ping_exclude_iTXt=excluding;
12657 #endif
12658
12659     if (IsOptionMember("offs",value) != MagickFalse)
12660       mng_info->ping_exclude_oFFs=excluding;
12661
12662     if (IsOptionMember("phys",value) != MagickFalse)
12663       mng_info->ping_exclude_pHYs=excluding;
12664
12665     if (IsOptionMember("srgb",value) != MagickFalse)
12666       mng_info->ping_exclude_sRGB=excluding;
12667
12668     if (IsOptionMember("text",value) != MagickFalse)
12669       mng_info->ping_exclude_tEXt=excluding;
12670
12671     if (IsOptionMember("time",value) != MagickFalse)
12672       mng_info->ping_exclude_tIME=excluding;
12673
12674     if (IsOptionMember("trns",value) != MagickFalse)
12675       mng_info->ping_exclude_tRNS=excluding;
12676
12677     if (IsOptionMember("vpag",value) != MagickFalse)
12678       mng_info->ping_exclude_vpAg=excluding;
12679
12680     if (IsOptionMember("zccp",value) != MagickFalse)
12681       mng_info->ping_exclude_zCCP=excluding;
12682
12683     if (IsOptionMember("ztxt",value) != MagickFalse)
12684       mng_info->ping_exclude_zTXt=excluding;
12685   }
12686
12687   if (logging != MagickFalse)
12688   {
12689     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12690       "  Chunks to be excluded from the output png:");
12691     if (mng_info->ping_exclude_bKGD != MagickFalse)
12692       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12693           "    bKGD");
12694     if (mng_info->ping_exclude_caNv != MagickFalse)
12695       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12696           "    caNv");
12697     if (mng_info->ping_exclude_cHRM != MagickFalse)
12698       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12699           "    cHRM");
12700     if (mng_info->ping_exclude_date != MagickFalse)
12701       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12702           "    date");
12703     if (mng_info->ping_exclude_EXIF != MagickFalse)
12704       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12705           "    EXIF");
12706     if (mng_info->ping_exclude_eXIf != MagickFalse)
12707       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12708           "    eXIf");
12709     if (mng_info->ping_exclude_gAMA != MagickFalse)
12710       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12711           "    gAMA");
12712     if (mng_info->ping_exclude_iCCP != MagickFalse)
12713       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12714           "    iCCP");
12715 #if 0
12716     if (mng_info->ping_exclude_iTXt != MagickFalse)
12717       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12718           "    iTXt");
12719 #endif
12720
12721     if (mng_info->ping_exclude_oFFs != MagickFalse)
12722       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12723           "    oFFs");
12724     if (mng_info->ping_exclude_pHYs != MagickFalse)
12725       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12726           "    pHYs");
12727     if (mng_info->ping_exclude_sRGB != MagickFalse)
12728       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12729           "    sRGB");
12730     if (mng_info->ping_exclude_tEXt != MagickFalse)
12731       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12732           "    tEXt");
12733     if (mng_info->ping_exclude_tIME != MagickFalse)
12734       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12735           "    tIME");
12736     if (mng_info->ping_exclude_tRNS != MagickFalse)
12737       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12738           "    tRNS");
12739     if (mng_info->ping_exclude_vpAg != MagickFalse)
12740       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12741           "    vpAg");
12742     if (mng_info->ping_exclude_zCCP != MagickFalse)
12743       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12744           "    zCCP");
12745     if (mng_info->ping_exclude_zTXt != MagickFalse)
12746       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12747           "    zTXt");
12748   }
12749
12750   mng_info->need_blob = MagickTrue;
12751
12752   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12753
12754   mng_info=MngInfoFreeStruct(mng_info);
12755
12756   if (logging != MagickFalse)
12757     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12758
12759   return(status);
12760 }
12761
12762 #if defined(JNG_SUPPORTED)
12763
12764 /* Write one JNG image */
12765 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12766    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12767 {
12768   Image
12769     *jpeg_image;
12770
12771   ImageInfo
12772     *jpeg_image_info;
12773
12774   MagickBooleanType
12775     logging,
12776     status;
12777
12778   size_t
12779     length;
12780
12781   unsigned char
12782     *blob,
12783     chunk[80],
12784     *p;
12785
12786   unsigned int
12787     jng_alpha_compression_method,
12788     jng_alpha_sample_depth,
12789     jng_color_type,
12790     transparent;
12791
12792   size_t
12793     jng_alpha_quality,
12794     jng_quality;
12795
12796   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12797     "  Enter WriteOneJNGImage()");
12798
12799   blob=(unsigned char *) NULL;
12800   jpeg_image=(Image *) NULL;
12801   jpeg_image_info=(ImageInfo *) NULL;
12802   length=0;
12803
12804   status=MagickTrue;
12805   transparent=image_info->type==GrayscaleAlphaType ||
12806      image_info->type==TrueColorAlphaType ||
12807      image->alpha_trait != UndefinedPixelTrait;
12808
12809   jng_alpha_sample_depth = 0;
12810
12811   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12812
12813   jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12814
12815   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12816       image_info->quality;
12817
12818   if (jng_alpha_quality >= 1000)
12819     jng_alpha_quality /= 1000;
12820
12821   length=0;
12822
12823   if (transparent != 0)
12824     {
12825       jng_color_type=14;
12826
12827       /* Create JPEG blob, image, and image_info */
12828       if (logging != MagickFalse)
12829         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12830           "  Creating jpeg_image_info for alpha.");
12831
12832       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12833
12834       if (jpeg_image_info == (ImageInfo *) NULL)
12835         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12836
12837       if (logging != MagickFalse)
12838         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12839           "  Creating jpeg_image.");
12840
12841       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12842       if (jpeg_image == (Image *) NULL)
12843         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12844       (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12845       jpeg_image->alpha_trait=UndefinedPixelTrait;
12846       jpeg_image->quality=jng_alpha_quality;
12847       jpeg_image_info->type=GrayscaleType;
12848       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12849       (void) AcquireUniqueFilename(jpeg_image->filename);
12850       (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12851         "%s",jpeg_image->filename);
12852     }
12853   else
12854     {
12855       jng_alpha_compression_method=0;
12856       jng_color_type=10;
12857       jng_alpha_sample_depth=0;
12858     }
12859
12860   /* To do: check bit depth of PNG alpha channel */
12861
12862   /* Check if image is grayscale. */
12863   if (image_info->type != TrueColorAlphaType && image_info->type !=
12864     TrueColorType && SetImageGray(image,exception))
12865     jng_color_type-=2;
12866
12867   if (logging != MagickFalse)
12868     {
12869         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12870           "    JNG Quality           = %d",(int) jng_quality);
12871         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12872           "    JNG Color Type        = %d",jng_color_type);
12873         if (transparent != 0)
12874           {
12875             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12876               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12877             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12878               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12879             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12880               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12881           }
12882     }
12883
12884   if (transparent != 0)
12885     {
12886       if (jng_alpha_compression_method==0)
12887         {
12888           const char
12889             *value;
12890
12891           /* Encode alpha as a grayscale PNG blob */
12892           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12893             exception);
12894           if (status == MagickFalse)
12895             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12896
12897           if (logging != MagickFalse)
12898             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12899               "  Creating PNG blob.");
12900
12901           (void) CopyMagickString(jpeg_image_info->magick,"PNG",
12902              MagickPathExtent);
12903           (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12904           jpeg_image_info->interlace=NoInterlace;
12905
12906           /* Exclude all ancillary chunks */
12907           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12908
12909           blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12910             &length,exception);
12911
12912           /* Retrieve sample depth used */
12913           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12914           if (value != (char *) NULL)
12915             jng_alpha_sample_depth= (unsigned int) value[0];
12916         }
12917       else
12918         {
12919           /* Encode alpha as a grayscale JPEG blob */
12920
12921           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12922             exception);
12923           if (status == MagickFalse)
12924             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12925
12926           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",
12927             MagickPathExtent);
12928           (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12929           jpeg_image_info->interlace=NoInterlace;
12930           if (logging != MagickFalse)
12931             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12932               "  Creating blob.");
12933           blob=(unsigned char *) ImageToBlob(jpeg_image_info,
12934              jpeg_image,&length,
12935            exception);
12936           jng_alpha_sample_depth=8;
12937
12938           if (logging != MagickFalse)
12939             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12940               "  Successfully read jpeg_image into a blob, length=%.20g.",
12941               (double) length);
12942
12943         }
12944       /* Destroy JPEG image and image_info */
12945       jpeg_image=DestroyImage(jpeg_image);
12946       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12947       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12948     }
12949
12950   /* Write JHDR chunk */
12951   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12952   PNGType(chunk,mng_JHDR);
12953   LogPNGChunk(logging,mng_JHDR,16L);
12954   PNGLong(chunk+4,(png_uint_32) image->columns);
12955   PNGLong(chunk+8,(png_uint_32) image->rows);
12956   chunk[12]=jng_color_type;
12957   chunk[13]=8;  /* sample depth */
12958   chunk[14]=8; /*jng_image_compression_method */
12959   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12960   chunk[16]=jng_alpha_sample_depth;
12961   chunk[17]=jng_alpha_compression_method;
12962   chunk[18]=0; /*jng_alpha_filter_method */
12963   chunk[19]=0; /*jng_alpha_interlace_method */
12964   (void) WriteBlob(image,20,chunk);
12965   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12966   if (logging != MagickFalse)
12967     {
12968       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12969         "    JNG width:%15lu",(unsigned long) image->columns);
12970
12971       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12972         "    JNG height:%14lu",(unsigned long) image->rows);
12973
12974       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12975         "    JNG color type:%10d",jng_color_type);
12976
12977       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12978         "    JNG sample depth:%8d",8);
12979
12980       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12981         "    JNG compression:%9d",8);
12982
12983       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12984         "    JNG interlace:%11d",0);
12985
12986       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12987         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
12988
12989       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12990         "    JNG alpha compression:%3d",jng_alpha_compression_method);
12991
12992       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12993         "    JNG alpha filter:%8d",0);
12994
12995       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12996         "    JNG alpha interlace:%5d",0);
12997     }
12998
12999   /* Write any JNG-chunk-b profiles */
13000   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
13001
13002   /*
13003      Write leading ancillary chunks
13004   */
13005
13006   if (transparent != 0)
13007   {
13008     /*
13009       Write JNG bKGD chunk
13010     */
13011
13012     unsigned char
13013       blue,
13014       green,
13015       red;
13016
13017     ssize_t
13018       num_bytes;
13019
13020     if (jng_color_type == 8 || jng_color_type == 12)
13021       num_bytes=6L;
13022     else
13023       num_bytes=10L;
13024     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
13025     PNGType(chunk,mng_bKGD);
13026     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
13027     red=ScaleQuantumToChar(image->background_color.red);
13028     green=ScaleQuantumToChar(image->background_color.green);
13029     blue=ScaleQuantumToChar(image->background_color.blue);
13030     *(chunk+4)=0;
13031     *(chunk+5)=red;
13032     *(chunk+6)=0;
13033     *(chunk+7)=green;
13034     *(chunk+8)=0;
13035     *(chunk+9)=blue;
13036     (void) WriteBlob(image,(size_t) num_bytes,chunk);
13037     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
13038   }
13039
13040   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
13041     {
13042       /*
13043         Write JNG sRGB chunk
13044       */
13045       (void) WriteBlobMSBULong(image,1L);
13046       PNGType(chunk,mng_sRGB);
13047       LogPNGChunk(logging,mng_sRGB,1L);
13048
13049       if (image->rendering_intent != UndefinedIntent)
13050         chunk[4]=(unsigned char)
13051           Magick_RenderingIntent_to_PNG_RenderingIntent(
13052           (image->rendering_intent));
13053
13054       else
13055         chunk[4]=(unsigned char)
13056           Magick_RenderingIntent_to_PNG_RenderingIntent(
13057           (PerceptualIntent));
13058
13059       (void) WriteBlob(image,5,chunk);
13060       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13061     }
13062   else
13063     {
13064       if (image->gamma != 0.0)
13065         {
13066           /*
13067              Write JNG gAMA chunk
13068           */
13069           (void) WriteBlobMSBULong(image,4L);
13070           PNGType(chunk,mng_gAMA);
13071           LogPNGChunk(logging,mng_gAMA,4L);
13072           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13073           (void) WriteBlob(image,8,chunk);
13074           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13075         }
13076
13077       if ((mng_info->equal_chrms == MagickFalse) &&
13078           (image->chromaticity.red_primary.x != 0.0))
13079         {
13080           PrimaryInfo
13081             primary;
13082
13083           /*
13084              Write JNG cHRM chunk
13085           */
13086           (void) WriteBlobMSBULong(image,32L);
13087           PNGType(chunk,mng_cHRM);
13088           LogPNGChunk(logging,mng_cHRM,32L);
13089           primary=image->chromaticity.white_point;
13090           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13091           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13092           primary=image->chromaticity.red_primary;
13093           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13094           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13095           primary=image->chromaticity.green_primary;
13096           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13097           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13098           primary=image->chromaticity.blue_primary;
13099           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13100           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13101           (void) WriteBlob(image,36,chunk);
13102           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13103         }
13104     }
13105
13106   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
13107     {
13108       /*
13109          Write JNG pHYs chunk
13110       */
13111       (void) WriteBlobMSBULong(image,9L);
13112       PNGType(chunk,mng_pHYs);
13113       LogPNGChunk(logging,mng_pHYs,9L);
13114       if (image->units == PixelsPerInchResolution)
13115         {
13116           PNGLong(chunk+4,(png_uint_32)
13117             (image->resolution.x*100.0/2.54+0.5));
13118
13119           PNGLong(chunk+8,(png_uint_32)
13120             (image->resolution.y*100.0/2.54+0.5));
13121
13122           chunk[12]=1;
13123         }
13124
13125       else
13126         {
13127           if (image->units == PixelsPerCentimeterResolution)
13128             {
13129               PNGLong(chunk+4,(png_uint_32)
13130                 (image->resolution.x*100.0+0.5));
13131
13132               PNGLong(chunk+8,(png_uint_32)
13133                 (image->resolution.y*100.0+0.5));
13134
13135               chunk[12]=1;
13136             }
13137
13138           else
13139             {
13140               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13141               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13142               chunk[12]=0;
13143             }
13144         }
13145       (void) WriteBlob(image,13,chunk);
13146       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13147     }
13148
13149   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
13150     {
13151       /*
13152          Write JNG oFFs chunk
13153       */
13154       (void) WriteBlobMSBULong(image,9L);
13155       PNGType(chunk,mng_oFFs);
13156       LogPNGChunk(logging,mng_oFFs,9L);
13157       PNGsLong(chunk+4,(ssize_t) (image->page.x));
13158       PNGsLong(chunk+8,(ssize_t) (image->page.y));
13159       chunk[12]=0;
13160       (void) WriteBlob(image,13,chunk);
13161       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13162     }
13163   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
13164     {
13165        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
13166        PNGType(chunk,mng_vpAg);
13167        LogPNGChunk(logging,mng_vpAg,9L);
13168        PNGLong(chunk+4,(png_uint_32) image->page.width);
13169        PNGLong(chunk+8,(png_uint_32) image->page.height);
13170        chunk[12]=0;   /* unit = pixels */
13171        (void) WriteBlob(image,13,chunk);
13172        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13173     }
13174
13175   if (transparent != 0)
13176     {
13177       if (jng_alpha_compression_method==0)
13178         {
13179           register ssize_t
13180             i;
13181
13182           size_t
13183             len;
13184
13185           /* Write IDAT chunk header */
13186           if (logging != MagickFalse)
13187             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13188               "  Write IDAT chunks from blob, length=%.20g.",(double)
13189               length);
13190
13191           /* Copy IDAT chunks */
13192           len=0;
13193           p=blob+8;
13194           for (i=8; i<(ssize_t) length; i+=len+12)
13195           {
13196             len=(size_t) (*p) << 24;
13197             len|=(size_t) (*(p+1)) << 16;
13198             len|=(size_t) (*(p+2)) << 8;
13199             len|=(size_t) (*(p+3));
13200             p+=4;
13201
13202             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
13203               {
13204                 /* Found an IDAT chunk. */
13205                 (void) WriteBlobMSBULong(image,len);
13206                 LogPNGChunk(logging,mng_IDAT,len);
13207                 (void) WriteBlob(image,len+4,p);
13208                 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
13209               }
13210
13211             else
13212               {
13213                 if (logging != MagickFalse)
13214                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13215                     "    Skipping %c%c%c%c chunk, length=%.20g.",
13216                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
13217               }
13218             p+=(8+len);
13219           }
13220         }
13221       else if (length != 0)
13222         {
13223           /* Write JDAA chunk header */
13224           if (logging != MagickFalse)
13225             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13226               "  Write JDAA chunk, length=%.20g.",(double) length);
13227           (void) WriteBlobMSBULong(image,(size_t) length);
13228           PNGType(chunk,mng_JDAA);
13229           LogPNGChunk(logging,mng_JDAA,length);
13230           /* Write JDAT chunk(s) data */
13231           (void) WriteBlob(image,4,chunk);
13232           (void) WriteBlob(image,length,blob);
13233           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
13234              (uInt) length));
13235         }
13236       blob=(unsigned char *) RelinquishMagickMemory(blob);
13237     }
13238
13239   /* Encode image as a JPEG blob */
13240   if (logging != MagickFalse)
13241     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13242       "  Creating jpeg_image_info.");
13243   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
13244   if (jpeg_image_info == (ImageInfo *) NULL)
13245     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13246
13247   if (logging != MagickFalse)
13248     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13249       "  Creating jpeg_image.");
13250
13251   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
13252   if (jpeg_image == (Image *) NULL)
13253     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13254   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13255
13256   (void) AcquireUniqueFilename(jpeg_image->filename);
13257   (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
13258     jpeg_image->filename);
13259
13260   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
13261     exception);
13262
13263   if (logging != MagickFalse)
13264     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13265       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
13266       (double) jpeg_image->rows);
13267
13268   if (status == MagickFalse)
13269     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13270
13271   if (jng_color_type == 8 || jng_color_type == 12)
13272     jpeg_image_info->type=GrayscaleType;
13273
13274   jpeg_image_info->quality=jng_quality;
13275   jpeg_image->quality=jng_quality;
13276   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
13277   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13278
13279   if (logging != MagickFalse)
13280     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13281       "  Creating blob.");
13282
13283   blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
13284     exception);
13285
13286   if (logging != MagickFalse)
13287     {
13288       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13289         "  Successfully read jpeg_image into a blob, length=%.20g.",
13290         (double) length);
13291
13292       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13293         "  Write JDAT chunk, length=%.20g.",(double) length);
13294     }
13295
13296   /* Write JDAT chunk(s) */
13297   (void) WriteBlobMSBULong(image,(size_t) length);
13298   PNGType(chunk,mng_JDAT);
13299   LogPNGChunk(logging,mng_JDAT,length);
13300   (void) WriteBlob(image,4,chunk);
13301   (void) WriteBlob(image,length,blob);
13302   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
13303
13304   jpeg_image=DestroyImage(jpeg_image);
13305   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
13306   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13307   blob=(unsigned char *) RelinquishMagickMemory(blob);
13308
13309   /* Write any JNG-chunk-e profiles */
13310   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
13311
13312   /* Write IEND chunk */
13313   (void) WriteBlobMSBULong(image,0L);
13314   PNGType(chunk,mng_IEND);
13315   LogPNGChunk(logging,mng_IEND,0);
13316   (void) WriteBlob(image,4,chunk);
13317   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13318
13319   if (logging != MagickFalse)
13320     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13321       "  exit WriteOneJNGImage()");
13322
13323   return(status);
13324 }
13325
13326 /*
13327 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13328 %                                                                             %
13329 %                                                                             %
13330 %                                                                             %
13331 %   W r i t e J N G I m a g e                                                 %
13332 %                                                                             %
13333 %                                                                             %
13334 %                                                                             %
13335 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13336 %
13337 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
13338 %
13339 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
13340 %
13341 %  The format of the WriteJNGImage method is:
13342 %
13343 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13344 %        Image *image,ExceptionInfo *exception)
13345 %
13346 %  A description of each parameter follows:
13347 %
13348 %    o image_info: the image info.
13349 %
13350 %    o image:  The image.
13351 %
13352 %    o exception: return any errors or warnings in this structure.
13353 %
13354 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13355 */
13356 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13357   Image *image, ExceptionInfo *exception)
13358 {
13359   MagickBooleanType
13360     logging,
13361     status;
13362
13363   MngInfo
13364     *mng_info;
13365
13366   /*
13367     Open image file.
13368   */
13369   assert(image_info != (const ImageInfo *) NULL);
13370   assert(image_info->signature == MagickCoreSignature);
13371   assert(image != (Image *) NULL);
13372   assert(image->signature == MagickCoreSignature);
13373   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13374   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
13375   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13376   if (status == MagickFalse)
13377     return(status);
13378   if ((image->columns > 65535UL) || (image->rows > 65535UL))
13379     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
13380
13381   /*
13382     Allocate a MngInfo structure.
13383   */
13384   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13385   if (mng_info == (MngInfo *) NULL)
13386     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13387   /*
13388     Initialize members of the MngInfo structure.
13389   */
13390   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13391   mng_info->image=image;
13392
13393   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
13394
13395   status=WriteOneJNGImage(mng_info,image_info,image,exception);
13396   mng_info=MngInfoFreeStruct(mng_info);
13397   (void) CloseBlob(image);
13398
13399   (void) CatchImageException(image);
13400   if (logging != MagickFalse)
13401     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13402   return(status);
13403 }
13404 #endif
13405
13406 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
13407   Image *image, ExceptionInfo *exception)
13408 {
13409   Image
13410     *next_image;
13411
13412   MagickBooleanType
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   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13475   if (mng_info == (MngInfo *) NULL)
13476     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13477   /*
13478     Initialize members of the MngInfo structure.
13479   */
13480   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13481   mng_info->image=image;
13482   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13483
13484   /*
13485    * See if user has requested a specific PNG subformat to be used
13486    * for all of the PNGs in the MNG being written, e.g.,
13487    *
13488    *    convert *.png png8:animation.mng
13489    *
13490    * To do: check -define png:bit_depth and png:color_type as well,
13491    * or perhaps use mng:bit_depth and mng:color_type instead for
13492    * global settings.
13493    */
13494
13495   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13496   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13497   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13498
13499   write_jng=MagickFalse;
13500   if (image_info->compression == JPEGCompression)
13501     write_jng=MagickTrue;
13502
13503   mng_info->adjoin=image_info->adjoin &&
13504     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13505
13506   if (logging != MagickFalse)
13507     {
13508       /* Log some info about the input */
13509       Image
13510         *p;
13511
13512       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13513         "  Checking input image(s)\n"
13514         "    Image_info depth: %.20g,    Type: %d",
13515         (double) image_info->depth, image_info->type);
13516
13517       scene=0;
13518       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13519       {
13520
13521         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13522            "    Scene: %.20g\n,   Image depth: %.20g",
13523            (double) scene++, (double) p->depth);
13524
13525         if (p->alpha_trait != UndefinedPixelTrait)
13526           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13527             "      Matte: True");
13528
13529         else
13530           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13531             "      Matte: False");
13532
13533         if (p->storage_class == PseudoClass)
13534           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13535             "      Storage class: PseudoClass");
13536
13537         else
13538           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13539             "      Storage class: DirectClass");
13540
13541         if (p->colors)
13542           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13543             "      Number of colors: %.20g",(double) p->colors);
13544
13545         else
13546           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13547             "      Number of colors: unspecified");
13548
13549         if (mng_info->adjoin == MagickFalse)
13550           break;
13551       }
13552     }
13553
13554   use_global_plte=MagickFalse;
13555   all_images_are_gray=MagickFalse;
13556 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13557   need_local_plte=MagickTrue;
13558 #endif
13559   need_defi=MagickFalse;
13560   need_matte=MagickFalse;
13561   mng_info->framing_mode=1;
13562   mng_info->old_framing_mode=1;
13563
13564   if (write_mng)
13565       if (image_info->page != (char *) NULL)
13566         {
13567           /*
13568             Determine image bounding box.
13569           */
13570           SetGeometry(image,&mng_info->page);
13571           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13572             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13573         }
13574   if (write_mng)
13575     {
13576       unsigned int
13577         need_geom;
13578
13579       unsigned short
13580         red,
13581         green,
13582         blue;
13583
13584       const char *
13585         option;
13586
13587       mng_info->page=image->page;
13588       need_geom=MagickTrue;
13589       if (mng_info->page.width || mng_info->page.height)
13590          need_geom=MagickFalse;
13591       /*
13592         Check all the scenes.
13593       */
13594       initial_delay=image->delay;
13595       need_iterations=MagickFalse;
13596       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13597       mng_info->equal_physs=MagickTrue,
13598       mng_info->equal_gammas=MagickTrue;
13599       mng_info->equal_srgbs=MagickTrue;
13600       mng_info->equal_backgrounds=MagickTrue;
13601       image_count=0;
13602 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13603     defined(PNG_MNG_FEATURES_SUPPORTED)
13604       all_images_are_gray=MagickTrue;
13605       mng_info->equal_palettes=MagickFalse;
13606       need_local_plte=MagickFalse;
13607 #endif
13608       for (next_image=image; next_image != (Image *) NULL; )
13609       {
13610         if (need_geom)
13611           {
13612             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13613               mng_info->page.width=next_image->columns+next_image->page.x;
13614
13615             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13616               mng_info->page.height=next_image->rows+next_image->page.y;
13617           }
13618
13619         if (next_image->page.x || next_image->page.y)
13620           need_defi=MagickTrue;
13621
13622         if (next_image->alpha_trait != UndefinedPixelTrait)
13623           need_matte=MagickTrue;
13624
13625         if ((int) next_image->dispose >= BackgroundDispose)
13626           if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13627                next_image->page.x || next_image->page.y ||
13628               ((next_image->columns < mng_info->page.width) &&
13629                (next_image->rows < mng_info->page.height)))
13630             mng_info->need_fram=MagickTrue;
13631
13632         if (next_image->iterations)
13633           need_iterations=MagickTrue;
13634
13635         final_delay=next_image->delay;
13636
13637         if (final_delay != initial_delay || final_delay > 1UL*
13638            next_image->ticks_per_second)
13639           mng_info->need_fram=1;
13640
13641 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13642     defined(PNG_MNG_FEATURES_SUPPORTED)
13643         /*
13644           check for global palette possibility.
13645         */
13646         if (image->alpha_trait != UndefinedPixelTrait)
13647            need_local_plte=MagickTrue;
13648
13649         if (need_local_plte == 0)
13650           {
13651             if (SetImageGray(image,exception) == MagickFalse)
13652               all_images_are_gray=MagickFalse;
13653             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13654             if (use_global_plte == 0)
13655               use_global_plte=mng_info->equal_palettes;
13656             need_local_plte=!mng_info->equal_palettes;
13657           }
13658 #endif
13659         if (GetNextImageInList(next_image) != (Image *) NULL)
13660           {
13661             if (next_image->background_color.red !=
13662                 next_image->next->background_color.red ||
13663                 next_image->background_color.green !=
13664                 next_image->next->background_color.green ||
13665                 next_image->background_color.blue !=
13666                 next_image->next->background_color.blue)
13667               mng_info->equal_backgrounds=MagickFalse;
13668
13669             if (next_image->gamma != next_image->next->gamma)
13670               mng_info->equal_gammas=MagickFalse;
13671
13672             if (next_image->rendering_intent !=
13673                 next_image->next->rendering_intent)
13674               mng_info->equal_srgbs=MagickFalse;
13675
13676             if ((next_image->units != next_image->next->units) ||
13677                 (next_image->resolution.x != next_image->next->resolution.x) ||
13678                 (next_image->resolution.y != next_image->next->resolution.y))
13679               mng_info->equal_physs=MagickFalse;
13680
13681             if (mng_info->equal_chrms)
13682               {
13683                 if (next_image->chromaticity.red_primary.x !=
13684                     next_image->next->chromaticity.red_primary.x ||
13685                     next_image->chromaticity.red_primary.y !=
13686                     next_image->next->chromaticity.red_primary.y ||
13687                     next_image->chromaticity.green_primary.x !=
13688                     next_image->next->chromaticity.green_primary.x ||
13689                     next_image->chromaticity.green_primary.y !=
13690                     next_image->next->chromaticity.green_primary.y ||
13691                     next_image->chromaticity.blue_primary.x !=
13692                     next_image->next->chromaticity.blue_primary.x ||
13693                     next_image->chromaticity.blue_primary.y !=
13694                     next_image->next->chromaticity.blue_primary.y ||
13695                     next_image->chromaticity.white_point.x !=
13696                     next_image->next->chromaticity.white_point.x ||
13697                     next_image->chromaticity.white_point.y !=
13698                     next_image->next->chromaticity.white_point.y)
13699                   mng_info->equal_chrms=MagickFalse;
13700               }
13701           }
13702         image_count++;
13703         next_image=GetNextImageInList(next_image);
13704       }
13705       if (image_count < 2)
13706         {
13707           mng_info->equal_backgrounds=MagickFalse;
13708           mng_info->equal_chrms=MagickFalse;
13709           mng_info->equal_gammas=MagickFalse;
13710           mng_info->equal_srgbs=MagickFalse;
13711           mng_info->equal_physs=MagickFalse;
13712           use_global_plte=MagickFalse;
13713 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13714           need_local_plte=MagickTrue;
13715 #endif
13716           need_iterations=MagickFalse;
13717         }
13718
13719      if (mng_info->need_fram == MagickFalse)
13720        {
13721          /*
13722            Only certain framing rates 100/n are exactly representable without
13723            the FRAM chunk but we'll allow some slop in VLC files
13724          */
13725          if (final_delay == 0)
13726            {
13727              if (need_iterations != MagickFalse)
13728                {
13729                  /*
13730                    It's probably a GIF with loop; don't run it *too* fast.
13731                  */
13732                  if (mng_info->adjoin)
13733                    {
13734                      final_delay=10;
13735                      (void) ThrowMagickException(exception,GetMagickModule(),
13736                        CoderWarning,
13737                        "input has zero delay between all frames; assuming",
13738                        " 10 cs `%s'","");
13739                    }
13740                }
13741              else
13742                mng_info->ticks_per_second=0;
13743            }
13744          if (final_delay != 0)
13745            mng_info->ticks_per_second=(png_uint_32)
13746               (image->ticks_per_second/final_delay);
13747          if (final_delay > 50)
13748            mng_info->ticks_per_second=2;
13749
13750          if (final_delay > 75)
13751            mng_info->ticks_per_second=1;
13752
13753          if (final_delay > 125)
13754            mng_info->need_fram=MagickTrue;
13755
13756          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13757             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13758             (final_delay != 25) && (final_delay != 50) &&
13759             (final_delay != (size_t) image->ticks_per_second))
13760            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13761        }
13762
13763      if (mng_info->need_fram != MagickFalse)
13764         mng_info->ticks_per_second=image->ticks_per_second;
13765      /*
13766         If pseudocolor, we should also check to see if all the
13767         palettes are identical and write a global PLTE if they are.
13768         ../glennrp Feb 99.
13769      */
13770      /*
13771         Write the MNG version 1.0 signature and MHDR chunk.
13772      */
13773      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13774      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13775      PNGType(chunk,mng_MHDR);
13776      LogPNGChunk(logging,mng_MHDR,28L);
13777      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13778      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13779      PNGLong(chunk+12,mng_info->ticks_per_second);
13780      PNGLong(chunk+16,0L);  /* layer count=unknown */
13781      PNGLong(chunk+20,0L);  /* frame count=unknown */
13782      PNGLong(chunk+24,0L);  /* play time=unknown   */
13783      if (write_jng)
13784        {
13785          if (need_matte)
13786            {
13787              if (need_defi || mng_info->need_fram || use_global_plte)
13788                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13789
13790              else
13791                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13792            }
13793
13794          else
13795            {
13796              if (need_defi || mng_info->need_fram || use_global_plte)
13797                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13798
13799              else
13800                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13801            }
13802        }
13803
13804      else
13805        {
13806          if (need_matte)
13807            {
13808              if (need_defi || mng_info->need_fram || use_global_plte)
13809                PNGLong(chunk+28,11L);    /* simplicity=LC */
13810
13811              else
13812                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13813            }
13814
13815          else
13816            {
13817              if (need_defi || mng_info->need_fram || use_global_plte)
13818                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13819
13820              else
13821                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13822            }
13823        }
13824      (void) WriteBlob(image,32,chunk);
13825      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13826      option=GetImageOption(image_info,"mng:need-cacheoff");
13827      if (option != (const char *) NULL)
13828        {
13829          size_t
13830            length;
13831          /*
13832            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13833          */
13834          PNGType(chunk,mng_nEED);
13835          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13836          (void) WriteBlobMSBULong(image,(size_t) length);
13837          LogPNGChunk(logging,mng_nEED,(size_t) length);
13838          length+=4;
13839          (void) WriteBlob(image,length,chunk);
13840          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13841        }
13842      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13843          (GetNextImageInList(image) != (Image *) NULL) &&
13844          (image->iterations != 1))
13845        {
13846          /*
13847            Write MNG TERM chunk
13848          */
13849          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13850          PNGType(chunk,mng_TERM);
13851          LogPNGChunk(logging,mng_TERM,10L);
13852          chunk[4]=3;  /* repeat animation */
13853          chunk[5]=0;  /* show last frame when done */
13854          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13855             final_delay/MagickMax(image->ticks_per_second,1)));
13856
13857          if (image->iterations == 0)
13858            PNGLong(chunk+10,PNG_UINT_31_MAX);
13859
13860          else
13861            PNGLong(chunk+10,(png_uint_32) image->iterations);
13862
13863          if (logging != MagickFalse)
13864            {
13865              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13866                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13867               final_delay/MagickMax(image->ticks_per_second,1)));
13868
13869              if (image->iterations == 0)
13870                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13871                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13872
13873              else
13874                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13875                  "     Image iterations: %.20g",(double) image->iterations);
13876            }
13877          (void) WriteBlob(image,14,chunk);
13878          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13879        }
13880      /*
13881        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13882      */
13883      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13884           mng_info->equal_srgbs)
13885        {
13886          /*
13887            Write MNG sRGB chunk
13888          */
13889          (void) WriteBlobMSBULong(image,1L);
13890          PNGType(chunk,mng_sRGB);
13891          LogPNGChunk(logging,mng_sRGB,1L);
13892
13893          if (image->rendering_intent != UndefinedIntent)
13894            chunk[4]=(unsigned char)
13895              Magick_RenderingIntent_to_PNG_RenderingIntent(
13896              (image->rendering_intent));
13897
13898          else
13899            chunk[4]=(unsigned char)
13900              Magick_RenderingIntent_to_PNG_RenderingIntent(
13901                (PerceptualIntent));
13902
13903          (void) WriteBlob(image,5,chunk);
13904          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13905          mng_info->have_write_global_srgb=MagickTrue;
13906        }
13907
13908      else
13909        {
13910          if (image->gamma && mng_info->equal_gammas)
13911            {
13912              /*
13913                 Write MNG gAMA chunk
13914              */
13915              (void) WriteBlobMSBULong(image,4L);
13916              PNGType(chunk,mng_gAMA);
13917              LogPNGChunk(logging,mng_gAMA,4L);
13918              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13919              (void) WriteBlob(image,8,chunk);
13920              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13921              mng_info->have_write_global_gama=MagickTrue;
13922            }
13923          if (mng_info->equal_chrms)
13924            {
13925              PrimaryInfo
13926                primary;
13927
13928              /*
13929                 Write MNG cHRM chunk
13930              */
13931              (void) WriteBlobMSBULong(image,32L);
13932              PNGType(chunk,mng_cHRM);
13933              LogPNGChunk(logging,mng_cHRM,32L);
13934              primary=image->chromaticity.white_point;
13935              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13936              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13937              primary=image->chromaticity.red_primary;
13938              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13939              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13940              primary=image->chromaticity.green_primary;
13941              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13942              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13943              primary=image->chromaticity.blue_primary;
13944              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13945              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13946              (void) WriteBlob(image,36,chunk);
13947              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13948              mng_info->have_write_global_chrm=MagickTrue;
13949            }
13950        }
13951      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13952        {
13953          /*
13954             Write MNG pHYs chunk
13955          */
13956          (void) WriteBlobMSBULong(image,9L);
13957          PNGType(chunk,mng_pHYs);
13958          LogPNGChunk(logging,mng_pHYs,9L);
13959
13960          if (image->units == PixelsPerInchResolution)
13961            {
13962              PNGLong(chunk+4,(png_uint_32)
13963                (image->resolution.x*100.0/2.54+0.5));
13964
13965              PNGLong(chunk+8,(png_uint_32)
13966                (image->resolution.y*100.0/2.54+0.5));
13967
13968              chunk[12]=1;
13969            }
13970
13971          else
13972            {
13973              if (image->units == PixelsPerCentimeterResolution)
13974                {
13975                  PNGLong(chunk+4,(png_uint_32)
13976                    (image->resolution.x*100.0+0.5));
13977
13978                  PNGLong(chunk+8,(png_uint_32)
13979                    (image->resolution.y*100.0+0.5));
13980
13981                  chunk[12]=1;
13982                }
13983
13984              else
13985                {
13986                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13987                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13988                  chunk[12]=0;
13989                }
13990            }
13991          (void) WriteBlob(image,13,chunk);
13992          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13993        }
13994      /*
13995        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13996        or does not cover the entire frame.
13997      */
13998      if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13999          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
14000          (image->page.width+image->page.x < mng_info->page.width))
14001          || (image->page.height && (image->page.height+image->page.y
14002          < mng_info->page.height))))
14003        {
14004          (void) WriteBlobMSBULong(image,6L);
14005          PNGType(chunk,mng_BACK);
14006          LogPNGChunk(logging,mng_BACK,6L);
14007          red=ScaleQuantumToShort(image->background_color.red);
14008          green=ScaleQuantumToShort(image->background_color.green);
14009          blue=ScaleQuantumToShort(image->background_color.blue);
14010          PNGShort(chunk+4,red);
14011          PNGShort(chunk+6,green);
14012          PNGShort(chunk+8,blue);
14013          (void) WriteBlob(image,10,chunk);
14014          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
14015          if (mng_info->equal_backgrounds)
14016            {
14017              (void) WriteBlobMSBULong(image,6L);
14018              PNGType(chunk,mng_bKGD);
14019              LogPNGChunk(logging,mng_bKGD,6L);
14020              (void) WriteBlob(image,10,chunk);
14021              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
14022            }
14023        }
14024
14025 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
14026      if ((need_local_plte == MagickFalse) &&
14027          (image->storage_class == PseudoClass) &&
14028          (all_images_are_gray == MagickFalse))
14029        {
14030          size_t
14031            data_length;
14032
14033          /*
14034            Write MNG PLTE chunk
14035          */
14036          data_length=3*image->colors;
14037          (void) WriteBlobMSBULong(image,data_length);
14038          PNGType(chunk,mng_PLTE);
14039          LogPNGChunk(logging,mng_PLTE,data_length);
14040
14041          for (i=0; i < (ssize_t) image->colors; i++)
14042          {
14043            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
14044              image->colormap[i].red) & 0xff);
14045            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
14046              image->colormap[i].green) & 0xff);
14047            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
14048              image->colormap[i].blue) & 0xff);
14049          }
14050
14051          (void) WriteBlob(image,data_length+4,chunk);
14052          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
14053          mng_info->have_write_global_plte=MagickTrue;
14054        }
14055 #endif
14056     }
14057   scene=0;
14058   mng_info->delay=0;
14059 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14060     defined(PNG_MNG_FEATURES_SUPPORTED)
14061   mng_info->equal_palettes=MagickFalse;
14062 #endif
14063   do
14064   {
14065     if (mng_info->adjoin)
14066     {
14067 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14068     defined(PNG_MNG_FEATURES_SUPPORTED)
14069     /*
14070       If we aren't using a global palette for the entire MNG, check to
14071       see if we can use one for two or more consecutive images.
14072     */
14073     if (need_local_plte && use_global_plte && !all_images_are_gray)
14074       {
14075         if (mng_info->IsPalette)
14076           {
14077             /*
14078               When equal_palettes is true, this image has the same palette
14079               as the previous PseudoClass image
14080             */
14081             mng_info->have_write_global_plte=mng_info->equal_palettes;
14082             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
14083             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
14084               {
14085                 /*
14086                   Write MNG PLTE chunk
14087                 */
14088                 size_t
14089                   data_length;
14090
14091                 data_length=3*image->colors;
14092                 (void) WriteBlobMSBULong(image,data_length);
14093                 PNGType(chunk,mng_PLTE);
14094                 LogPNGChunk(logging,mng_PLTE,data_length);
14095
14096                 for (i=0; i < (ssize_t) image->colors; i++)
14097                 {
14098                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
14099                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
14100                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
14101                 }
14102
14103                 (void) WriteBlob(image,data_length+4,chunk);
14104                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
14105                    (uInt) (data_length+4)));
14106                 mng_info->have_write_global_plte=MagickTrue;
14107               }
14108           }
14109         else
14110           mng_info->have_write_global_plte=MagickFalse;
14111       }
14112 #endif
14113     if (need_defi)
14114       {
14115         ssize_t
14116           previous_x,
14117           previous_y;
14118
14119         if (scene)
14120           {
14121             previous_x=mng_info->page.x;
14122             previous_y=mng_info->page.y;
14123           }
14124         else
14125           {
14126             previous_x=0;
14127             previous_y=0;
14128           }
14129         mng_info->page=image->page;
14130         if ((mng_info->page.x !=  previous_x) ||
14131             (mng_info->page.y != previous_y))
14132           {
14133              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
14134              PNGType(chunk,mng_DEFI);
14135              LogPNGChunk(logging,mng_DEFI,12L);
14136              chunk[4]=0; /* object 0 MSB */
14137              chunk[5]=0; /* object 0 LSB */
14138              chunk[6]=0; /* visible  */
14139              chunk[7]=0; /* abstract */
14140              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
14141              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
14142              (void) WriteBlob(image,16,chunk);
14143              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
14144           }
14145       }
14146     }
14147
14148    mng_info->write_mng=write_mng;
14149
14150    if ((int) image->dispose >= 3)
14151      mng_info->framing_mode=3;
14152
14153    if (mng_info->need_fram && mng_info->adjoin &&
14154        ((image->delay != mng_info->delay) ||
14155         (mng_info->framing_mode != mng_info->old_framing_mode)))
14156      {
14157        if (image->delay == mng_info->delay)
14158          {
14159            /*
14160              Write a MNG FRAM chunk with the new framing mode.
14161            */
14162            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
14163            PNGType(chunk,mng_FRAM);
14164            LogPNGChunk(logging,mng_FRAM,1L);
14165            chunk[4]=(unsigned char) mng_info->framing_mode;
14166            (void) WriteBlob(image,5,chunk);
14167            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
14168          }
14169        else
14170          {
14171            /*
14172              Write a MNG FRAM chunk with the delay.
14173            */
14174            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
14175            PNGType(chunk,mng_FRAM);
14176            LogPNGChunk(logging,mng_FRAM,10L);
14177            chunk[4]=(unsigned char) mng_info->framing_mode;
14178            chunk[5]=0;  /* frame name separator (no name) */
14179            chunk[6]=2;  /* flag for changing default delay */
14180            chunk[7]=0;  /* flag for changing frame timeout */
14181            chunk[8]=0;  /* flag for changing frame clipping */
14182            chunk[9]=0;  /* flag for changing frame sync_id */
14183            PNGLong(chunk+10,(png_uint_32)
14184              ((mng_info->ticks_per_second*
14185              image->delay)/MagickMax(image->ticks_per_second,1)));
14186            (void) WriteBlob(image,14,chunk);
14187            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
14188            mng_info->delay=(png_uint_32) image->delay;
14189          }
14190        mng_info->old_framing_mode=mng_info->framing_mode;
14191      }
14192
14193 #if defined(JNG_SUPPORTED)
14194    if (image_info->compression == JPEGCompression)
14195      {
14196        ImageInfo
14197          *write_info;
14198
14199        if (logging != MagickFalse)
14200          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14201            "  Writing JNG object.");
14202        /* To do: specify the desired alpha compression method. */
14203        write_info=CloneImageInfo(image_info);
14204        write_info->compression=UndefinedCompression;
14205        status=WriteOneJNGImage(mng_info,write_info,image,exception);
14206        write_info=DestroyImageInfo(write_info);
14207      }
14208    else
14209 #endif
14210      {
14211        if (logging != MagickFalse)
14212          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14213            "  Writing PNG object.");
14214
14215        mng_info->need_blob = MagickFalse;
14216        mng_info->ping_preserve_colormap = MagickFalse;
14217
14218        /* We don't want any ancillary chunks written */
14219        mng_info->ping_exclude_bKGD=MagickTrue;
14220        mng_info->ping_exclude_caNv=MagickTrue;
14221        mng_info->ping_exclude_cHRM=MagickTrue;
14222        mng_info->ping_exclude_date=MagickTrue;
14223        mng_info->ping_exclude_EXIF=MagickTrue;
14224        mng_info->ping_exclude_gAMA=MagickTrue;
14225        mng_info->ping_exclude_iCCP=MagickTrue;
14226        /* mng_info->ping_exclude_iTXt=MagickTrue; */
14227        mng_info->ping_exclude_oFFs=MagickTrue;
14228        mng_info->ping_exclude_pHYs=MagickTrue;
14229        mng_info->ping_exclude_sRGB=MagickTrue;
14230        mng_info->ping_exclude_tEXt=MagickTrue;
14231        mng_info->ping_exclude_tRNS=MagickTrue;
14232        mng_info->ping_exclude_vpAg=MagickTrue;
14233        mng_info->ping_exclude_zCCP=MagickTrue;
14234        mng_info->ping_exclude_zTXt=MagickTrue;
14235
14236        status=WriteOnePNGImage(mng_info,image_info,image,exception);
14237      }
14238
14239     if (status == MagickFalse)
14240       {
14241         mng_info=MngInfoFreeStruct(mng_info);
14242         (void) CloseBlob(image);
14243         return(MagickFalse);
14244       }
14245     (void) CatchImageException(image);
14246     if (GetNextImageInList(image) == (Image *) NULL)
14247       break;
14248     image=SyncNextImageInList(image);
14249     status=SetImageProgress(image,SaveImagesTag,scene++,
14250       GetImageListLength(image));
14251
14252     if (status == MagickFalse)
14253       break;
14254
14255   } while (mng_info->adjoin);
14256
14257   if (write_mng)
14258     {
14259       while (GetPreviousImageInList(image) != (Image *) NULL)
14260         image=GetPreviousImageInList(image);
14261       /*
14262         Write the MEND chunk.
14263       */
14264       (void) WriteBlobMSBULong(image,0x00000000L);
14265       PNGType(chunk,mng_MEND);
14266       LogPNGChunk(logging,mng_MEND,0L);
14267       (void) WriteBlob(image,4,chunk);
14268       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
14269     }
14270   /*
14271     Relinquish resources.
14272   */
14273   (void) CloseBlob(image);
14274   mng_info=MngInfoFreeStruct(mng_info);
14275
14276   if (logging != MagickFalse)
14277     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
14278
14279   return(MagickTrue);
14280 }
14281 #else /* PNG_LIBPNG_VER > 10011 */
14282
14283 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
14284   Image *image)
14285 {
14286   (void) image;
14287   printf("Your PNG library is too old: You have libpng-%s\n",
14288      PNG_LIBPNG_VER_STRING);
14289
14290   ThrowBinaryException(CoderError,"PNG library is too old",
14291      image_info->filename);
14292 }
14293
14294 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
14295   Image *image)
14296 {
14297   return(WritePNGImage(image_info,image));
14298 }
14299 #endif /* PNG_LIBPNG_VER > 10011 */
14300 #endif