]> granicus.if.org Git - imagemagick/blob - coders/png.c
(no commit message)
[imagemagick] / coders / png.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            PPPP   N   N   GGGG                              %
7 %                            P   P  NN  N  G                                  %
8 %                            PPPP   N N N  G  GG                              %
9 %                            P      N  NN  G   G                              %
10 %                            P      N   N   GGG                               %
11 %                                                                             %
12 %                                                                             %
13 %              Read/Write Portable Network Graphics Image Format              %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                           Glenn Randers-Pehrson                             %
18 %                               November 1997                                 %
19 %                                                                             %
20 %                                                                             %
21 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
22 %  dedicated to making software imaging solutions freely available.           %
23 %                                                                             %
24 %  You may not use this file except in compliance with the License.  You may  %
25 %  obtain a copy of the License at                                            %
26 %                                                                             %
27 %    http://www.imagemagick.org/script/license.php                            %
28 %                                                                             %
29 %  Unless required by applicable law or agreed to in writing, software        %
30 %  distributed under the License is distributed on an "AS IS" BASIS,          %
31 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32 %  See the License for the specific language governing permissions and        %
33 %  limitations under the License.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39
40 \f
41 /*
42   Include declarations.
43 */
44 #include "magick/studio.h"
45 #include "magick/artifact.h"
46 #include "magick/attribute.h"
47 #include "magick/blob.h"
48 #include "magick/blob-private.h"
49 #include "magick/cache.h"
50 #include "magick/color.h"
51 #include "magick/color-private.h"
52 #include "magick/colormap.h"
53 #include "magick/colorspace.h"
54 #include "magick/constitute.h"
55 #include "magick/enhance.h"
56 #include "magick/exception.h"
57 #include "magick/exception-private.h"
58 #include "magick/geometry.h"
59 #include "magick/histogram.h"
60 #include "magick/image.h"
61 #include "magick/image-private.h"
62 #include "magick/layer.h"
63 #include "magick/list.h"
64 #include "magick/log.h"
65 #include "magick/magick.h"
66 #include "magick/memory_.h"
67 #include "magick/module.h"
68 #include "magick/monitor.h"
69 #include "magick/monitor-private.h"
70 #include "magick/option.h"
71 #include "magick/quantum-private.h"
72 #include "magick/profile.h"
73 #include "magick/property.h"
74 #include "magick/resource_.h"
75 #include "magick/semaphore.h"
76 #include "magick/quantum-private.h"
77 #include "magick/static.h"
78 #include "magick/statistic.h"
79 #include "magick/string_.h"
80 #include "magick/string-private.h"
81 #include "magick/transform.h"
82 #include "magick/utility.h"
83 #if defined(MAGICKCORE_PNG_DELEGATE)
84
85 /* Suppress libpng pedantic warnings that were added in
86  * libpng-1.2.41 and libpng-1.4.0.  If you are working on
87  * migration to libpng-1.5, remove these defines and then
88  * fix any code that generates warnings.
89  */
90 /* #define PNG_DEPRECATED   Use of this function is deprecated */
91 /* #define PNG_USE_RESULT   The result of this function must be checked */
92 /* #define PNG_NORETURN     This function does not return */
93 /* #define PNG_ALLOCATED    The result of the function is new memory */
94 /* #define PNG_DEPSTRUCT    Access to this struct member is deprecated */
95
96 #include "png.h"
97 #include "zlib.h"
98 \f
99 /* ImageMagick differences */
100 #define first_scene scene
101
102 #if PNG_LIBPNG_VER > 10011
103 /*
104   Optional declarations. Define or undefine them as you like.
105 */
106 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
107
108 /*
109   Features under construction.  Define these to work on them.
110 */
111 #undef MNG_OBJECT_BUFFERS
112 #undef MNG_BASI_SUPPORTED
113 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
114 #define MNG_INSERT_LAYERS   /* Troublesome, but seem to work as of 5.4.4 */
115 #if defined(MAGICKCORE_JPEG_DELEGATE)
116 #  define JNG_SUPPORTED /* Not finished as of 5.5.2.  See "To do" comments. */
117 #endif
118 #if !defined(RGBColorMatchExact)
119 #define IsPNGColorEqual(color,target) \
120    (((color).red == (target).red) && \
121     ((color).green == (target).green) && \
122     ((color).blue == (target).blue))
123 #endif
124
125 /*
126   Establish thread safety.
127   setjmp/longjmp is claimed to be safe on these platforms:
128   setjmp/longjmp is alleged to be unsafe on these platforms:
129 */
130 #ifndef SETJMP_IS_THREAD_SAFE
131 #define PNG_SETJMP_NOT_THREAD_SAFE
132 #endif
133
134 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
135 static SemaphoreInfo
136   *ping_semaphore = (SemaphoreInfo *) NULL;
137 #endif
138
139 /*
140   This temporary until I set up malloc'ed object attributes array.
141   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
142   waste more memory.
143 */
144 #define MNG_MAX_OBJECTS 256
145
146 /*
147   If this not defined, spec is interpreted strictly.  If it is
148   defined, an attempt will be made to recover from some errors,
149   including
150       o global PLTE too short
151 */
152 #undef MNG_LOOSE
153
154 /*
155   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
156   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
157   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
158   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
159   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
160   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
161   will be enabled by default in libpng-1.2.0.
162 */
163 #ifdef PNG_MNG_FEATURES_SUPPORTED
164 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
165 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
166 #  endif
167 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
168 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
169 #  endif
170 #endif
171
172 /*
173   Maximum valid size_t in PNG/MNG chunks is (2^31)-1
174   This macro is only defined in libpng-1.0.3 and later.
175   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
176 */
177 #ifndef PNG_UINT_31_MAX
178 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
179 #endif
180
181 /*
182   Constant strings for known chunk types.  If you need to add a chunk,
183   add a string holding the name here.   To make the code more
184   portable, we use ASCII numbers like this, not characters.
185 */
186
187 static png_byte FARDATA mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
188 static png_byte FARDATA mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
189 static png_byte FARDATA mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
190 static png_byte FARDATA mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
191 static png_byte FARDATA mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
192 static png_byte FARDATA mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
193 static png_byte FARDATA mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
194 static png_byte FARDATA mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
195 static png_byte FARDATA mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
196 static png_byte FARDATA mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
197 static png_byte FARDATA mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
198 static png_byte FARDATA mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
199 static png_byte FARDATA mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
200 static png_byte FARDATA mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
201 static png_byte FARDATA mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
202 static png_byte FARDATA mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
203 static png_byte FARDATA mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
204 static png_byte FARDATA mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
205 static png_byte FARDATA mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
206 static png_byte FARDATA mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
207 static png_byte FARDATA mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
208 static png_byte FARDATA mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
209 static png_byte FARDATA mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
210 static png_byte FARDATA mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
211 static png_byte FARDATA mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
212 static png_byte FARDATA mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
213 static png_byte FARDATA mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
214 static png_byte FARDATA mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
215 static png_byte FARDATA mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
216 static png_byte FARDATA mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
217 static png_byte FARDATA mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
218 static png_byte FARDATA mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
219 static png_byte FARDATA mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
220 static png_byte FARDATA mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
221
222 #if defined(JNG_SUPPORTED)
223 static png_byte FARDATA mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
224 static png_byte FARDATA mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
225 static png_byte FARDATA mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
226 static png_byte FARDATA mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
227 static png_byte FARDATA mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
228 static png_byte FARDATA mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
229 #endif
230
231 /*
232 Other known chunks that are not yet supported by ImageMagick:
233 static png_byte FARDATA mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
234 static png_byte FARDATA mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
235 static png_byte FARDATA mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
236 static png_byte FARDATA mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
237 static png_byte FARDATA mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
238 static png_byte FARDATA mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
239 static png_byte FARDATA mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
240 static png_byte FARDATA mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
241 */
242
243 typedef struct _MngBox
244 {
245   long
246     left,
247     right,
248     top,
249     bottom;
250 } MngBox;
251
252 typedef struct _MngPair
253 {
254   volatile long
255     a,
256     b;
257 } MngPair;
258
259 #ifdef MNG_OBJECT_BUFFERS
260 typedef struct _MngBuffer
261 {
262
263   size_t
264     height,
265     width;
266
267   Image
268     *image;
269
270   png_color
271     plte[256];
272
273   int
274     reference_count;
275
276   unsigned char
277     alpha_sample_depth,
278     compression_method,
279     color_type,
280     concrete,
281     filter_method,
282     frozen,
283     image_type,
284     interlace_method,
285     pixel_sample_depth,
286     plte_length,
287     sample_depth,
288     viewable;
289 } MngBuffer;
290 #endif
291
292 typedef struct _MngInfo
293 {
294
295 #ifdef MNG_OBJECT_BUFFERS
296   MngBuffer
297     *ob[MNG_MAX_OBJECTS];
298 #endif
299
300   Image *
301     image;
302
303   RectangleInfo
304     page;
305
306   int
307     adjoin,
308 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
309     bytes_in_read_buffer,
310     found_empty_plte,
311 #endif
312     equal_backgrounds,
313     equal_chrms,
314     equal_gammas,
315 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
316     defined(PNG_MNG_FEATURES_SUPPORTED)
317     equal_palettes,
318 #endif
319     equal_physs,
320     equal_srgbs,
321     framing_mode,
322     have_global_bkgd,
323     have_global_chrm,
324     have_global_gama,
325     have_global_phys,
326     have_global_sbit,
327     have_global_srgb,
328     have_saved_bkgd_index,
329     have_write_global_chrm,
330     have_write_global_gama,
331     have_write_global_plte,
332     have_write_global_srgb,
333     need_fram,
334     object_id,
335     old_framing_mode,
336     saved_bkgd_index;
337
338   int
339     new_number_colors;
340
341   ssize_t
342     image_found,
343     loop_count[256],
344     loop_iteration[256],
345     scenes_found,
346     x_off[MNG_MAX_OBJECTS],
347     y_off[MNG_MAX_OBJECTS];
348
349   MngBox
350     clip,
351     frame,
352     image_box,
353     object_clip[MNG_MAX_OBJECTS];
354
355   unsigned char
356     /* These flags could be combined into one byte */
357     exists[MNG_MAX_OBJECTS],
358     frozen[MNG_MAX_OBJECTS],
359     loop_active[256],
360     invisible[MNG_MAX_OBJECTS],
361     viewable[MNG_MAX_OBJECTS];
362
363   MagickOffsetType
364     loop_jump[256];
365
366   png_colorp
367     global_plte;
368
369   png_color_8
370     global_sbit;
371
372   png_byte
373 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
374     read_buffer[8],
375 #endif
376     global_trns[256];
377
378   float
379     global_gamma;
380
381   ChromaticityInfo
382     global_chrm;
383
384   RenderingIntent
385     global_srgb_intent;
386
387   unsigned int
388     delay,
389     global_plte_length,
390     global_trns_length,
391     global_x_pixels_per_unit,
392     global_y_pixels_per_unit,
393     mng_width,
394     mng_height,
395     ticks_per_second;
396
397   MagickBooleanType
398     need_blob;
399
400   unsigned int
401     IsPalette,
402     global_phys_unit_type,
403     basi_warning,
404     clon_warning,
405     dhdr_warning,
406     jhdr_warning,
407     magn_warning,
408     past_warning,
409     phyg_warning,
410     phys_warning,
411     sbit_warning,
412     show_warning,
413     mng_type,
414     write_mng,
415     write_png_colortype,
416     write_png_depth,
417     write_png8,
418     write_png24,
419     write_png32;
420
421 #ifdef MNG_BASI_SUPPORTED
422   size_t
423     basi_width,
424     basi_height;
425
426   unsigned int
427     basi_depth,
428     basi_color_type,
429     basi_compression_method,
430     basi_filter_type,
431     basi_interlace_method,
432     basi_red,
433     basi_green,
434     basi_blue,
435     basi_alpha,
436     basi_viewable;
437 #endif
438
439   png_uint_16
440     magn_first,
441     magn_last,
442     magn_mb,
443     magn_ml,
444     magn_mr,
445     magn_mt,
446     magn_mx,
447     magn_my,
448     magn_methx,
449     magn_methy;
450
451   PixelPacket
452     mng_global_bkgd;
453
454   /* Added at version 6.6.6-7 */
455   MagickBooleanType
456     ping_exclude_bKGD,
457     ping_exclude_cHRM,
458     ping_exclude_EXIF,
459     ping_exclude_gAMA,
460     ping_exclude_iCCP,
461     /* ping_exclude_iTXt, */
462     ping_exclude_oFFs,
463     ping_exclude_pHYs,
464     ping_exclude_sRGB,
465     ping_exclude_tEXt,
466     ping_exclude_tRNS,
467     ping_exclude_vpAg,
468     ping_exclude_zCCP, /* hex-encoded iCCP */
469     ping_exclude_zTXt;
470
471 } MngInfo;
472 #endif /* VER */
473 \f
474 /*
475   Forward declarations.
476 */
477 static MagickBooleanType
478   WritePNGImage(const ImageInfo *,Image *);
479
480 static MagickBooleanType
481   WriteMNGImage(const ImageInfo *,Image *);
482
483 #if defined(JNG_SUPPORTED)
484 static MagickBooleanType
485   WriteJNGImage(const ImageInfo *,Image *);
486 #endif
487
488 #if PNG_LIBPNG_VER > 10011
489
490
491 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
492 static MagickBooleanType
493 LosslessReduceDepthOK(Image *image)
494 {
495     MagickBooleanType
496       ok_to_reduce=MagickFalse;
497
498     /* Reduce bit depth if it can be reduced losslessly from 16 to 8.
499      * Note that the method GetImageDepth doesn't check background
500      * and doesn't handle PseudoClass specially.  Also it uses
501      * multiplication and division by 257 instead of shifting, so
502      * might be slower.
503      */
504
505     if (image->depth == 16)
506       {
507
508         const PixelPacket
509           *p;
510
511         ok_to_reduce=
512           (((((size_t) image->background_color.red >> 8) & 0xff)
513           == ((size_t) image->background_color.red & 0xff)) &&
514            ((((size_t) image->background_color.green >> 8) & 0xff)
515           == ((size_t) image->background_color.green & 0xff)) &&
516            ((((size_t) image->background_color.blue >> 8) & 0xff)
517           == ((size_t) image->background_color.blue & 0xff))) ? MagickTrue :
518           MagickFalse;
519
520         if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
521           {
522             int indx;
523
524             for (indx=0; indx < (ssize_t) image->colors; indx++)
525               {
526                 ok_to_reduce=(((((size_t) image->colormap[indx].red >>
527                     8) & 0xff)
528                   == ((size_t) image->colormap[indx].red & 0xff)) &&
529                   ((((size_t) image->colormap[indx].green >> 8) & 0xff)
530                   == ((size_t) image->colormap[indx].green & 0xff)) &&
531                   ((((size_t) image->colormap[indx].blue >> 8) & 0xff)
532                   == ((size_t) image->colormap[indx].blue & 0xff)) &&
533                   (image->matte == MagickFalse ||
534                   (((size_t) image->colormap[indx].opacity >> 8) & 0xff)
535                   == ((size_t) image->colormap[indx].opacity & 0xff))) ?
536                   MagickTrue : MagickFalse;
537                 if (ok_to_reduce == MagickFalse)
538                   break;
539               }
540           }
541
542         if ((ok_to_reduce != MagickFalse) &&
543             (image->storage_class != PseudoClass))
544           {
545             ssize_t
546               y;
547
548             register ssize_t
549               x;
550
551             for (y=0; y < (ssize_t) image->rows; y++)
552             {
553               p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
554
555               if (p == (const PixelPacket *) NULL)
556                 {
557                   ok_to_reduce = MagickFalse;
558                   break;
559                 }
560
561               for (x=(ssize_t) image->columns-1; x >= 0; x--)
562               {
563                 ok_to_reduce=((
564                   (((size_t) p->red >> 8) & 0xff) ==
565                   ((size_t) p->red & 0xff)) &&
566                   ((((size_t) p->green >> 8) & 0xff) ==
567                   ((size_t) p->green & 0xff)) &&
568                   ((((size_t) p->blue >> 8) & 0xff) ==
569                   ((size_t) p->blue & 0xff)) &&
570                   (((image->matte == MagickFalse ||
571                   (((size_t) p->opacity >> 8) & 0xff) ==
572                   ((size_t) p->opacity & 0xff))))) ? MagickTrue : MagickFalse;
573
574                 if (ok_to_reduce == MagickFalse)
575                   break;
576
577                 p++;
578               }
579               if (x >= 0)
580                 break;
581             }
582           }
583
584         if (ok_to_reduce != MagickFalse)
585           {
586             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
587                 "    OK to reduce PNG bit depth to 8 without loss of info");
588           }
589         else
590           {
591             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
592                 "    Not OK to reduce PNG bit depth to 8 without loss of info");
593           }
594       }
595
596     return ok_to_reduce;
597 }
598 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
599
600 static int
601 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
602 {
603   switch (intent)
604   {
605     case PerceptualIntent:
606        return 0;
607
608     case RelativeIntent:
609        return 1;
610
611     case SaturationIntent:
612        return 2;
613
614     case AbsoluteIntent:
615        return 3;
616
617     default:
618        return -1;
619   }
620 }
621
622 static RenderingIntent
623 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
624 {
625   switch (ping_intent)
626   {
627     case 0:
628       return PerceptualIntent;
629
630     case 1:
631       return RelativeIntent;
632
633     case 2:
634       return SaturationIntent;
635
636     case 3:
637       return AbsoluteIntent;
638
639     default:
640       return UndefinedIntent;
641     }
642 }
643
644 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
645 {
646   if (x > y)
647     return(x);
648
649   return(y);
650 }
651
652 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
653 {
654   if (x < y)
655     return(x);
656
657   return(y);
658 }
659
660 \f
661 /*
662 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
663 %                                                                             %
664 %                                                                             %
665 %                                                                             %
666 %   I m a g e I s G r a y                                                     %
667 %                                                                             %
668 %                                                                             %
669 %                                                                             %
670 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
671 %                                                                             %
672 %   Like IsGrayImage except does not change DirectClass to PseudoClass        %
673 %                                                                             %
674 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
675 */
676 static MagickBooleanType ImageIsGray(Image *image)
677 {
678   register const PixelPacket
679     *p;
680
681   register ssize_t
682     i,
683     x,
684     y;
685
686   assert(image != (Image *) NULL);
687   assert(image->signature == MagickSignature);
688   if (image->debug != MagickFalse)
689     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
690
691   if (image->storage_class == PseudoClass)
692     {
693       for (i=0; i < (ssize_t) image->colors; i++)
694         if (IsGray(image->colormap+i) == MagickFalse)
695           return(MagickFalse);
696       return(MagickTrue);
697     }
698   for (y=0; y < (ssize_t) image->rows; y++)
699   {
700     p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
701     if (p == (const PixelPacket *) NULL)
702       return(MagickFalse);
703     for (x=(ssize_t) image->columns-1; x >= 0; x--)
704     {
705        if (IsGray(p) == MagickFalse)
706           return(MagickFalse);
707        p++;
708     }
709   }
710   return(MagickTrue);
711 }
712 #endif /* PNG_LIBPNG_VER > 10011 */
713 #endif /* MAGICKCORE_PNG_DELEGATE */
714 \f
715 /*
716 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
717 %                                                                             %
718 %                                                                             %
719 %                                                                             %
720 %   I s M N G                                                                 %
721 %                                                                             %
722 %                                                                             %
723 %                                                                             %
724 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
725 %
726 %  IsMNG() returns MagickTrue if the image format type, identified by the
727 %  magick string, is MNG.
728 %
729 %  The format of the IsMNG method is:
730 %
731 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
732 %
733 %  A description of each parameter follows:
734 %
735 %    o magick: compare image format pattern against these bytes.
736 %
737 %    o length: Specifies the length of the magick string.
738 %
739 %
740 */
741 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
742 {
743   if (length < 8)
744     return(MagickFalse);
745
746   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
747     return(MagickTrue);
748
749   return(MagickFalse);
750 }
751 \f
752 /*
753 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
754 %                                                                             %
755 %                                                                             %
756 %                                                                             %
757 %   I s J N G                                                                 %
758 %                                                                             %
759 %                                                                             %
760 %                                                                             %
761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
762 %
763 %  IsJNG() returns MagickTrue if the image format type, identified by the
764 %  magick string, is JNG.
765 %
766 %  The format of the IsJNG method is:
767 %
768 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
769 %
770 %  A description of each parameter follows:
771 %
772 %    o magick: compare image format pattern against these bytes.
773 %
774 %    o length: Specifies the length of the magick string.
775 %
776 %
777 */
778 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
779 {
780   if (length < 8)
781     return(MagickFalse);
782
783   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
784     return(MagickTrue);
785
786   return(MagickFalse);
787 }
788 \f
789 /*
790 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
791 %                                                                             %
792 %                                                                             %
793 %                                                                             %
794 %   I s P N G                                                                 %
795 %                                                                             %
796 %                                                                             %
797 %                                                                             %
798 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
799 %
800 %  IsPNG() returns MagickTrue if the image format type, identified by the
801 %  magick string, is PNG.
802 %
803 %  The format of the IsPNG method is:
804 %
805 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
806 %
807 %  A description of each parameter follows:
808 %
809 %    o magick: compare image format pattern against these bytes.
810 %
811 %    o length: Specifies the length of the magick string.
812 %
813 */
814 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
815 {
816   if (length < 8)
817     return(MagickFalse);
818
819   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
820     return(MagickTrue);
821
822   return(MagickFalse);
823 }
824 \f
825 #if defined(MAGICKCORE_PNG_DELEGATE)
826 #if defined(__cplusplus) || defined(c_plusplus)
827 extern "C" {
828 #endif
829
830 #if (PNG_LIBPNG_VER > 10011)
831 static size_t WriteBlobMSBULong(Image *image,const size_t value)
832 {
833   unsigned char
834     buffer[4];
835
836   assert(image != (Image *) NULL);
837   assert(image->signature == MagickSignature);
838   buffer[0]=(unsigned char) (value >> 24);
839   buffer[1]=(unsigned char) (value >> 16);
840   buffer[2]=(unsigned char) (value >> 8);
841   buffer[3]=(unsigned char) value;
842   return((size_t) WriteBlob(image,4,buffer));
843 }
844
845 static void PNGLong(png_bytep p,png_uint_32 value)
846 {
847   *p++=(png_byte) ((value >> 24) & 0xff);
848   *p++=(png_byte) ((value >> 16) & 0xff);
849   *p++=(png_byte) ((value >> 8) & 0xff);
850   *p++=(png_byte) (value & 0xff);
851 }
852
853 #if defined(JNG_SUPPORTED)
854 static void PNGsLong(png_bytep p,png_int_32 value)
855 {
856   *p++=(png_byte) ((value >> 24) & 0xff);
857   *p++=(png_byte) ((value >> 16) & 0xff);
858   *p++=(png_byte) ((value >> 8) & 0xff);
859   *p++=(png_byte) (value & 0xff);
860 }
861 #endif
862
863 static void PNGShort(png_bytep p,png_uint_16 value)
864 {
865   *p++=(png_byte) ((value >> 8) & 0xff);
866   *p++=(png_byte) (value & 0xff);
867 }
868
869 static void PNGType(png_bytep p,png_bytep type)
870 {
871   (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
872 }
873
874 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
875    size_t length)
876 {
877   if (logging != MagickFalse)
878     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
879       "  Writing %c%c%c%c chunk, length: %.20g",
880       type[0],type[1],type[2],type[3],(double) length);
881 }
882 #endif /* PNG_LIBPNG_VER > 10011 */
883
884 #if defined(__cplusplus) || defined(c_plusplus)
885 }
886 #endif
887
888 #if PNG_LIBPNG_VER > 10011
889 /*
890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
891 %                                                                             %
892 %                                                                             %
893 %                                                                             %
894 %   R e a d P N G I m a g e                                                   %
895 %                                                                             %
896 %                                                                             %
897 %                                                                             %
898 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
899 %
900 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
901 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
902 %  allocates the memory necessary for the new Image structure and returns a
903 %  pointer to the new image or set of images.
904 %
905 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
906 %
907 %  The format of the ReadPNGImage method is:
908 %
909 %      Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
910 %
911 %  A description of each parameter follows:
912 %
913 %    o image_info: the image info.
914 %
915 %    o exception: return any errors or warnings in this structure.
916 %
917 %  To do, more or less in chronological order (as of version 5.5.2,
918 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
919 %
920 %    Get 16-bit cheap transparency working.
921 %
922 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
923 %
924 %    Preserve all unknown and not-yet-handled known chunks found in input
925 %    PNG file and copy them into output PNG files according to the PNG
926 %    copying rules.
927 %
928 %    (At this point, PNG encoding should be in full MNG compliance)
929 %
930 %    Provide options for choice of background to use when the MNG BACK
931 %    chunk is not present or is not mandatory (i.e., leave transparent,
932 %    user specified, MNG BACK, PNG bKGD)
933 %
934 %    Implement LOOP/ENDL [done, but could do discretionary loops more
935 %    efficiently by linking in the duplicate frames.].
936 %
937 %    Decode and act on the MHDR simplicity profile (offer option to reject
938 %    files or attempt to process them anyway when the profile isn't LC or VLC).
939 %
940 %    Upgrade to full MNG without Delta-PNG.
941 %
942 %        o  BACK [done a while ago except for background image ID]
943 %        o  MOVE [done 15 May 1999]
944 %        o  CLIP [done 15 May 1999]
945 %        o  DISC [done 19 May 1999]
946 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
947 %        o  SEEK [partially done 19 May 1999 (discard function only)]
948 %        o  SHOW
949 %        o  PAST
950 %        o  BASI
951 %        o  MNG-level tEXt/iTXt/zTXt
952 %        o  pHYg
953 %        o  pHYs
954 %        o  sBIT
955 %        o  bKGD
956 %        o  iTXt (wait for libpng implementation).
957 %
958 %    Use the scene signature to discover when an identical scene is
959 %    being reused, and just point to the original image->exception instead
960 %    of storing another set of pixels.  This not specific to MNG
961 %    but could be applied generally.
962 %
963 %    Upgrade to full MNG with Delta-PNG.
964 %
965 %    JNG tEXt/iTXt/zTXt
966 %
967 %    We will not attempt to read files containing the CgBI chunk.
968 %    They are really Xcode files meant for display on the iPhone.
969 %    These are not valid PNG files and it is impossible to recover
970 %    the orginal PNG from files that have been converted to Xcode-PNG,
971 %    since irretrievable loss of color data has occurred due to the
972 %    use of premultiplied alpha.
973 */
974
975 #if defined(__cplusplus) || defined(c_plusplus)
976 extern "C" {
977 #endif
978
979 /*
980   This the function that does the actual reading of data.  It is
981   the same as the one supplied in libpng, except that it receives the
982   datastream from the ReadBlob() function instead of standard input.
983 */
984 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
985 {
986   Image
987     *image;
988
989   image=(Image *) png_get_io_ptr(png_ptr);
990   if (length)
991     {
992       png_size_t
993         check;
994
995       check=(png_size_t) ReadBlob(image,(size_t) length,data);
996       if (check != length)
997         {
998           char
999             msg[MaxTextExtent];
1000
1001           (void) FormatMagickString(msg,MaxTextExtent,
1002             "Expected %.20g bytes; found %.20g bytes",(double) length,
1003             (double) check);
1004           png_warning(png_ptr,msg);
1005           png_error(png_ptr,"Read Exception");
1006         }
1007     }
1008 }
1009
1010 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1011     !defined(PNG_MNG_FEATURES_SUPPORTED)
1012 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1013  * older than libpng-1.0.3a, which was the first to allow the empty
1014  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1015  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1016  * encountered after an empty PLTE, so we have to look ahead for bKGD
1017  * chunks and remove them from the datastream that is passed to libpng,
1018  * and store their contents for later use.
1019  */
1020 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1021 {
1022   MngInfo
1023     *mng_info;
1024
1025   Image
1026     *image;
1027
1028   png_size_t
1029     check;
1030
1031   register ssize_t
1032     i;
1033
1034   i=0;
1035   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1036   image=(Image *) mng_info->image;
1037   while (mng_info->bytes_in_read_buffer && length)
1038   {
1039     data[i]=mng_info->read_buffer[i];
1040     mng_info->bytes_in_read_buffer--;
1041     length--;
1042     i++;
1043   }
1044   if (length)
1045     {
1046       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1047
1048       if (check != length)
1049         png_error(png_ptr,"Read Exception");
1050
1051       if (length == 4)
1052         {
1053           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1054               (data[3] == 0))
1055             {
1056               check=(png_size_t) ReadBlob(image,(size_t) length,
1057                 (char *) mng_info->read_buffer);
1058               mng_info->read_buffer[4]=0;
1059               mng_info->bytes_in_read_buffer=4;
1060               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1061                 mng_info->found_empty_plte=MagickTrue;
1062               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1063                 {
1064                   mng_info->found_empty_plte=MagickFalse;
1065                   mng_info->have_saved_bkgd_index=MagickFalse;
1066                 }
1067             }
1068
1069           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1070               (data[3] == 1))
1071             {
1072               check=(png_size_t) ReadBlob(image,(size_t) length,
1073                 (char *) mng_info->read_buffer);
1074               mng_info->read_buffer[4]=0;
1075               mng_info->bytes_in_read_buffer=4;
1076               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1077                 if (mng_info->found_empty_plte)
1078                   {
1079                     /*
1080                       Skip the bKGD data byte and CRC.
1081                     */
1082                     check=(png_size_t)
1083                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1084                     check=(png_size_t) ReadBlob(image,(size_t) length,
1085                       (char *) mng_info->read_buffer);
1086                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1087                     mng_info->have_saved_bkgd_index=MagickTrue;
1088                     mng_info->bytes_in_read_buffer=0;
1089                   }
1090             }
1091         }
1092     }
1093 }
1094 #endif
1095
1096 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1097 {
1098   Image
1099     *image;
1100
1101   image=(Image *) png_get_io_ptr(png_ptr);
1102   if (length)
1103     {
1104       png_size_t
1105         check;
1106
1107       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1108
1109       if (check != length)
1110         png_error(png_ptr,"WriteBlob Failed");
1111     }
1112 }
1113
1114 static void png_flush_data(png_structp png_ptr)
1115 {
1116   (void) png_ptr;
1117 }
1118
1119 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1120 static int PalettesAreEqual(Image *a,Image *b)
1121 {
1122   ssize_t
1123     i;
1124
1125   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1126     return((int) MagickFalse);
1127
1128   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1129     return((int) MagickFalse);
1130
1131   if (a->colors != b->colors)
1132     return((int) MagickFalse);
1133
1134   for (i=0; i < (ssize_t) a->colors; i++)
1135   {
1136     if ((a->colormap[i].red != b->colormap[i].red) ||
1137         (a->colormap[i].green != b->colormap[i].green) ||
1138         (a->colormap[i].blue != b->colormap[i].blue))
1139       return((int) MagickFalse);
1140   }
1141
1142   return((int) MagickTrue);
1143 }
1144 #endif
1145
1146 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1147 {
1148   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1149       mng_info->exists[i] && !mng_info->frozen[i])
1150     {
1151 #ifdef MNG_OBJECT_BUFFERS
1152       if (mng_info->ob[i] != (MngBuffer *) NULL)
1153         {
1154           if (mng_info->ob[i]->reference_count > 0)
1155             mng_info->ob[i]->reference_count--;
1156
1157           if (mng_info->ob[i]->reference_count == 0)
1158             {
1159               if (mng_info->ob[i]->image != (Image *) NULL)
1160                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1161
1162               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1163             }
1164         }
1165       mng_info->ob[i]=(MngBuffer *) NULL;
1166 #endif
1167       mng_info->exists[i]=MagickFalse;
1168       mng_info->invisible[i]=MagickFalse;
1169       mng_info->viewable[i]=MagickFalse;
1170       mng_info->frozen[i]=MagickFalse;
1171       mng_info->x_off[i]=0;
1172       mng_info->y_off[i]=0;
1173       mng_info->object_clip[i].left=0;
1174       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1175       mng_info->object_clip[i].top=0;
1176       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1177     }
1178 }
1179
1180 static void MngInfoFreeStruct(MngInfo *mng_info,
1181     MagickBooleanType *have_mng_structure)
1182 {
1183   if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1184     {
1185       register ssize_t
1186         i;
1187
1188       for (i=1; i < MNG_MAX_OBJECTS; i++)
1189         MngInfoDiscardObject(mng_info,i);
1190
1191       if (mng_info->global_plte != (png_colorp) NULL)
1192         mng_info->global_plte=(png_colorp)
1193           RelinquishMagickMemory(mng_info->global_plte);
1194
1195       mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1196       *have_mng_structure=MagickFalse;
1197     }
1198 }
1199
1200 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1201 {
1202   MngBox
1203     box;
1204
1205   box=box1;
1206   if (box.left < box2.left)
1207     box.left=box2.left;
1208
1209   if (box.top < box2.top)
1210     box.top=box2.top;
1211
1212   if (box.right > box2.right)
1213     box.right=box2.right;
1214
1215   if (box.bottom > box2.bottom)
1216     box.bottom=box2.bottom;
1217
1218   return box;
1219 }
1220
1221 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1222 {
1223    MngBox
1224       box;
1225
1226   /*
1227     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1228   */
1229   box.left=(ssize_t) ((p[0]  << 24) | (p[1]  << 16) | (p[2]  << 8) | p[3]);
1230   box.right=(ssize_t) ((p[4]  << 24) | (p[5]  << 16) | (p[6]  << 8) | p[7]);
1231   box.top=(ssize_t) ((p[8]  << 24) | (p[9]  << 16) | (p[10] << 8) | p[11]);
1232   box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1233   if (delta_type != 0)
1234     {
1235       box.left+=previous_box.left;
1236       box.right+=previous_box.right;
1237       box.top+=previous_box.top;
1238       box.bottom+=previous_box.bottom;
1239     }
1240
1241   return(box);
1242 }
1243
1244 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1245   unsigned char *p)
1246 {
1247   MngPair
1248     pair;
1249   /*
1250     Read two ssize_ts from CLON, MOVE or PAST chunk
1251   */
1252   pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1253   pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1254
1255   if (delta_type != 0)
1256     {
1257       pair.a+=previous_pair.a;
1258       pair.b+=previous_pair.b;
1259     }
1260
1261   return(pair);
1262 }
1263
1264 static long mng_get_long(unsigned char *p)
1265 {
1266   return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1267 }
1268
1269 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1270 {
1271   Image
1272     *image;
1273
1274   image=(Image *) png_get_error_ptr(ping);
1275
1276   if (image->debug != MagickFalse)
1277     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1278       "  libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1279
1280   (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1281     message,"`%s'",image->filename);
1282
1283 #if (PNG_LIBPNG_VER < 10500)
1284   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1285    * are building with libpng-1.4.x and can be ignored.
1286    */
1287   longjmp(ping->jmpbuf,1);
1288 #else
1289   png_longjmp(ping,1);
1290 #endif
1291 }
1292
1293 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1294 {
1295   Image
1296     *image;
1297
1298   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1299     png_error(ping, message);
1300
1301   image=(Image *) png_get_error_ptr(ping);
1302   if (image->debug != MagickFalse)
1303     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1304       "  libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
1305
1306   (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1307     message,"`%s'",image->filename);
1308 }
1309
1310 #ifdef PNG_USER_MEM_SUPPORTED
1311 static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
1312 {
1313 #if (PNG_LIBPNG_VER < 10011)
1314   png_voidp
1315     ret;
1316
1317   png_ptr=png_ptr;
1318   ret=((png_voidp) AcquireMagickMemory((size_t) size));
1319
1320   if (ret == NULL)
1321     png_error("Insufficient memory.");
1322
1323   return(ret);
1324 #else
1325   png_ptr=png_ptr;
1326   return((png_voidp) AcquireMagickMemory((size_t) size));
1327 #endif
1328 }
1329
1330 /*
1331   Free a pointer.  It is removed from the list at the same time.
1332 */
1333 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1334 {
1335   png_ptr=png_ptr;
1336   ptr=RelinquishMagickMemory(ptr);
1337   return((png_free_ptr) NULL);
1338 }
1339 #endif
1340
1341 #if defined(__cplusplus) || defined(c_plusplus)
1342 }
1343 #endif
1344
1345 static int
1346 Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
1347    png_textp text,int ii)
1348 {
1349   register ssize_t
1350     i;
1351
1352   register unsigned char
1353     *dp;
1354
1355   register png_charp
1356     sp;
1357
1358   png_uint_32
1359     length,
1360     nibbles;
1361
1362   StringInfo
1363     *profile;
1364
1365   const unsigned char
1366     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1367                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1368                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1369                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1370                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1371                  13,14,15};
1372
1373   sp=text[ii].text+1;
1374   /* look for newline */
1375   while (*sp != '\n')
1376      sp++;
1377
1378   /* look for length */
1379   while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1380      sp++;
1381
1382   length=(png_uint_32) StringToLong(sp);
1383
1384   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1385        "      length: %lu",(unsigned long) length);
1386
1387   while (*sp != ' ' && *sp != '\n')
1388      sp++;
1389
1390   /* allocate space */
1391   if (length == 0)
1392   {
1393     (void) ThrowMagickException(&image->exception,GetMagickModule(),
1394       CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1395     return(MagickFalse);
1396   }
1397
1398   profile=AcquireStringInfo(length);
1399
1400   if (profile == (StringInfo *) NULL)
1401   {
1402     (void) ThrowMagickException(&image->exception,GetMagickModule(),
1403       ResourceLimitError,"MemoryAllocationFailed","`%s'",
1404       "unable to copy profile");
1405     return(MagickFalse);
1406   }
1407
1408   /* copy profile, skipping white space and column 1 "=" signs */
1409   dp=GetStringInfoDatum(profile);
1410   nibbles=length*2;
1411
1412   for (i=0; i < (ssize_t) nibbles; i++)
1413   {
1414     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1415     {
1416       if (*sp == '\0')
1417         {
1418           (void) ThrowMagickException(&image->exception,GetMagickModule(),
1419             CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1420           profile=DestroyStringInfo(profile);
1421           return(MagickFalse);
1422         }
1423       sp++;
1424     }
1425
1426     if (i%2 == 0)
1427       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1428
1429     else
1430       (*dp++)+=unhex[(int) *sp++];
1431   }
1432   /*
1433     We have already read "Raw profile type.
1434   */
1435   (void) SetImageProfile(image,&text[ii].key[17],profile);
1436   profile=DestroyStringInfo(profile);
1437
1438   if (image_info->verbose)
1439     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1440
1441   return MagickTrue;
1442 }
1443
1444 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1445 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1446 {
1447   Image
1448     *image;
1449
1450
1451   /* The unknown chunk structure contains the chunk data:
1452      png_byte name[5];
1453      png_byte *data;
1454      png_size_t size;
1455
1456      Note that libpng has already taken care of the CRC handling.
1457   */
1458
1459
1460   if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1461       chunk->name[2] != 65 ||chunk-> name[3] != 103)
1462     return(0); /* Did not recognize */
1463
1464   /* recognized vpAg */
1465
1466   if (chunk->size != 9)
1467     return(-1); /* Error return */
1468
1469   if (chunk->data[8] != 0)
1470     return(0);  /* ImageMagick requires pixel units */
1471
1472   image=(Image *) png_get_user_chunk_ptr(ping);
1473
1474   image->page.width=(size_t) ((chunk->data[0] << 24) |
1475      (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1476
1477   image->page.height=(size_t) ((chunk->data[4] << 24) |
1478      (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1479
1480   /* Return one of the following: */
1481      /* return(-n);  chunk had an error */
1482      /* return(0);  did not recognize */
1483      /* return(n);  success */
1484
1485   return(1);
1486
1487 }
1488 #endif
1489
1490 /*
1491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1492 %                                                                             %
1493 %                                                                             %
1494 %                                                                             %
1495 %   R e a d O n e P N G I m a g e                                             %
1496 %                                                                             %
1497 %                                                                             %
1498 %                                                                             %
1499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1500 %
1501 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1502 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
1503 %  necessary for the new Image structure and returns a pointer to the new
1504 %  image.
1505 %
1506 %  The format of the ReadOnePNGImage method is:
1507 %
1508 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1509 %         ExceptionInfo *exception)
1510 %
1511 %  A description of each parameter follows:
1512 %
1513 %    o mng_info: Specifies a pointer to a MngInfo structure.
1514 %
1515 %    o image_info: the image info.
1516 %
1517 %    o exception: return any errors or warnings in this structure.
1518 %
1519 */
1520 static Image *ReadOnePNGImage(MngInfo *mng_info,
1521     const ImageInfo *image_info, ExceptionInfo *exception)
1522 {
1523   /* Read one PNG image */
1524
1525   Image
1526     *image;
1527
1528   int
1529     num_text,
1530     num_passes,
1531     pass,
1532     ping_bit_depth,
1533     ping_color_type,
1534     ping_interlace_method,
1535     ping_compression_method,
1536     ping_filter_method,
1537     ping_num_trans;
1538
1539   LongPixelPacket
1540     transparent_color;
1541
1542   MagickBooleanType
1543     logging,
1544     status;
1545
1546   png_bytep
1547      ping_trans_alpha;
1548
1549   png_color_16p
1550      ping_background,
1551      ping_trans_color;
1552
1553   png_info
1554     *end_info,
1555     *ping_info;
1556
1557   png_struct
1558     *ping;
1559
1560   png_textp
1561     text;
1562
1563   png_uint_32
1564     ping_height,
1565     ping_width,
1566     ping_rowbytes;
1567
1568   QuantumInfo
1569     *quantum_info;
1570
1571   unsigned char
1572     *ping_pixels;
1573
1574   ssize_t
1575     y;
1576
1577   register unsigned char
1578     *p;
1579
1580   register IndexPacket
1581     *indexes;
1582
1583   register ssize_t
1584     i,
1585     x;
1586
1587   register PixelPacket
1588     *q;
1589
1590   size_t
1591     length,
1592     row_offset;
1593
1594 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1595   png_byte unused_chunks[]=
1596   {
1597     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
1598     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
1599     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
1600     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
1601     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
1602     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
1603   };
1604 #endif
1605
1606   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
1607     "  Enter ReadOnePNGImage()");
1608
1609 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1610   LockSemaphoreInfo(ping_semaphore);
1611 #endif
1612
1613 #if (PNG_LIBPNG_VER < 10200)
1614   if (image_info->verbose)
1615     printf("Your PNG library (libpng-%s) is rather old.\n",
1616        PNG_LIBPNG_VER_STRING);
1617 #endif
1618
1619 #if (PNG_LIBPNG_VER >= 10400)
1620 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
1621   if (image_info->verbose)
1622     {
1623       printf("Your PNG library (libpng-%s) is an old beta version.\n",
1624            PNG_LIBPNG_VER_STRING);
1625       printf("Please update it.\n");
1626     }
1627 #  endif
1628 #endif
1629
1630
1631   quantum_info = (QuantumInfo *) NULL;
1632   image=mng_info->image;
1633
1634   if (logging != MagickFalse)
1635     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
1636       "  image->matte=%d",(int) image->matte);
1637
1638   /* Set to an out-of-range color unless tRNS chunk is present */
1639   transparent_color.red=65537;
1640   transparent_color.green=65537;
1641   transparent_color.blue=65537;
1642   transparent_color.opacity=65537;
1643
1644   /*
1645     Allocate the PNG structures
1646   */
1647 #ifdef PNG_USER_MEM_SUPPORTED
1648  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
1649    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
1650    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
1651 #else
1652   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
1653     MagickPNGErrorHandler,MagickPNGWarningHandler);
1654 #endif
1655   if (ping == (png_struct *) NULL)
1656     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1657
1658   ping_info=png_create_info_struct(ping);
1659
1660   if (ping_info == (png_info *) NULL)
1661     {
1662       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1663       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1664     }
1665
1666   end_info=png_create_info_struct(ping);
1667
1668   if (end_info == (png_info *) NULL)
1669     {
1670       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1671       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1672     }
1673
1674   ping_pixels=(unsigned char *) NULL;
1675
1676   if (setjmp(png_jmpbuf(ping)))
1677     {
1678       /*
1679         PNG image is corrupt.
1680       */
1681       png_destroy_read_struct(&ping,&ping_info,&end_info);
1682 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1683       UnlockSemaphoreInfo(ping_semaphore);
1684 #endif
1685       if (logging != MagickFalse)
1686         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1687           "  exit ReadOnePNGImage() with error.");
1688
1689       if (image != (Image *) NULL)
1690         {
1691           InheritException(exception,&image->exception);
1692           image->columns=0;
1693         }
1694
1695       return(GetFirstImageInList(image));
1696     }
1697   /*
1698     Prepare PNG for reading.
1699   */
1700
1701   mng_info->image_found++;
1702   png_set_sig_bytes(ping,8);
1703
1704   if (LocaleCompare(image_info->magick,"MNG") == 0)
1705     {
1706 #if defined(PNG_MNG_FEATURES_SUPPORTED)
1707       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1708       png_set_read_fn(ping,image,png_get_data);
1709 #else
1710 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1711       png_permit_empty_plte(ping,MagickTrue);
1712       png_set_read_fn(ping,image,png_get_data);
1713 #else
1714       mng_info->image=image;
1715       mng_info->bytes_in_read_buffer=0;
1716       mng_info->found_empty_plte=MagickFalse;
1717       mng_info->have_saved_bkgd_index=MagickFalse;
1718       png_set_read_fn(ping,mng_info,mng_get_data);
1719 #endif
1720 #endif
1721     }
1722
1723   else
1724     png_set_read_fn(ping,image,png_get_data);
1725
1726 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1727   /* Ignore unused chunks and all unknown chunks except for vpAg */
1728   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1729   png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1730   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1731      (int)sizeof(unused_chunks)/5);
1732   /* Callback for other unknown chunks */
1733   png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1734 #endif
1735
1736 #if (PNG_LIBPNG_VER < 10400)
1737 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1738    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
1739   /* Disable thread-unsafe features of pnggccrd */
1740   if (png_access_version_number() >= 10200)
1741   {
1742     png_uint_32 mmx_disable_mask=0;
1743     png_uint_32 asm_flags;
1744
1745     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
1746                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
1747                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
1748                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1749     asm_flags=png_get_asm_flags(ping);
1750     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1751   }
1752 #  endif
1753 #endif
1754
1755   png_read_info(ping,ping_info);
1756
1757   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1758                &ping_bit_depth,&ping_color_type,
1759                &ping_interlace_method,&ping_compression_method,
1760                &ping_filter_method);
1761
1762   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1763                       &ping_trans_color);
1764
1765   (void) png_get_bKGD(ping, ping_info, &ping_background);
1766
1767   if (ping_bit_depth < 8)
1768     {
1769       if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1770         {
1771           png_set_packing(ping);
1772           ping_bit_depth = 8;
1773         }
1774     }
1775
1776   image->depth=ping_bit_depth;
1777   image->depth=GetImageQuantumDepth(image,MagickFalse);
1778   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
1779   if (logging != MagickFalse)
1780     {
1781       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1782         "    PNG width: %.20g, height: %.20g",
1783         (double) ping_width, (double) ping_height);
1784
1785       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1786         "    PNG color_type: %d, bit_depth: %d",
1787         ping_color_type, ping_bit_depth);
1788
1789       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1790         "    PNG compression_method: %d",
1791         ping_compression_method);
1792
1793       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1794         "    PNG interlace_method: %d, filter_method: %d",
1795         ping_interlace_method,ping_filter_method);
1796     }
1797
1798 #ifdef PNG_READ_iCCP_SUPPORTED
1799   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
1800     {
1801       int
1802         compression;
1803
1804 #if (PNG_LIBPNG_VER < 10500)
1805       png_charp
1806         info;
1807 #else
1808       png_bytep
1809         info;
1810 #endif
1811
1812       png_charp
1813         name;
1814
1815       png_uint_32
1816         profile_length;
1817
1818       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1819         &profile_length);
1820
1821       if (profile_length != 0)
1822         {
1823           StringInfo
1824             *profile;
1825
1826           if (logging != MagickFalse)
1827             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1828               "    Reading PNG iCCP chunk.");
1829           profile=AcquireStringInfo(profile_length);
1830           SetStringInfoDatum(profile,(const unsigned char *) info);
1831           (void) SetImageProfile(image,"icc",profile);
1832           profile=DestroyStringInfo(profile);
1833       }
1834     }
1835 #endif
1836 #if defined(PNG_READ_sRGB_SUPPORTED)
1837   {
1838     int
1839       intent;
1840
1841     if (mng_info->have_global_srgb)
1842       image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1843         (mng_info->global_srgb_intent);
1844
1845     if (png_get_sRGB(ping,ping_info,&intent))
1846       {
1847         image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
1848           (intent);
1849
1850         if (logging != MagickFalse)
1851           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1852             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
1853       }
1854   }
1855 #endif
1856   {
1857      double
1858         file_gamma;
1859
1860      if (!png_get_gAMA(ping,ping_info,&file_gamma))
1861        if (mng_info->have_global_gama)
1862          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
1863
1864      if (png_get_gAMA(ping,ping_info,&file_gamma))
1865        {
1866          image->gamma=(float) file_gamma;
1867          if (logging != MagickFalse)
1868            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1869              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
1870        }
1871   }
1872   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1873     {
1874       if (mng_info->have_global_chrm != MagickFalse)
1875         {
1876           (void) png_set_cHRM(ping,ping_info,
1877             mng_info->global_chrm.white_point.x,
1878             mng_info->global_chrm.white_point.y,
1879             mng_info->global_chrm.red_primary.x,
1880             mng_info->global_chrm.red_primary.y,
1881             mng_info->global_chrm.green_primary.x,
1882             mng_info->global_chrm.green_primary.y,
1883             mng_info->global_chrm.blue_primary.x,
1884             mng_info->global_chrm.blue_primary.y);
1885         }
1886     }
1887
1888   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1889     {
1890       (void) png_get_cHRM(ping,ping_info,
1891         &image->chromaticity.white_point.x,
1892         &image->chromaticity.white_point.y,
1893         &image->chromaticity.red_primary.x,
1894         &image->chromaticity.red_primary.y,
1895         &image->chromaticity.green_primary.x,
1896         &image->chromaticity.green_primary.y,
1897         &image->chromaticity.blue_primary.x,
1898         &image->chromaticity.blue_primary.y);
1899
1900       if (logging != MagickFalse)
1901         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1902           "    Reading PNG cHRM chunk.");
1903     }
1904
1905   if (image->rendering_intent != UndefinedIntent)
1906     {
1907       png_set_sRGB(ping,ping_info,
1908          Magick_RenderingIntent_to_PNG_RenderingIntent
1909          (image->rendering_intent));
1910       png_set_gAMA(ping,ping_info,0.45455f);
1911       png_set_cHRM(ping,ping_info,
1912                   0.6400f, 0.3300f, 0.3000f, 0.6000f,
1913                   0.1500f, 0.0600f, 0.3127f, 0.3290f);
1914     }
1915 #if defined(PNG_oFFs_SUPPORTED)
1916   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
1917     {
1918       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
1919       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
1920
1921       if (logging != MagickFalse)
1922         if (image->page.x || image->page.y)
1923           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1924             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
1925             image->page.x,(double) image->page.y);
1926     }
1927 #endif
1928 #if defined(PNG_pHYs_SUPPORTED)
1929   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1930     {
1931       if (mng_info->have_global_phys)
1932         {
1933           png_set_pHYs(ping,ping_info,
1934                        mng_info->global_x_pixels_per_unit,
1935                        mng_info->global_y_pixels_per_unit,
1936                        mng_info->global_phys_unit_type);
1937         }
1938     }
1939
1940   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
1941     {
1942       int
1943         unit_type;
1944
1945       png_uint_32
1946         x_resolution,
1947         y_resolution;
1948
1949       /*
1950         Set image resolution.
1951       */
1952       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
1953         &unit_type);
1954       image->x_resolution=(double) x_resolution;
1955       image->y_resolution=(double) y_resolution;
1956
1957       if (unit_type == PNG_RESOLUTION_METER)
1958         {
1959           image->units=PixelsPerCentimeterResolution;
1960           image->x_resolution=(double) x_resolution/100.0;
1961           image->y_resolution=(double) y_resolution/100.0;
1962         }
1963
1964       if (logging != MagickFalse)
1965         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1966           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
1967           (double) x_resolution,(double) y_resolution,unit_type);
1968     }
1969 #endif
1970
1971   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
1972     {
1973       int
1974         number_colors;
1975
1976       png_colorp
1977         palette;
1978
1979       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
1980
1981       if ((number_colors == 0) &&
1982           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1983         {
1984           if (mng_info->global_plte_length)
1985             {
1986               png_set_PLTE(ping,ping_info,mng_info->global_plte,
1987                 (int) mng_info->global_plte_length);
1988
1989               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
1990                 if (mng_info->global_trns_length)
1991                   {
1992                     if (mng_info->global_trns_length >
1993                         mng_info->global_plte_length)
1994                       (void) ThrowMagickException(&image->exception,
1995                         GetMagickModule(),CoderError,
1996                         "global tRNS has more entries than global PLTE",
1997                         "`%s'",image_info->filename);
1998                     png_set_tRNS(ping,ping_info,mng_info->global_trns,
1999                       (int) mng_info->global_trns_length,NULL);
2000                   }
2001 #if defined(PNG_READ_bKGD_SUPPORTED)
2002               if (
2003 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2004                    mng_info->have_saved_bkgd_index ||
2005 #endif
2006                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2007                     {
2008                       png_color_16
2009                          background;
2010
2011 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2012                       if (mng_info->have_saved_bkgd_index)
2013                         background.index=mng_info->saved_bkgd_index;
2014 #endif
2015                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2016                         background.index=ping_background->index;
2017
2018                       background.red=(png_uint_16)
2019                         mng_info->global_plte[background.index].red;
2020
2021                       background.green=(png_uint_16)
2022                         mng_info->global_plte[background.index].green;
2023
2024                       background.blue=(png_uint_16)
2025                         mng_info->global_plte[background.index].blue;
2026
2027                       png_set_bKGD(ping,ping_info,&background);
2028                     }
2029 #endif
2030                 }
2031               else
2032                 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2033                   CoderError,"No global PLTE in file","`%s'",
2034                   image_info->filename);
2035             }
2036         }
2037
2038 #if defined(PNG_READ_bKGD_SUPPORTED)
2039   if (mng_info->have_global_bkgd &&
2040           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2041       image->background_color=mng_info->mng_global_bkgd;
2042
2043   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2044     {
2045       /*
2046         Set image background color.
2047       */
2048       if (logging != MagickFalse)
2049         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2050           "    Reading PNG bKGD chunk.");
2051
2052       if (ping_bit_depth == MAGICKCORE_QUANTUM_DEPTH)
2053         {
2054           image->background_color.red=ping_background->red;
2055           image->background_color.green=ping_background->green;
2056           image->background_color.blue=ping_background->blue;
2057         }
2058
2059       else /* Scale background components to 16-bit */
2060         {
2061           unsigned int
2062             bkgd_scale;
2063
2064           if (logging != MagickFalse)
2065             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2066               "    raw ping_background=(%d,%d,%d).",ping_background->red,
2067               ping_background->green,ping_background->blue);
2068
2069           bkgd_scale = 1;
2070
2071           if (ping_bit_depth == 1)
2072              bkgd_scale = 255;
2073
2074           else if (ping_bit_depth == 2)
2075              bkgd_scale = 85;
2076
2077           else if (ping_bit_depth == 4)
2078              bkgd_scale = 17;
2079
2080           if (ping_bit_depth <= 8)
2081              bkgd_scale *= 257;
2082
2083           ping_background->red *= bkgd_scale;
2084           ping_background->green *= bkgd_scale;
2085           ping_background->blue *= bkgd_scale;
2086
2087           if (logging != MagickFalse)
2088             {
2089             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2090               "    bkgd_scale=%d.",bkgd_scale);
2091
2092             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2093               "    ping_background=(%d,%d,%d).",ping_background->red,
2094               ping_background->green,ping_background->blue);
2095             }
2096
2097           image->background_color.red=
2098             ScaleShortToQuantum(ping_background->red);
2099
2100           image->background_color.green=
2101             ScaleShortToQuantum(ping_background->green);
2102
2103           image->background_color.blue=
2104             ScaleShortToQuantum(ping_background->blue);
2105
2106           image->background_color.opacity=OpaqueOpacity;
2107
2108           if (logging != MagickFalse)
2109             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2110               "    image->background_color=(%.20g,%.20g,%.20g).",
2111               (double) image->background_color.red,
2112               (double) image->background_color.green,
2113               (double) image->background_color.blue);
2114         }
2115     }
2116 #endif
2117
2118   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2119     {
2120       /*
2121         Image has a tRNS chunk.
2122       */
2123       int
2124         max_sample;
2125
2126       size_t
2127         one=1;
2128
2129       if (logging != MagickFalse)
2130         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2131           "    Reading PNG tRNS chunk.");
2132
2133       max_sample = (int) ((one << ping_bit_depth) - 1);
2134
2135       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2136           (int)ping_trans_color->gray > max_sample) ||
2137           (ping_color_type == PNG_COLOR_TYPE_RGB &&
2138           ((int)ping_trans_color->red > max_sample ||
2139           (int)ping_trans_color->green > max_sample ||
2140           (int)ping_trans_color->blue > max_sample)))
2141         {
2142           if (logging != MagickFalse)
2143             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2144               "    Ignoring PNG tRNS chunk with out-of-range sample.");
2145           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2146           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2147           image->matte=MagickFalse;
2148         }
2149       else
2150         {
2151           int
2152              scale_to_short;
2153
2154           scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2155
2156           /* Scale transparent_color to short */
2157           transparent_color.red= scale_to_short*ping_trans_color->red;
2158           transparent_color.green= scale_to_short*ping_trans_color->green;
2159           transparent_color.blue= scale_to_short*ping_trans_color->blue;
2160           transparent_color.opacity= scale_to_short*ping_trans_color->gray;
2161
2162           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2163             {
2164               if (logging != MagickFalse)
2165               {
2166                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2167                   "    Raw tRNS graylevel is %d.",ping_trans_color->gray);
2168
2169                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2170                   "    scaled graylevel is %d.",transparent_color.opacity);
2171               }
2172               transparent_color.red=transparent_color.opacity;
2173               transparent_color.green=transparent_color.opacity;
2174               transparent_color.blue=transparent_color.opacity;
2175             }
2176         }
2177     }
2178 #if defined(PNG_READ_sBIT_SUPPORTED)
2179   if (mng_info->have_global_sbit)
2180     {
2181       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2182         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2183     }
2184 #endif
2185   num_passes=png_set_interlace_handling(ping);
2186
2187   png_read_update_info(ping,ping_info);
2188
2189   ping_rowbytes=png_get_rowbytes(ping,ping_info);
2190
2191   /*
2192     Initialize image structure.
2193   */
2194   mng_info->image_box.left=0;
2195   mng_info->image_box.right=(ssize_t) ping_width;
2196   mng_info->image_box.top=0;
2197   mng_info->image_box.bottom=(ssize_t) ping_height;
2198   if (mng_info->mng_type == 0)
2199     {
2200       mng_info->mng_width=ping_width;
2201       mng_info->mng_height=ping_height;
2202       mng_info->frame=mng_info->image_box;
2203       mng_info->clip=mng_info->image_box;
2204     }
2205
2206   else
2207     {
2208       image->page.y=mng_info->y_off[mng_info->object_id];
2209     }
2210
2211   image->compression=ZipCompression;
2212   image->columns=ping_width;
2213   image->rows=ping_height;
2214   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2215       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2216       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2217     {
2218       size_t
2219         one;
2220
2221       image->storage_class=PseudoClass;
2222       one=1;
2223       image->colors=one << ping_bit_depth;
2224 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2225       if (image->colors > 256)
2226         image->colors=256;
2227 #else
2228       if (image->colors > 65536L)
2229         image->colors=65536L;
2230 #endif
2231       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2232         {
2233           int
2234             number_colors;
2235
2236           png_colorp
2237             palette;
2238
2239           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2240           image->colors=(size_t) number_colors;
2241
2242           if (logging != MagickFalse)
2243             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2244               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2245         }
2246     }
2247
2248   if (image->storage_class == PseudoClass)
2249     {
2250       /*
2251         Initialize image colormap.
2252       */
2253       if (AcquireImageColormap(image,image->colors) == MagickFalse)
2254         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2255
2256       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2257         {
2258           int
2259             number_colors;
2260
2261           png_colorp
2262             palette;
2263
2264           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2265
2266           for (i=0; i < (ssize_t) image->colors; i++)
2267           {
2268             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2269             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2270             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2271           }
2272         }
2273
2274       else
2275         {
2276           size_t
2277             scale;
2278
2279           scale=(QuantumRange/((1UL << ping_bit_depth)-1));
2280
2281           if (scale < 1)
2282              scale=1;
2283
2284           for (i=0; i < (ssize_t) image->colors; i++)
2285           {
2286             image->colormap[i].red=(Quantum) (i*scale);
2287             image->colormap[i].green=(Quantum) (i*scale);
2288             image->colormap[i].blue=(Quantum) (i*scale);
2289           }
2290        }
2291     }
2292   /*
2293     Read image scanlines.
2294   */
2295   if (image->delay != 0)
2296     mng_info->scenes_found++;
2297
2298   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
2299       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2300       (image_info->first_scene+image_info->number_scenes))))
2301     {
2302       if (logging != MagickFalse)
2303         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2304           "    Skipping PNG image data for scene %.20g",(double)
2305           mng_info->scenes_found-1);
2306       png_destroy_read_struct(&ping,&ping_info,&end_info);
2307 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2308       UnlockSemaphoreInfo(ping_semaphore);
2309 #endif
2310       if (logging != MagickFalse)
2311         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2312           "  exit ReadOnePNGImage().");
2313
2314       return(image);
2315     }
2316
2317   if (logging != MagickFalse)
2318     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2319       "    Reading PNG IDAT chunk(s)");
2320
2321   if (num_passes > 1)
2322     ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2323       ping_rowbytes*sizeof(*ping_pixels));
2324
2325   else
2326     ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2327       sizeof(*ping_pixels));
2328
2329   if (ping_pixels == (unsigned char *) NULL)
2330     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2331
2332   if (logging != MagickFalse)
2333     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2334       "    Converting PNG pixels to pixel packets");
2335   /*
2336     Convert PNG pixels to pixel packets.
2337   */
2338   if (setjmp(png_jmpbuf(ping)))
2339     {
2340       /*
2341         PNG image is corrupt.
2342       */
2343       png_destroy_read_struct(&ping,&ping_info,&end_info);
2344 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2345       UnlockSemaphoreInfo(ping_semaphore);
2346 #endif
2347       if (quantum_info != (QuantumInfo *) NULL)
2348         quantum_info = DestroyQuantumInfo(quantum_info);
2349
2350       if (ping_pixels != (unsigned char *) NULL)
2351         ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2352
2353       if (logging != MagickFalse)
2354         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2355           "  exit ReadOnePNGImage() with error.");
2356
2357       if (image != (Image *) NULL)
2358         {
2359           InheritException(exception,&image->exception);
2360           image->columns=0;
2361         }
2362
2363       return(GetFirstImageInList(image));
2364     }
2365
2366   quantum_info=AcquireQuantumInfo(image_info,image);
2367
2368   if (quantum_info == (QuantumInfo *) NULL)
2369     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2370
2371   {
2372
2373    MagickBooleanType
2374      found_transparent_pixel;
2375
2376   found_transparent_pixel=MagickFalse;
2377
2378   if (image->storage_class == DirectClass)
2379     {
2380       for (pass=0; pass < num_passes; pass++)
2381       {
2382         /*
2383           Convert image to DirectClass pixel packets.
2384         */
2385 #if  (MAGICKCORE_QUANTUM_DEPTH == 8)
2386         int
2387           depth;
2388
2389         depth=(ssize_t) ping_bit_depth;
2390 #endif
2391         image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2392             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2393             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2394             MagickTrue : MagickFalse;
2395
2396         for (y=0; y < (ssize_t) image->rows; y++)
2397         {
2398           if (num_passes > 1)
2399             row_offset=ping_rowbytes*y;
2400
2401           else
2402             row_offset=0;
2403
2404           png_read_row(ping,ping_pixels+row_offset,NULL);
2405           q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2406
2407           if (q == (PixelPacket *) NULL)
2408             break;
2409
2410 #if  (0 && (MAGICKCORE_QUANTUM_DEPTH == 8) && !defined(MAGICKCORE_HDRI_SUPPORT))
2411 /* code deleted from version 6.6.6-8 */
2412 #else  /* (MAGICKCORE_QUANTUM_DEPTH != 8) */
2413
2414           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2415             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2416               GrayQuantum,ping_pixels+row_offset,exception);
2417
2418           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2419             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2420               GrayAlphaQuantum,ping_pixels+row_offset,exception);
2421
2422           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2423             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2424               RGBAQuantum,ping_pixels+row_offset,exception);
2425
2426           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2427             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2428               IndexQuantum,ping_pixels+row_offset,exception);
2429
2430           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2431             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2432               RGBQuantum,ping_pixels+row_offset,exception);
2433 #endif
2434           if (found_transparent_pixel == MagickFalse)
2435             {
2436               /* Is there a transparent pixel in the row? */
2437               if (y== 0 && logging != MagickFalse)
2438                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2439                    "    Looking for cheap transparent pixel");
2440
2441               for (x=(ssize_t) image->columns-1; x >= 0; x--)
2442               {
2443                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2444                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
2445                    (q->opacity != OpaqueOpacity))
2446                   {
2447                     if (logging != MagickFalse)
2448                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2449                         "    ...got one.");
2450
2451                     found_transparent_pixel = MagickTrue;
2452                     break;
2453                   }
2454                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2455                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
2456                     (ScaleQuantumToShort(q->red) == transparent_color.red &&
2457                     ScaleQuantumToShort(q->green) == transparent_color.green &&
2458                     ScaleQuantumToShort(q->blue) == transparent_color.blue))
2459                   {
2460                     if (logging != MagickFalse)
2461                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2462                         "    ...got one.");
2463                     found_transparent_pixel = MagickTrue;
2464                     break;
2465                   }
2466                 q++;
2467               }
2468             }
2469
2470           if ((image->previous == (Image *) NULL) && (num_passes == 1))
2471             {
2472               status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2473                   image->rows);
2474
2475               if (status == MagickFalse)
2476                 break;
2477             }
2478           if (SyncAuthenticPixels(image,exception) == MagickFalse)
2479             break;
2480         }
2481
2482         if ((image->previous == (Image *) NULL) && (num_passes != 1))
2483           {
2484             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2485             if (status == MagickFalse)
2486               break;
2487           }
2488       }
2489     }
2490
2491   else /* image->storage_class != DirectClass */
2492
2493     for (pass=0; pass < num_passes; pass++)
2494     {
2495       Quantum
2496         *quantum_scanline;
2497
2498       register Quantum
2499         *r;
2500
2501       /*
2502         Convert grayscale image to PseudoClass pixel packets.
2503       */
2504       image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
2505         MagickTrue : MagickFalse;
2506
2507       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2508         (image->matte ?  2 : 1)*sizeof(*quantum_scanline));
2509
2510       if (quantum_scanline == (Quantum *) NULL)
2511         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2512
2513       for (y=0; y < (ssize_t) image->rows; y++)
2514       {
2515         if (num_passes > 1)
2516           row_offset=ping_rowbytes*y;
2517
2518         else
2519           row_offset=0;
2520
2521         png_read_row(ping,ping_pixels+row_offset,NULL);
2522         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2523
2524         if (q == (PixelPacket *) NULL)
2525           break;
2526
2527         indexes=GetAuthenticIndexQueue(image);
2528         p=ping_pixels+row_offset;
2529         r=quantum_scanline;
2530
2531         switch (ping_bit_depth)
2532         {
2533           case 1:
2534           {
2535             register ssize_t
2536               bit;
2537
2538             for (x=(ssize_t) image->columns-7; x > 0; x-=8)
2539             {
2540               for (bit=7; bit >= 0; bit--)
2541                 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2542               p++;
2543             }
2544
2545             if ((image->columns % 8) != 0)
2546               {
2547                 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
2548                   *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
2549               }
2550
2551             break;
2552           }
2553
2554           case 2:
2555           {
2556             for (x=(ssize_t) image->columns-3; x > 0; x-=4)
2557             {
2558               *r++=(*p >> 6) & 0x03;
2559               *r++=(*p >> 4) & 0x03;
2560               *r++=(*p >> 2) & 0x03;
2561               *r++=(*p++) & 0x03;
2562             }
2563
2564             if ((image->columns % 4) != 0)
2565               {
2566                 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
2567                   *r++=(Quantum) ((*p >> (i*2)) & 0x03);
2568               }
2569
2570             break;
2571           }
2572
2573           case 4:
2574           {
2575             for (x=(ssize_t) image->columns-1; x > 0; x-=2)
2576             {
2577               *r++=(*p >> 4) & 0x0f;
2578               *r++=(*p++) & 0x0f;
2579             }
2580
2581             if ((image->columns % 2) != 0)
2582               *r++=(*p++ >> 4) & 0x0f;
2583
2584             break;
2585           }
2586
2587           case 8:
2588           {
2589             if (ping_color_type == 4)
2590               for (x=(ssize_t) image->columns-1; x >= 0; x--)
2591               {
2592                 *r++=*p++;
2593                 /* In image.h, OpaqueOpacity is 0
2594                  * TransparentOpacity is QuantumRange
2595                  * In a PNG datastream, Opaque is QuantumRange
2596                  * and Transparent is 0.
2597                  */
2598                 q->opacity=ScaleCharToQuantum((unsigned char) (255-(*p++)));
2599                 if (q->opacity != OpaqueOpacity)
2600                   found_transparent_pixel = MagickTrue;
2601                 q++;
2602               }
2603
2604             else
2605               for (x=(ssize_t) image->columns-1; x >= 0; x--)
2606                 *r++=*p++;
2607
2608             break;
2609           }
2610
2611           case 16:
2612           {
2613             for (x=(ssize_t) image->columns-1; x >= 0; x--)
2614             {
2615 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
2616               size_t
2617                 quantum;
2618
2619               if (image->colors > 256)
2620                 *r=((*p++) << 8);
2621
2622               else
2623                 *r=0;
2624
2625               quantum=(*r);
2626               quantum|=(*p++);
2627               *r=(Quantum) quantum;
2628               r++;
2629
2630               if (ping_color_type == 4)
2631                 {
2632                   quantum=((*p++) << 8);
2633                   quantum|=(*p++);
2634                   q->opacity=(Quantum) (QuantumRange-quantum);
2635                   if (q->opacity != OpaqueOpacity)
2636                     found_transparent_pixel = MagickTrue;
2637                   q++;
2638                 }
2639 #else
2640 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
2641               size_t
2642                 quantum;
2643
2644               if (image->colors > 256)
2645                 *r=((*p++) << 8);
2646
2647               else
2648                 *r=0;
2649
2650               quantum=(*r);
2651               quantum|=(*p++);
2652               *r=quantum;
2653               r++;
2654
2655               if (ping_color_type == 4)
2656                 {
2657                   q->opacity=(*p << 8) | *(p+1);
2658                   q->opacity*=65537L;
2659                   q->opacity=(Quantum) GetAlphaPixelComponent(q);
2660                   if (q->opacity != OpaqueOpacity)
2661                     found_transparent_pixel = MagickTrue;
2662                   p+=2;
2663                   q++;
2664                 }
2665
2666 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
2667               *r++=(*p++);
2668               p++; /* strip low byte */
2669
2670               if (ping_color_type == 4)
2671                 {
2672                   q->opacity=(Quantum) (QuantumRange-(*p++));
2673                   if (q->opacity != OpaqueOpacity)
2674                     found_transparent_pixel = MagickTrue;
2675                   p++;
2676                   q++;
2677                 }
2678 #endif
2679 #endif
2680             }
2681
2682             break;
2683           }
2684
2685           default:
2686             break;
2687         }
2688         /*
2689           Transfer image scanline.
2690         */
2691         r=quantum_scanline;
2692
2693         for (x=0; x < (ssize_t) image->columns; x++)
2694           indexes[x]=(IndexPacket) (*r++);
2695
2696         if (SyncAuthenticPixels(image,exception) == MagickFalse)
2697           break;
2698
2699         if ((image->previous == (Image *) NULL) && (num_passes == 1))
2700           {
2701             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2702                 image->rows);
2703
2704             if (status == MagickFalse)
2705               break;
2706           }
2707       }
2708
2709       if ((image->previous == (Image *) NULL) && (num_passes != 1))
2710         {
2711           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
2712
2713           if (status == MagickFalse)
2714             break;
2715         }
2716
2717       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2718     }
2719
2720     image->matte=found_transparent_pixel;
2721
2722     if (logging != MagickFalse)
2723       {
2724         if (found_transparent_pixel != MagickFalse)
2725           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2726             "    Found transparent pixel");
2727         else
2728           {
2729             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2730               "    No transparent pixel was found");
2731           
2732             ping_color_type&=0x03;
2733           }
2734       }
2735     }
2736
2737   if (quantum_info != (QuantumInfo *) NULL)
2738     quantum_info=DestroyQuantumInfo(quantum_info);
2739
2740   if (image->storage_class == PseudoClass)
2741     {
2742       MagickBooleanType
2743         matte;
2744
2745       matte=image->matte;
2746       image->matte=MagickFalse;
2747       (void) SyncImage(image);
2748       image->matte=matte;
2749     }
2750
2751   png_read_end(ping,ping_info);
2752
2753   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
2754       (ssize_t) image_info->first_scene && image->delay != 0)
2755     {
2756       png_destroy_read_struct(&ping,&ping_info,&end_info);
2757       ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2758       image->colors=2;
2759       (void) SetImageBackgroundColor(image);
2760 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
2761       UnlockSemaphoreInfo(ping_semaphore);
2762 #endif
2763       if (logging != MagickFalse)
2764         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2765           "  exit ReadOnePNGImage() early.");
2766       return(image);
2767     }
2768
2769   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2770     {
2771       ClassType
2772         storage_class;
2773
2774       /*
2775         Image has a transparent background.
2776       */
2777       storage_class=image->storage_class;
2778       image->matte=MagickTrue;
2779
2780 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
2781
2782       if (storage_class == PseudoClass)
2783         {
2784           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2785             {
2786               for (x=0; x < ping_num_trans; x++)
2787               {
2788                  image->colormap[x].opacity =
2789                    ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
2790               }
2791             }
2792
2793           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2794             {
2795               for (x=0; x < (int) image->colors; x++)
2796               {
2797                  if (ScaleQuantumToShort(image->colormap[x].red) ==
2798                      transparent_color.opacity)
2799                  {
2800                     image->colormap[x].opacity = (Quantum) TransparentOpacity;
2801                  }
2802               }
2803             }
2804           (void) SyncImage(image);
2805         }
2806
2807 #if 1 /* Should have already been done above, but glennrp problem P10
2808        * needs this.
2809        */
2810       else
2811         {
2812           for (y=0; y < (ssize_t) image->rows; y++)
2813           {
2814             image->storage_class=storage_class;
2815             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
2816
2817             if (q == (PixelPacket *) NULL)
2818               break;
2819
2820             indexes=GetAuthenticIndexQueue(image);
2821
2822             /* Caution: on a Q8 build, this does not distinguish between
2823              * 16-bit colors that differ only in the low byte
2824              */
2825             for (x=(ssize_t) image->columns-1; x >= 0; x--)
2826             {
2827               if (ScaleQuantumToShort(q->red) == transparent_color.red &&
2828                   ScaleQuantumToShort(q->green) == transparent_color.green &&
2829                   ScaleQuantumToShort(q->blue) == transparent_color.blue)
2830                 {
2831                   q->opacity=(Quantum) TransparentOpacity;
2832                 }
2833
2834 #if 0 /* I have not found a case where this is needed. */
2835               else
2836                 {
2837                   q->opacity=(Quantum) OpaqueOpacity;
2838                 }
2839 #endif
2840
2841               q++;
2842             }
2843
2844             if (SyncAuthenticPixels(image,exception) == MagickFalse)
2845                break;
2846           }
2847         }
2848 #endif
2849
2850       image->storage_class=DirectClass;
2851     }
2852
2853   if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2854       (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2855     image->colorspace=GRAYColorspace;
2856
2857   if (png_get_text(ping,ping_info,&text,&num_text) != 0)
2858     for (i=0; i < (ssize_t) num_text; i++)
2859     {
2860       /* Check for a profile */
2861
2862       if (logging != MagickFalse)
2863         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2864           "    Reading PNG text chunk");
2865
2866       if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2867           (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
2868
2869       else
2870         {
2871           char
2872             *value;
2873
2874           length=text[i].text_length;
2875           value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2876             sizeof(*value));
2877           if (value == (char *) NULL)
2878             {
2879               (void) ThrowMagickException(&image->exception,GetMagickModule(),
2880                 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2881                 image->filename);
2882               break;
2883             }
2884           *value='\0';
2885           (void) ConcatenateMagickString(value,text[i].text,length+2);
2886
2887           /* Don't save "density" or "units" property if we have a pHYs
2888            * chunk
2889            */
2890           if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
2891               (LocaleCompare(text[i].key,"density") != 0 &&
2892               LocaleCompare(text[i].key,"units") != 0))
2893              (void) SetImageProperty(image,text[i].key,value);
2894
2895           if (logging != MagickFalse)
2896           {
2897             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2898               "      length: %lu",(unsigned long) length);
2899             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2900               "      Keyword: %s",text[i].key);
2901           }
2902
2903           value=DestroyString(value);
2904         }
2905     }
2906
2907 #ifdef MNG_OBJECT_BUFFERS
2908   /*
2909     Store the object if necessary.
2910   */
2911   if (object_id && !mng_info->frozen[object_id])
2912     {
2913       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2914         {
2915           /*
2916             create a new object buffer.
2917           */
2918           mng_info->ob[object_id]=(MngBuffer *)
2919             AcquireMagickMemory(sizeof(MngBuffer));
2920
2921           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
2922             {
2923               mng_info->ob[object_id]->image=(Image *) NULL;
2924               mng_info->ob[object_id]->reference_count=1;
2925             }
2926         }
2927
2928       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
2929           mng_info->ob[object_id]->frozen)
2930         {
2931           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
2932             (void) ThrowMagickException(&image->exception,GetMagickModule(),
2933               ResourceLimitError,"MemoryAllocationFailed","`%s'",
2934               image->filename);
2935
2936           if (mng_info->ob[object_id]->frozen)
2937             (void) ThrowMagickException(&image->exception,GetMagickModule(),
2938               ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
2939               "`%s'",image->filename);
2940         }
2941
2942       else
2943         {
2944
2945           if (mng_info->ob[object_id]->image != (Image *) NULL)
2946             mng_info->ob[object_id]->image=DestroyImage
2947                 (mng_info->ob[object_id]->image);
2948
2949           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
2950             &image->exception);
2951
2952           if (mng_info->ob[object_id]->image != (Image *) NULL)
2953             mng_info->ob[object_id]->image->file=(FILE *) NULL;
2954
2955           else
2956             (void) ThrowMagickException(&image->exception,GetMagickModule(),
2957               ResourceLimitError,"Cloning image for object buffer failed",
2958               "`%s'",image->filename);
2959
2960           if (ping_width > 250000L || ping_height > 250000L)
2961              png_error(ping,"PNG Image dimensions are too large.");
2962
2963           mng_info->ob[object_id]->width=ping_width;
2964           mng_info->ob[object_id]->height=ping_height;
2965           mng_info->ob[object_id]->color_type=ping_color_type;
2966           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
2967           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
2968           mng_info->ob[object_id]->compression_method=
2969              ping_compression_method;
2970           mng_info->ob[object_id]->filter_method=ping_filter_method;
2971
2972           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2973             {
2974               int
2975                 number_colors;
2976
2977               png_colorp
2978                 plte;
2979
2980               /*
2981                 Copy the PLTE to the object buffer.
2982               */
2983               png_get_PLTE(ping,ping_info,&plte,&number_colors);
2984               mng_info->ob[object_id]->plte_length=number_colors;
2985
2986               for (i=0; i < number_colors; i++)
2987               {
2988                 mng_info->ob[object_id]->plte[i]=plte[i];
2989               }
2990             }
2991
2992           else
2993               mng_info->ob[object_id]->plte_length=0;
2994         }
2995     }
2996 #endif
2997
2998    /* Set image->matte to MagickTrue if the input colortype supports
2999     * alpha or if a valid tRNS chunk is present, no matter whether there
3000     * is actual transparency present.
3001     */
3002     image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3003         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3004         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3005         MagickTrue : MagickFalse;
3006
3007   /*
3008     Relinquish resources.
3009   */
3010   png_destroy_read_struct(&ping,&ping_info,&end_info);
3011
3012   ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3013 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3014   UnlockSemaphoreInfo(ping_semaphore);
3015 #endif
3016
3017   if (logging != MagickFalse)
3018     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3019       "  exit ReadOnePNGImage()");
3020
3021   return(image);
3022
3023 /* end of reading one PNG image */
3024 }
3025
3026 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3027 {
3028   Image
3029     *image,
3030     *previous;
3031
3032   MagickBooleanType
3033     have_mng_structure,
3034     logging,
3035     status;
3036
3037   MngInfo
3038     *mng_info;
3039
3040   char
3041     magic_number[MaxTextExtent];
3042
3043   ssize_t
3044     count;
3045
3046   /*
3047     Open image file.
3048   */
3049   assert(image_info != (const ImageInfo *) NULL);
3050   assert(image_info->signature == MagickSignature);
3051
3052   if (image_info->debug != MagickFalse)
3053     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3054       image_info->filename);
3055
3056   assert(exception != (ExceptionInfo *) NULL);
3057   assert(exception->signature == MagickSignature);
3058   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3059   image=AcquireImage(image_info);
3060   mng_info=(MngInfo *) NULL;
3061   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3062
3063   if (status == MagickFalse)
3064     ThrowReaderException(FileOpenError,"UnableToOpenFile");
3065
3066   /*
3067     Verify PNG signature.
3068   */
3069   count=ReadBlob(image,8,(unsigned char *) magic_number);
3070
3071   if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3072     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3073
3074   /*
3075     Allocate a MngInfo structure.
3076   */
3077   have_mng_structure=MagickFalse;
3078   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3079
3080   if (mng_info == (MngInfo *) NULL)
3081     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3082
3083   /*
3084     Initialize members of the MngInfo structure.
3085   */
3086   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3087   mng_info->image=image;
3088   have_mng_structure=MagickTrue;
3089
3090   previous=image;
3091   image=ReadOnePNGImage(mng_info,image_info,exception);
3092   MngInfoFreeStruct(mng_info,&have_mng_structure);
3093
3094   if (image == (Image *) NULL)
3095     {
3096       if (previous != (Image *) NULL)
3097         {
3098           if (previous->signature != MagickSignature)
3099             ThrowReaderException(CorruptImageError,"CorruptImage");
3100
3101           (void) CloseBlob(previous);
3102           (void) DestroyImageList(previous);
3103         }
3104
3105       if (logging != MagickFalse)
3106         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3107           "exit ReadPNGImage() with error");
3108
3109       return((Image *) NULL);
3110     }
3111
3112   (void) CloseBlob(image);
3113
3114   if ((image->columns == 0) || (image->rows == 0))
3115     {
3116       if (logging != MagickFalse)
3117         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3118           "exit ReadPNGImage() with error.");
3119
3120       ThrowReaderException(CorruptImageError,"CorruptImage");
3121     }
3122
3123   if (LocaleCompare(image_info->magick,"PNG8") == 0)
3124     {
3125       (void) SetImageType(image,PaletteType);
3126
3127       if (image->matte != MagickFalse)
3128         {
3129           /* To do: Reduce to binary transparency */
3130         }
3131     }
3132
3133   if (LocaleCompare(image_info->magick,"PNG24") == 0)
3134     {
3135       (void) SetImageType(image,TrueColorType);
3136       image->matte=MagickFalse;
3137     }
3138
3139   if (LocaleCompare(image_info->magick,"PNG32") == 0)
3140     (void) SetImageType(image,TrueColorMatteType);
3141
3142   if (logging != MagickFalse)
3143     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3144         "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3145             (double) image->page.width,(double) image->page.height,
3146             (double) image->page.x,(double) image->page.y);
3147
3148   if (logging != MagickFalse)
3149     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3150
3151   return(image);
3152 }
3153
3154
3155
3156 #if defined(JNG_SUPPORTED)
3157 /*
3158 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3159 %                                                                             %
3160 %                                                                             %
3161 %                                                                             %
3162 %   R e a d O n e J N G I m a g e                                             %
3163 %                                                                             %
3164 %                                                                             %
3165 %                                                                             %
3166 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3167 %
3168 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3169 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
3170 %  necessary for the new Image structure and returns a pointer to the new
3171 %  image.
3172 %
3173 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
3174 %
3175 %  The format of the ReadOneJNGImage method is:
3176 %
3177 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3178 %         ExceptionInfo *exception)
3179 %
3180 %  A description of each parameter follows:
3181 %
3182 %    o mng_info: Specifies a pointer to a MngInfo structure.
3183 %
3184 %    o image_info: the image info.
3185 %
3186 %    o exception: return any errors or warnings in this structure.
3187 %
3188 */
3189 static Image *ReadOneJNGImage(MngInfo *mng_info,
3190     const ImageInfo *image_info, ExceptionInfo *exception)
3191 {
3192   Image
3193     *alpha_image,
3194     *color_image,
3195     *image,
3196     *jng_image;
3197
3198   ImageInfo
3199     *alpha_image_info,
3200     *color_image_info;
3201
3202   MagickBooleanType
3203     logging;
3204
3205   ssize_t
3206     y;
3207
3208   MagickBooleanType
3209     status;
3210
3211   png_uint_32
3212     jng_height,
3213     jng_width;
3214
3215   png_byte
3216     jng_color_type,
3217     jng_image_sample_depth,
3218     jng_image_compression_method,
3219     jng_image_interlace_method,
3220     jng_alpha_sample_depth,
3221     jng_alpha_compression_method,
3222     jng_alpha_filter_method,
3223     jng_alpha_interlace_method;
3224
3225   register const PixelPacket
3226     *s;
3227
3228   register ssize_t
3229     i,
3230     x;
3231
3232   register PixelPacket
3233     *q;
3234
3235   register unsigned char
3236     *p;
3237
3238   unsigned int
3239     read_JSEP,
3240     reading_idat,
3241     skip_to_iend;
3242
3243   size_t
3244     length;
3245
3246   jng_alpha_compression_method=0;
3247   jng_alpha_sample_depth=8;
3248   jng_color_type=0;
3249   jng_height=0;
3250   jng_width=0;
3251   alpha_image=(Image *) NULL;
3252   color_image=(Image *) NULL;
3253   alpha_image_info=(ImageInfo *) NULL;
3254   color_image_info=(ImageInfo *) NULL;
3255
3256   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3257     "  Enter ReadOneJNGImage()");
3258
3259   image=mng_info->image;
3260
3261   if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3262     {
3263       /*
3264         Allocate next image structure.
3265       */
3266       if (logging != MagickFalse)
3267         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3268            "  AcquireNextImage()");
3269
3270       AcquireNextImage(image_info,image);
3271
3272       if (GetNextImageInList(image) == (Image *) NULL)
3273         return((Image *) NULL);
3274
3275       image=SyncNextImageInList(image);
3276     }
3277   mng_info->image=image;
3278
3279   /*
3280     Signature bytes have already been read.
3281   */
3282
3283   read_JSEP=MagickFalse;
3284   reading_idat=MagickFalse;
3285   skip_to_iend=MagickFalse;
3286   for (;;)
3287   {
3288     char
3289       type[MaxTextExtent];
3290
3291     unsigned char
3292       *chunk;
3293
3294     unsigned int
3295       count;
3296
3297     /*
3298       Read a new JNG chunk.
3299     */
3300     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3301       2*GetBlobSize(image));
3302
3303     if (status == MagickFalse)
3304       break;
3305
3306     type[0]='\0';
3307     (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3308     length=ReadBlobMSBLong(image);
3309     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3310
3311     if (logging != MagickFalse)
3312       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3313         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
3314         type[0],type[1],type[2],type[3],(double) length);
3315
3316     if (length > PNG_UINT_31_MAX || count == 0)
3317       ThrowReaderException(CorruptImageError,"CorruptImage");
3318
3319     p=NULL;
3320     chunk=(unsigned char *) NULL;
3321
3322     if (length)
3323       {
3324         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3325
3326         if (chunk == (unsigned char *) NULL)
3327           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3328
3329         for (i=0; i < (ssize_t) length; i++)
3330           chunk[i]=(unsigned char) ReadBlobByte(image);
3331
3332         p=chunk;
3333       }
3334
3335     (void) ReadBlobMSBLong(image);  /* read crc word */
3336
3337     if (skip_to_iend)
3338       {
3339         if (length)
3340           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3341
3342         continue;
3343       }
3344
3345     if (memcmp(type,mng_JHDR,4) == 0)
3346       {
3347         if (length == 16)
3348           {
3349             jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3350                 (p[2] << 8) | p[3]);
3351             jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3352                 (p[6] << 8) | p[7]);
3353             jng_color_type=p[8];
3354             jng_image_sample_depth=p[9];
3355             jng_image_compression_method=p[10];
3356             jng_image_interlace_method=p[11];
3357
3358             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3359               NoInterlace;
3360
3361             jng_alpha_sample_depth=p[12];
3362             jng_alpha_compression_method=p[13];
3363             jng_alpha_filter_method=p[14];
3364             jng_alpha_interlace_method=p[15];
3365
3366             if (logging != MagickFalse)
3367               {
3368                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3369                   "    jng_width:      %16lu",(unsigned long) jng_width);
3370
3371                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3372                   "    jng_width:      %16lu",(unsigned long) jng_height);
3373
3374                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3375                   "    jng_color_type: %16d",jng_color_type);
3376
3377                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3378                   "    jng_image_sample_depth:      %3d",
3379                   jng_image_sample_depth);
3380
3381                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3382                   "    jng_image_compression_method:%3d",
3383                   jng_image_compression_method);
3384
3385                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3386                   "    jng_image_interlace_method:  %3d",
3387                   jng_image_interlace_method);
3388
3389                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3390                   "    jng_alpha_sample_depth:      %3d",
3391                   jng_alpha_sample_depth);
3392
3393                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3394                   "    jng_alpha_compression_method:%3d",
3395                   jng_alpha_compression_method);
3396
3397                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3398                   "    jng_alpha_filter_method:     %3d",
3399                   jng_alpha_filter_method);
3400
3401                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3402                   "    jng_alpha_interlace_method:  %3d",
3403                   jng_alpha_interlace_method);
3404               }
3405           }
3406
3407         if (length)
3408           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3409
3410         continue;
3411       }
3412
3413
3414     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3415         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3416          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3417       {
3418         /*
3419            o create color_image
3420            o open color_blob, attached to color_image
3421            o if (color type has alpha)
3422                open alpha_blob, attached to alpha_image
3423         */
3424
3425         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
3426
3427         if (color_image_info == (ImageInfo *) NULL)
3428           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3429
3430         GetImageInfo(color_image_info);
3431         color_image=AcquireImage(color_image_info);
3432
3433         if (color_image == (Image *) NULL)
3434           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3435
3436         if (logging != MagickFalse)
3437           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3438             "    Creating color_blob.");
3439
3440         (void) AcquireUniqueFilename(color_image->filename);
3441         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3442           exception);
3443
3444         if (status == MagickFalse)
3445           return((Image *) NULL);
3446
3447         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3448           {
3449             alpha_image_info=(ImageInfo *)
3450               AcquireMagickMemory(sizeof(ImageInfo));
3451
3452             if (alpha_image_info == (ImageInfo *) NULL)
3453               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3454
3455             GetImageInfo(alpha_image_info);
3456             alpha_image=AcquireImage(alpha_image_info);
3457
3458             if (alpha_image == (Image *) NULL)
3459               {
3460                 alpha_image=DestroyImage(alpha_image);
3461                 ThrowReaderException(ResourceLimitError,
3462                   "MemoryAllocationFailed");
3463               }
3464
3465             if (logging != MagickFalse)
3466               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3467                 "    Creating alpha_blob.");
3468
3469             (void) AcquireUniqueFilename(alpha_image->filename);
3470             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3471               exception);
3472
3473             if (status == MagickFalse)
3474               return((Image *) NULL);
3475
3476             if (jng_alpha_compression_method == 0)
3477               {
3478                 unsigned char
3479                   data[18];
3480
3481                 if (logging != MagickFalse)
3482                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3483                     "    Writing IHDR chunk to alpha_blob.");
3484
3485                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3486                   "\211PNG\r\n\032\n");
3487
3488                 (void) WriteBlobMSBULong(alpha_image,13L);
3489                 PNGType(data,mng_IHDR);
3490                 LogPNGChunk(logging,mng_IHDR,13L);
3491                 PNGLong(data+4,jng_width);
3492                 PNGLong(data+8,jng_height);
3493                 data[12]=jng_alpha_sample_depth;
3494                 data[13]=0; /* color_type gray */
3495                 data[14]=0; /* compression method 0 */
3496                 data[15]=0; /* filter_method 0 */
3497                 data[16]=0; /* interlace_method 0 */
3498                 (void) WriteBlob(alpha_image,17,data);
3499                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3500               }
3501           }
3502         reading_idat=MagickTrue;
3503       }
3504
3505     if (memcmp(type,mng_JDAT,4) == 0)
3506       {
3507         /* Copy chunk to color_image->blob */
3508
3509         if (logging != MagickFalse)
3510           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3511             "    Copying JDAT chunk data to color_blob.");
3512
3513         (void) WriteBlob(color_image,length,chunk);
3514
3515         if (length)
3516           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3517
3518         continue;
3519       }
3520
3521     if (memcmp(type,mng_IDAT,4) == 0)
3522       {
3523         png_byte
3524            data[5];
3525
3526         /* Copy IDAT header and chunk data to alpha_image->blob */
3527
3528         if (image_info->ping == MagickFalse)
3529           {
3530             if (logging != MagickFalse)
3531               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3532                 "    Copying IDAT chunk data to alpha_blob.");
3533
3534             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
3535             PNGType(data,mng_IDAT);
3536             LogPNGChunk(logging,mng_IDAT,length);
3537             (void) WriteBlob(alpha_image,4,data);
3538             (void) WriteBlob(alpha_image,length,chunk);
3539             (void) WriteBlobMSBULong(alpha_image,
3540               crc32(crc32(0,data,4),chunk,(uInt) length));
3541           }
3542
3543         if (length)
3544           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3545
3546         continue;
3547       }
3548
3549     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3550       {
3551         /* Copy chunk data to alpha_image->blob */
3552
3553         if (image_info->ping == MagickFalse)
3554           {
3555             if (logging != MagickFalse)
3556               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3557                 "    Copying JDAA chunk data to alpha_blob.");
3558
3559             (void) WriteBlob(alpha_image,length,chunk);
3560           }
3561
3562         if (length)
3563           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3564
3565         continue;
3566       }
3567
3568     if (memcmp(type,mng_JSEP,4) == 0)
3569       {
3570         read_JSEP=MagickTrue;
3571
3572         if (length)
3573           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3574
3575         continue;
3576       }
3577
3578     if (memcmp(type,mng_bKGD,4) == 0)
3579       {
3580         if (length == 2)
3581           {
3582             image->background_color.red=ScaleCharToQuantum(p[1]);
3583             image->background_color.green=image->background_color.red;
3584             image->background_color.blue=image->background_color.red;
3585           }
3586
3587         if (length == 6)
3588           {
3589             image->background_color.red=ScaleCharToQuantum(p[1]);
3590             image->background_color.green=ScaleCharToQuantum(p[3]);
3591             image->background_color.blue=ScaleCharToQuantum(p[5]);
3592           }
3593
3594         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3595         continue;
3596       }
3597
3598     if (memcmp(type,mng_gAMA,4) == 0)
3599       {
3600         if (length == 4)
3601           image->gamma=((float) mng_get_long(p))*0.00001;
3602
3603         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3604         continue;
3605       }
3606
3607     if (memcmp(type,mng_cHRM,4) == 0)
3608       {
3609         if (length == 32)
3610           {
3611             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3612             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3613             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3614             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3615             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3616             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3617             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3618             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3619           }
3620
3621         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3622         continue;
3623       }
3624
3625     if (memcmp(type,mng_sRGB,4) == 0)
3626       {
3627         if (length == 1)
3628           {
3629             image->rendering_intent=
3630               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
3631             image->gamma=0.45455f;
3632             image->chromaticity.red_primary.x=0.6400f;
3633             image->chromaticity.red_primary.y=0.3300f;
3634             image->chromaticity.green_primary.x=0.3000f;
3635             image->chromaticity.green_primary.y=0.6000f;
3636             image->chromaticity.blue_primary.x=0.1500f;
3637             image->chromaticity.blue_primary.y=0.0600f;
3638             image->chromaticity.white_point.x=0.3127f;
3639             image->chromaticity.white_point.y=0.3290f;
3640           }
3641
3642         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3643         continue;
3644       }
3645
3646     if (memcmp(type,mng_oFFs,4) == 0)
3647       {
3648         if (length > 8)
3649           {
3650             image->page.x=(ssize_t) mng_get_long(p);
3651             image->page.y=(ssize_t) mng_get_long(&p[4]);
3652
3653             if ((int) p[8] != 0)
3654               {
3655                 image->page.x/=10000;
3656                 image->page.y/=10000;
3657               }
3658           }
3659
3660         if (length)
3661           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3662
3663         continue;
3664       }
3665
3666     if (memcmp(type,mng_pHYs,4) == 0)
3667       {
3668         if (length > 8)
3669           {
3670             image->x_resolution=(double) mng_get_long(p);
3671             image->y_resolution=(double) mng_get_long(&p[4]);
3672             if ((int) p[8] == PNG_RESOLUTION_METER)
3673               {
3674                 image->units=PixelsPerCentimeterResolution;
3675                 image->x_resolution=image->x_resolution/100.0f;
3676                 image->y_resolution=image->y_resolution/100.0f;
3677               }
3678           }
3679
3680         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3681         continue;
3682       }
3683
3684 #if 0
3685     if (memcmp(type,mng_iCCP,4) == 0)
3686       {
3687         /* To do: */
3688         if (length)
3689           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3690
3691         continue;
3692       }
3693 #endif
3694
3695     if (length)
3696       chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3697
3698     if (memcmp(type,mng_IEND,4))
3699       continue;
3700
3701     break;
3702   }
3703
3704
3705   /* IEND found */
3706
3707   /*
3708     Finish up reading image data:
3709
3710        o read main image from color_blob.
3711
3712        o close color_blob.
3713
3714        o if (color_type has alpha)
3715             if alpha_encoding is PNG
3716                read secondary image from alpha_blob via ReadPNG
3717             if alpha_encoding is JPEG
3718                read secondary image from alpha_blob via ReadJPEG
3719
3720        o close alpha_blob.
3721
3722        o copy intensity of secondary image into
3723          opacity samples of main image.
3724
3725        o destroy the secondary image.
3726   */
3727
3728   (void) CloseBlob(color_image);
3729
3730   if (logging != MagickFalse)
3731     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3732       "    Reading jng_image from color_blob.");
3733
3734   (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3735     color_image->filename);
3736
3737   color_image_info->ping=MagickFalse;   /* To do: avoid this */
3738   jng_image=ReadImage(color_image_info,exception);
3739
3740   if (jng_image == (Image *) NULL)
3741     return((Image *) NULL);
3742
3743   (void) RelinquishUniqueFileResource(color_image->filename);
3744   color_image=DestroyImage(color_image);
3745   color_image_info=DestroyImageInfo(color_image_info);
3746
3747   if (jng_image == (Image *) NULL)
3748     return((Image *) NULL);
3749
3750   if (logging != MagickFalse)
3751     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3752       "    Copying jng_image pixels to main image.");
3753
3754   image->rows=jng_height;
3755   image->columns=jng_width;
3756   length=image->columns*sizeof(PixelPacket);
3757
3758   for (y=0; y < (ssize_t) image->rows; y++)
3759   {
3760     s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3761     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3762     (void) CopyMagickMemory(q,s,length);
3763
3764     if (SyncAuthenticPixels(image,exception) == MagickFalse)
3765       break;
3766   }
3767
3768   jng_image=DestroyImage(jng_image);
3769
3770   if (image_info->ping == MagickFalse)
3771     {
3772      if (jng_color_type >= 12)
3773        {
3774          if (jng_alpha_compression_method == 0)
3775            {
3776              png_byte
3777                data[5];
3778              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3779              PNGType(data,mng_IEND);
3780              LogPNGChunk(logging,mng_IEND,0L);
3781              (void) WriteBlob(alpha_image,4,data);
3782              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3783            }
3784
3785          (void) CloseBlob(alpha_image);
3786
3787          if (logging != MagickFalse)
3788            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3789              "    Reading opacity from alpha_blob.");
3790
3791          (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3792            "%s",alpha_image->filename);
3793
3794          jng_image=ReadImage(alpha_image_info,exception);
3795
3796          if (jng_image != (Image *) NULL)
3797            for (y=0; y < (ssize_t) image->rows; y++)
3798            {
3799              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3800                 &image->exception);
3801              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3802
3803              if (image->matte != MagickFalse)
3804                for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3805                   q->opacity=(Quantum) QuantumRange-s->red;
3806
3807              else
3808                for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3809                {
3810                   q->opacity=(Quantum) QuantumRange-s->red;
3811                   if (q->opacity != OpaqueOpacity)
3812                     image->matte=MagickTrue;
3813                }
3814
3815              if (SyncAuthenticPixels(image,exception) == MagickFalse)
3816                break;
3817            }
3818          (void) RelinquishUniqueFileResource(alpha_image->filename);
3819          alpha_image=DestroyImage(alpha_image);
3820          alpha_image_info=DestroyImageInfo(alpha_image_info);
3821          if (jng_image != (Image *) NULL)
3822            jng_image=DestroyImage(jng_image);
3823        }
3824     }
3825
3826   /* Read the JNG image.  */
3827
3828   if (mng_info->mng_type == 0)
3829     {
3830       mng_info->mng_width=jng_width;
3831       mng_info->mng_height=jng_height;
3832     }
3833
3834   if (image->page.width == 0 && image->page.height == 0)
3835     {
3836       image->page.width=jng_width;
3837       image->page.height=jng_height;
3838     }
3839
3840   if (image->page.x == 0 && image->page.y == 0)
3841     {
3842       image->page.x=mng_info->x_off[mng_info->object_id];
3843       image->page.y=mng_info->y_off[mng_info->object_id];
3844     }
3845
3846   else
3847     {
3848       image->page.y=mng_info->y_off[mng_info->object_id];
3849     }
3850
3851   mng_info->image_found++;
3852   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3853     2*GetBlobSize(image));
3854
3855   if (logging != MagickFalse)
3856     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3857       "  exit ReadOneJNGImage()");
3858
3859   return(image);
3860 }
3861
3862 /*
3863 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3864 %                                                                             %
3865 %                                                                             %
3866 %                                                                             %
3867 %   R e a d J N G I m a g e                                                   %
3868 %                                                                             %
3869 %                                                                             %
3870 %                                                                             %
3871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3872 %
3873 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3874 %  (including the 8-byte signature)  and returns it.  It allocates the memory
3875 %  necessary for the new Image structure and returns a pointer to the new
3876 %  image.
3877 %
3878 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
3879 %
3880 %  The format of the ReadJNGImage method is:
3881 %
3882 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3883 %         *exception)
3884 %
3885 %  A description of each parameter follows:
3886 %
3887 %    o image_info: the image info.
3888 %
3889 %    o exception: return any errors or warnings in this structure.
3890 %
3891 */
3892
3893 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3894 {
3895   Image
3896     *image,
3897     *previous;
3898
3899   MagickBooleanType
3900     have_mng_structure,
3901     logging,
3902     status;
3903
3904   MngInfo
3905     *mng_info;
3906
3907   char
3908     magic_number[MaxTextExtent];
3909
3910   size_t
3911     count;
3912
3913   /*
3914     Open image file.
3915   */
3916   assert(image_info != (const ImageInfo *) NULL);
3917   assert(image_info->signature == MagickSignature);
3918   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3919   assert(exception != (ExceptionInfo *) NULL);
3920   assert(exception->signature == MagickSignature);
3921   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
3922   image=AcquireImage(image_info);
3923   mng_info=(MngInfo *) NULL;
3924   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3925
3926   if (status == MagickFalse)
3927     return((Image *) NULL);
3928
3929   if (LocaleCompare(image_info->magick,"JNG") != 0)
3930     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3931
3932   /* Verify JNG signature.  */
3933
3934   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3935
3936   if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3937     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3938
3939   /* Allocate a MngInfo structure.  */
3940
3941   have_mng_structure=MagickFalse;
3942   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
3943
3944   if (mng_info == (MngInfo *) NULL)
3945     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3946
3947   /* Initialize members of the MngInfo structure.  */
3948
3949   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3950   have_mng_structure=MagickTrue;
3951
3952   mng_info->image=image;
3953   previous=image;
3954   image=ReadOneJNGImage(mng_info,image_info,exception);
3955   MngInfoFreeStruct(mng_info,&have_mng_structure);
3956
3957   if (image == (Image *) NULL)
3958     {
3959       if (IsImageObject(previous) != MagickFalse)
3960         {
3961           (void) CloseBlob(previous);
3962           (void) DestroyImageList(previous);
3963         }
3964
3965       if (logging != MagickFalse)
3966         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3967           "exit ReadJNGImage() with error");
3968
3969       return((Image *) NULL);
3970     }
3971   (void) CloseBlob(image);
3972
3973   if (image->columns == 0 || image->rows == 0)
3974     {
3975       if (logging != MagickFalse)
3976         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3977           "exit ReadJNGImage() with error");
3978
3979       ThrowReaderException(CorruptImageError,"CorruptImage");
3980     }
3981
3982   if (logging != MagickFalse)
3983     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3984
3985   return(image);
3986 }
3987 #endif
3988
3989 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3990 {
3991   char
3992     page_geometry[MaxTextExtent];
3993
3994   Image
3995     *image,
3996     *previous;
3997
3998   MagickBooleanType
3999     logging,
4000     have_mng_structure;
4001
4002   volatile int
4003     first_mng_object,
4004     object_id,
4005     term_chunk_found,
4006     skip_to_iend;
4007
4008   volatile ssize_t
4009     image_count=0;
4010
4011   MagickBooleanType
4012     status;
4013
4014   MagickOffsetType
4015     offset;
4016
4017   MngInfo
4018     *mng_info;
4019
4020   MngBox
4021     default_fb,
4022     fb,
4023     previous_fb;
4024
4025 #if defined(MNG_INSERT_LAYERS)
4026   PixelPacket
4027     mng_background_color;
4028 #endif
4029
4030   register unsigned char
4031     *p;
4032
4033   register ssize_t
4034     i;
4035
4036   size_t
4037     count;
4038
4039   ssize_t
4040     loop_level;
4041
4042   volatile short
4043     skipping_loop;
4044
4045 #if defined(MNG_INSERT_LAYERS)
4046   unsigned int
4047     mandatory_back=0;
4048 #endif
4049
4050   volatile unsigned int
4051 #ifdef MNG_OBJECT_BUFFERS
4052     mng_background_object=0,
4053 #endif
4054     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4055
4056   size_t
4057     default_frame_timeout,
4058     frame_timeout,
4059 #if defined(MNG_INSERT_LAYERS)
4060     image_height,
4061     image_width,
4062 #endif
4063     length;
4064
4065   /* These delays are all measured in image ticks_per_second,
4066    * not in MNG ticks_per_second
4067    */
4068   volatile size_t
4069     default_frame_delay,
4070     final_delay,
4071     final_image_delay,
4072     frame_delay,
4073 #if defined(MNG_INSERT_LAYERS)
4074     insert_layers,
4075 #endif
4076     mng_iterations=1,
4077     simplicity=0,
4078     subframe_height=0,
4079     subframe_width=0;
4080
4081   previous_fb.top=0;
4082   previous_fb.bottom=0;
4083   previous_fb.left=0;
4084   previous_fb.right=0;
4085   default_fb.top=0;
4086   default_fb.bottom=0;
4087   default_fb.left=0;
4088   default_fb.right=0;
4089
4090   /* Open image file.  */
4091
4092   assert(image_info != (const ImageInfo *) NULL);
4093   assert(image_info->signature == MagickSignature);
4094   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4095   assert(exception != (ExceptionInfo *) NULL);
4096   assert(exception->signature == MagickSignature);
4097   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4098   image=AcquireImage(image_info);
4099   mng_info=(MngInfo *) NULL;
4100   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4101
4102   if (status == MagickFalse)
4103     return((Image *) NULL);
4104
4105   first_mng_object=MagickFalse;
4106   skipping_loop=(-1);
4107   have_mng_structure=MagickFalse;
4108
4109   /* Allocate a MngInfo structure.  */
4110
4111   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4112
4113   if (mng_info == (MngInfo *) NULL)
4114     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4115
4116   /* Initialize members of the MngInfo structure.  */
4117
4118   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4119   mng_info->image=image;
4120   have_mng_structure=MagickTrue;
4121
4122   if (LocaleCompare(image_info->magick,"MNG") == 0)
4123     {
4124       char
4125         magic_number[MaxTextExtent];
4126
4127       /* Verify MNG signature.  */
4128       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4129       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4130         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4131
4132       /* Initialize some nonzero members of the MngInfo structure.  */
4133       for (i=0; i < MNG_MAX_OBJECTS; i++)
4134       {
4135         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4136         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4137       }
4138       mng_info->exists[0]=MagickTrue;
4139     }
4140
4141   first_mng_object=MagickTrue;
4142   mng_type=0;
4143 #if defined(MNG_INSERT_LAYERS)
4144   insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4145 #endif
4146   default_frame_delay=0;
4147   default_frame_timeout=0;
4148   frame_delay=0;
4149   final_delay=1;
4150   mng_info->ticks_per_second=1UL*image->ticks_per_second;
4151   object_id=0;
4152   skip_to_iend=MagickFalse;
4153   term_chunk_found=MagickFalse;
4154   mng_info->framing_mode=1;
4155 #if defined(MNG_INSERT_LAYERS)
4156   mandatory_back=MagickFalse;
4157 #endif
4158 #if defined(MNG_INSERT_LAYERS)
4159   mng_background_color=image->background_color;
4160 #endif
4161   default_fb=mng_info->frame;
4162   previous_fb=mng_info->frame;
4163   do
4164   {
4165     char
4166       type[MaxTextExtent];
4167
4168     if (LocaleCompare(image_info->magick,"MNG") == 0)
4169       {
4170         unsigned char
4171           *chunk;
4172
4173         /*
4174           Read a new chunk.
4175         */
4176         type[0]='\0';
4177         (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4178         length=ReadBlobMSBLong(image);
4179         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4180
4181         if (logging != MagickFalse)
4182           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4183            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
4184            type[0],type[1],type[2],type[3],(double) length);
4185
4186         if (length > PNG_UINT_31_MAX)
4187           status=MagickFalse;
4188
4189         if (count == 0)
4190           ThrowReaderException(CorruptImageError,"CorruptImage");
4191
4192         p=NULL;
4193         chunk=(unsigned char *) NULL;
4194
4195         if (length)
4196           {
4197             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4198
4199             if (chunk == (unsigned char *) NULL)
4200               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4201
4202             for (i=0; i < (ssize_t) length; i++)
4203               chunk[i]=(unsigned char) ReadBlobByte(image);
4204
4205             p=chunk;
4206           }
4207
4208         (void) ReadBlobMSBLong(image);  /* read crc word */
4209
4210 #if !defined(JNG_SUPPORTED)
4211         if (memcmp(type,mng_JHDR,4) == 0)
4212           {
4213             skip_to_iend=MagickTrue;
4214
4215             if (mng_info->jhdr_warning == 0)
4216               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4217                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4218
4219             mng_info->jhdr_warning++;
4220           }
4221 #endif
4222         if (memcmp(type,mng_DHDR,4) == 0)
4223           {
4224             skip_to_iend=MagickTrue;
4225
4226             if (mng_info->dhdr_warning == 0)
4227               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4228                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4229
4230             mng_info->dhdr_warning++;
4231           }
4232         if (memcmp(type,mng_MEND,4) == 0)
4233           break;
4234
4235         if (skip_to_iend)
4236           {
4237             if (memcmp(type,mng_IEND,4) == 0)
4238               skip_to_iend=MagickFalse;
4239
4240             if (length)
4241               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4242
4243             if (logging != MagickFalse)
4244               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4245                 "  Skip to IEND.");
4246
4247             continue;
4248           }
4249
4250         if (memcmp(type,mng_MHDR,4) == 0)
4251           {
4252             mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4253                 (p[2] << 8) | p[3]);
4254
4255             mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4256                 (p[6] << 8) | p[7]);
4257
4258             if (logging != MagickFalse)
4259               {
4260                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4261                   "  MNG width: %.20g",(double) mng_info->mng_width);
4262                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4263                   "  MNG height: %.20g",(double) mng_info->mng_height);
4264               }
4265
4266             p+=8;
4267             mng_info->ticks_per_second=(size_t) mng_get_long(p);
4268
4269             if (mng_info->ticks_per_second == 0)
4270               default_frame_delay=0;
4271
4272             else
4273               default_frame_delay=1UL*image->ticks_per_second/
4274                 mng_info->ticks_per_second;
4275
4276             frame_delay=default_frame_delay;
4277             simplicity=0;
4278
4279             if (length > 16)
4280               {
4281                 p+=16;
4282                 simplicity=(size_t) mng_get_long(p);
4283               }
4284
4285             mng_type=1;    /* Full MNG */
4286
4287             if ((simplicity != 0) && ((simplicity | 11) == 11))
4288               mng_type=2; /* LC */
4289
4290             if ((simplicity != 0) && ((simplicity | 9) == 9))
4291               mng_type=3; /* VLC */
4292
4293 #if defined(MNG_INSERT_LAYERS)
4294             if (mng_type != 3)
4295               insert_layers=MagickTrue;
4296 #endif
4297             if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4298               {
4299                 /* Allocate next image structure.  */
4300                 AcquireNextImage(image_info,image);
4301
4302                 if (GetNextImageInList(image) == (Image *) NULL)
4303                   return((Image *) NULL);
4304
4305                 image=SyncNextImageInList(image);
4306                 mng_info->image=image;
4307               }
4308
4309             if ((mng_info->mng_width > 65535L) ||
4310                 (mng_info->mng_height > 65535L))
4311               ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4312
4313             (void) FormatMagickString(page_geometry,MaxTextExtent,
4314               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4315               mng_info->mng_height);
4316
4317             mng_info->frame.left=0;
4318             mng_info->frame.right=(ssize_t) mng_info->mng_width;
4319             mng_info->frame.top=0;
4320             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4321             mng_info->clip=default_fb=previous_fb=mng_info->frame;
4322
4323             for (i=0; i < MNG_MAX_OBJECTS; i++)
4324               mng_info->object_clip[i]=mng_info->frame;
4325
4326             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4327             continue;
4328           }
4329
4330         if (memcmp(type,mng_TERM,4) == 0)
4331           {
4332             int
4333               repeat=0;
4334
4335
4336             if (length)
4337               repeat=p[0];
4338
4339             if (repeat == 3)
4340               {
4341                 final_delay=(png_uint_32) mng_get_long(&p[2]);
4342                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4343
4344                 if (mng_iterations == PNG_UINT_31_MAX)
4345                   mng_iterations=0;
4346
4347                 image->iterations=mng_iterations;
4348                 term_chunk_found=MagickTrue;
4349               }
4350
4351             if (logging != MagickFalse)
4352               {
4353                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4354                   "    repeat=%d",repeat);
4355
4356                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4357                   "    final_delay=%.20g",(double) final_delay);
4358
4359                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4360                   "    image->iterations=%.20g",(double) image->iterations);
4361               }
4362
4363             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4364             continue;
4365           }
4366         if (memcmp(type,mng_DEFI,4) == 0)
4367           {
4368             if (mng_type == 3)
4369               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4370                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4371                 image->filename);
4372
4373             object_id=(p[0] << 8) | p[1];
4374
4375             if (mng_type == 2 && object_id != 0)
4376               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4377                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4378                 image->filename);
4379
4380             if (object_id > MNG_MAX_OBJECTS)
4381               {
4382                 /*
4383                   Instead ofsuing a warning we should allocate a larger
4384                   MngInfo structure and continue.
4385                 */
4386                 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4387                   CoderError,"object id too large","`%s'",image->filename);
4388                 object_id=MNG_MAX_OBJECTS;
4389               }
4390
4391             if (mng_info->exists[object_id])
4392               if (mng_info->frozen[object_id])
4393                 {
4394                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4395                   (void) ThrowMagickException(&image->exception,
4396                     GetMagickModule(),CoderError,
4397                     "DEFI cannot redefine a frozen MNG object","`%s'",
4398                     image->filename);
4399                   continue;
4400                 }
4401
4402             mng_info->exists[object_id]=MagickTrue;
4403
4404             if (length > 2)
4405               mng_info->invisible[object_id]=p[2];
4406
4407             /*
4408               Extract object offset info.
4409             */
4410             if (length > 11)
4411               {
4412                 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4413                     (p[5] << 16) | (p[6] << 8) | p[7]);
4414
4415                 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4416                     (p[9] << 16) | (p[10] << 8) | p[11]);
4417
4418                 if (logging != MagickFalse)
4419                   {
4420                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4421                       "  x_off[%d]: %.20g",object_id,(double)
4422                       mng_info->x_off[object_id]);
4423
4424                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4425                       "  y_off[%d]: %.20g",object_id,(double)
4426                       mng_info->y_off[object_id]);
4427                   }
4428               }
4429
4430             /*
4431               Extract object clipping info.
4432             */
4433             if (length > 27)
4434               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4435                 &p[12]);
4436
4437             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4438             continue;
4439           }
4440         if (memcmp(type,mng_bKGD,4) == 0)
4441           {
4442             mng_info->have_global_bkgd=MagickFalse;
4443
4444             if (length > 5)
4445               {
4446                 mng_info->mng_global_bkgd.red=
4447                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4448
4449                 mng_info->mng_global_bkgd.green=
4450                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4451
4452                 mng_info->mng_global_bkgd.blue=
4453                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4454
4455                 mng_info->have_global_bkgd=MagickTrue;
4456               }
4457
4458             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4459             continue;
4460           }
4461         if (memcmp(type,mng_BACK,4) == 0)
4462           {
4463 #if defined(MNG_INSERT_LAYERS)
4464             if (length > 6)
4465               mandatory_back=p[6];
4466
4467             else
4468               mandatory_back=0;
4469
4470             if (mandatory_back && length > 5)
4471               {
4472                 mng_background_color.red=
4473                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4474
4475                 mng_background_color.green=
4476                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4477
4478                 mng_background_color.blue=
4479                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4480
4481                 mng_background_color.opacity=OpaqueOpacity;
4482               }
4483
4484 #ifdef MNG_OBJECT_BUFFERS
4485             if (length > 8)
4486               mng_background_object=(p[7] << 8) | p[8];
4487 #endif
4488 #endif
4489             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4490             continue;
4491           }
4492
4493         if (memcmp(type,mng_PLTE,4) == 0)
4494           {
4495             /* Read global PLTE.  */
4496
4497             if (length && (length < 769))
4498               {
4499                 if (mng_info->global_plte == (png_colorp) NULL)
4500                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4501                     sizeof(*mng_info->global_plte));
4502
4503                 for (i=0; i < (ssize_t) (length/3); i++)
4504                 {
4505                   mng_info->global_plte[i].red=p[3*i];
4506                   mng_info->global_plte[i].green=p[3*i+1];
4507                   mng_info->global_plte[i].blue=p[3*i+2];
4508                 }
4509
4510                 mng_info->global_plte_length=(unsigned int) (length/3);
4511               }
4512 #ifdef MNG_LOOSE
4513             for ( ; i < 256; i++)
4514             {
4515               mng_info->global_plte[i].red=i;
4516               mng_info->global_plte[i].green=i;
4517               mng_info->global_plte[i].blue=i;
4518             }
4519
4520             if (length)
4521               mng_info->global_plte_length=256;
4522 #endif
4523             else
4524               mng_info->global_plte_length=0;
4525
4526             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4527             continue;
4528           }
4529
4530         if (memcmp(type,mng_tRNS,4) == 0)
4531           {
4532             /* read global tRNS */
4533
4534             if (length < 257)
4535               for (i=0; i < (ssize_t) length; i++)
4536                 mng_info->global_trns[i]=p[i];
4537
4538 #ifdef MNG_LOOSE
4539             for ( ; i < 256; i++)
4540               mng_info->global_trns[i]=255;
4541 #endif
4542             mng_info->global_trns_length=(unsigned int) length;
4543             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4544             continue;
4545           }
4546         if (memcmp(type,mng_gAMA,4) == 0)
4547           {
4548             if (length == 4)
4549               {
4550                 ssize_t
4551                   igamma;
4552
4553                 igamma=mng_get_long(p);
4554                 mng_info->global_gamma=((float) igamma)*0.00001;
4555                 mng_info->have_global_gama=MagickTrue;
4556               }
4557
4558             else
4559               mng_info->have_global_gama=MagickFalse;
4560
4561             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4562             continue;
4563           }
4564
4565         if (memcmp(type,mng_cHRM,4) == 0)
4566           {
4567             /* Read global cHRM */
4568
4569             if (length == 32)
4570               {
4571                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4572                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4573                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4574                 mng_info->global_chrm.red_primary.y=0.00001*
4575                   mng_get_long(&p[12]);
4576                 mng_info->global_chrm.green_primary.x=0.00001*
4577                   mng_get_long(&p[16]);
4578                 mng_info->global_chrm.green_primary.y=0.00001*
4579                   mng_get_long(&p[20]);
4580                 mng_info->global_chrm.blue_primary.x=0.00001*
4581                   mng_get_long(&p[24]);
4582                 mng_info->global_chrm.blue_primary.y=0.00001*
4583                   mng_get_long(&p[28]);
4584                 mng_info->have_global_chrm=MagickTrue;
4585               }
4586             else
4587               mng_info->have_global_chrm=MagickFalse;
4588
4589             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4590             continue;
4591           }
4592
4593         if (memcmp(type,mng_sRGB,4) == 0)
4594           {
4595             /*
4596               Read global sRGB.
4597             */
4598             if (length)
4599               {
4600                 mng_info->global_srgb_intent=
4601                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4602                 mng_info->have_global_srgb=MagickTrue;
4603               }
4604             else
4605               mng_info->have_global_srgb=MagickFalse;
4606
4607             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4608             continue;
4609           }
4610
4611         if (memcmp(type,mng_iCCP,4) == 0)
4612           {
4613             /* To do: */
4614
4615             /*
4616               Read global iCCP.
4617             */
4618             if (length)
4619               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4620
4621             continue;
4622           }
4623
4624         if (memcmp(type,mng_FRAM,4) == 0)
4625           {
4626             if (mng_type == 3)
4627               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4628                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4629                 image->filename);
4630
4631             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4632               image->delay=frame_delay;
4633
4634             frame_delay=default_frame_delay;
4635             frame_timeout=default_frame_timeout;
4636             fb=default_fb;
4637
4638             if (length)
4639               if (p[0])
4640                 mng_info->framing_mode=p[0];
4641
4642             if (logging != MagickFalse)
4643               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4644                 "    Framing_mode=%d",mng_info->framing_mode);
4645
4646             if (length > 6)
4647               {
4648                 /* Note the delay and frame clipping boundaries.  */
4649
4650                 p++; /* framing mode */
4651
4652                 while (*p && ((p-chunk) < (ssize_t) length))
4653                   p++;  /* frame name */
4654
4655                 p++;  /* frame name terminator */
4656
4657                 if ((p-chunk) < (ssize_t) (length-4))
4658                   {
4659                     int
4660                       change_delay,
4661                       change_timeout,
4662                       change_clipping;
4663
4664                     change_delay=(*p++);
4665                     change_timeout=(*p++);
4666                     change_clipping=(*p++);
4667                     p++; /* change_sync */
4668
4669                     if (change_delay)
4670                       {
4671                         frame_delay=1UL*image->ticks_per_second*
4672                           mng_get_long(p);
4673
4674                         if (mng_info->ticks_per_second != 0)
4675                           frame_delay/=mng_info->ticks_per_second;
4676
4677                         else
4678                           frame_delay=PNG_UINT_31_MAX;
4679
4680                         if (change_delay == 2)
4681                           default_frame_delay=frame_delay;
4682
4683                         p+=4;
4684
4685                         if (logging != MagickFalse)
4686                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4687                             "    Framing_delay=%.20g",(double) frame_delay);
4688                       }
4689
4690                     if (change_timeout)
4691                       {
4692                         frame_timeout=1UL*image->ticks_per_second*
4693                           mng_get_long(p);
4694
4695                         if (mng_info->ticks_per_second != 0)
4696                           frame_timeout/=mng_info->ticks_per_second;
4697
4698                         else
4699                           frame_timeout=PNG_UINT_31_MAX;
4700
4701                         if (change_delay == 2)
4702                           default_frame_timeout=frame_timeout;
4703
4704                         p+=4;
4705
4706                         if (logging != MagickFalse)
4707                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4708                             "    Framing_timeout=%.20g",(double) frame_timeout);
4709                       }
4710
4711                     if (change_clipping)
4712                       {
4713                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4714                         p+=17;
4715                         previous_fb=fb;
4716
4717                         if (logging != MagickFalse)
4718                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4719                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
4720                             (double) fb.left,(double) fb.right,(double) fb.top,
4721                             (double) fb.bottom);
4722
4723                         if (change_clipping == 2)
4724                           default_fb=fb;
4725                       }
4726                   }
4727               }
4728             mng_info->clip=fb;
4729             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4730
4731             subframe_width=(size_t) (mng_info->clip.right
4732                -mng_info->clip.left);
4733
4734             subframe_height=(size_t) (mng_info->clip.bottom
4735                -mng_info->clip.top);
4736             /*
4737               Insert a background layer behind the frame if framing_mode is 4.
4738             */
4739 #if defined(MNG_INSERT_LAYERS)
4740             if (logging != MagickFalse)
4741               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4742                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
4743                 subframe_width,(double) subframe_height);
4744
4745             if (insert_layers && (mng_info->framing_mode == 4) &&
4746                 (subframe_width) && (subframe_height))
4747               {
4748                 /* Allocate next image structure.  */
4749                 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4750                   {
4751                     AcquireNextImage(image_info,image);
4752
4753                     if (GetNextImageInList(image) == (Image *) NULL)
4754                       {
4755                         image=DestroyImageList(image);
4756                         MngInfoFreeStruct(mng_info,&have_mng_structure);
4757                         return((Image *) NULL);
4758                       }
4759
4760                     image=SyncNextImageInList(image);
4761                   }
4762
4763                 mng_info->image=image;
4764
4765                 if (term_chunk_found)
4766                   {
4767                     image->start_loop=MagickTrue;
4768                     image->iterations=mng_iterations;
4769                     term_chunk_found=MagickFalse;
4770                   }
4771
4772                 else
4773                     image->start_loop=MagickFalse;
4774
4775                 image->columns=subframe_width;
4776                 image->rows=subframe_height;
4777                 image->page.width=subframe_width;
4778                 image->page.height=subframe_height;
4779                 image->page.x=mng_info->clip.left;
4780                 image->page.y=mng_info->clip.top;
4781                 image->background_color=mng_background_color;
4782                 image->matte=MagickFalse;
4783                 image->delay=0;
4784                 (void) SetImageBackgroundColor(image);
4785
4786                 if (logging != MagickFalse)
4787                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4788                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4789                     (double) mng_info->clip.left,(double) mng_info->clip.right,
4790                     (double) mng_info->clip.top,(double) mng_info->clip.bottom);
4791               }
4792 #endif
4793             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4794             continue;
4795           }
4796         if (memcmp(type,mng_CLIP,4) == 0)
4797           {
4798             unsigned int
4799               first_object,
4800               last_object;
4801
4802             /*
4803               Read CLIP.
4804             */
4805             first_object=(p[0] << 8) | p[1];
4806             last_object=(p[2] << 8) | p[3];
4807
4808             for (i=(int) first_object; i <= (int) last_object; i++)
4809             {
4810               if (mng_info->exists[i] && !mng_info->frozen[i])
4811                 {
4812                   MngBox
4813                     box;
4814
4815                   box=mng_info->object_clip[i];
4816                   mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4817                 }
4818             }
4819
4820             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4821             continue;
4822           }
4823         if (memcmp(type,mng_SAVE,4) == 0)
4824           {
4825             for (i=1; i < MNG_MAX_OBJECTS; i++)
4826               if (mng_info->exists[i])
4827                 {
4828                  mng_info->frozen[i]=MagickTrue;
4829 #ifdef MNG_OBJECT_BUFFERS
4830                  if (mng_info->ob[i] != (MngBuffer *) NULL)
4831                     mng_info->ob[i]->frozen=MagickTrue;
4832 #endif
4833                 }
4834
4835             if (length)
4836               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4837
4838             continue;
4839           }
4840
4841         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4842           {
4843             /* Read DISC or SEEK.  */
4844
4845             if ((length == 0) || !memcmp(type,mng_SEEK,4))
4846               {
4847                 for (i=1; i < MNG_MAX_OBJECTS; i++)
4848                   MngInfoDiscardObject(mng_info,i);
4849               }
4850
4851             else
4852               {
4853                 register ssize_t
4854                   j;
4855
4856                 for (j=0; j < (ssize_t) length; j+=2)
4857                 {
4858                   i=p[j] << 8 | p[j+1];
4859                   MngInfoDiscardObject(mng_info,i);
4860                 }
4861               }
4862
4863             if (length)
4864               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4865
4866             continue;
4867           }
4868
4869         if (memcmp(type,mng_MOVE,4) == 0)
4870           {
4871             size_t
4872               first_object,
4873               last_object;
4874
4875             /* read MOVE */
4876
4877             first_object=(p[0] << 8) | p[1];
4878             last_object=(p[2] << 8) | p[3];
4879             for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
4880             {
4881               if (mng_info->exists[i] && !mng_info->frozen[i])
4882                 {
4883                   MngPair
4884                     new_pair;
4885
4886                   MngPair
4887                     old_pair;
4888
4889                   old_pair.a=mng_info->x_off[i];
4890                   old_pair.b=mng_info->y_off[i];
4891                   new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4892                   mng_info->x_off[i]=new_pair.a;
4893                   mng_info->y_off[i]=new_pair.b;
4894                 }
4895             }
4896
4897             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4898             continue;
4899           }
4900
4901         if (memcmp(type,mng_LOOP,4) == 0)
4902           {
4903             ssize_t loop_iters=1;
4904             loop_level=chunk[0];
4905             mng_info->loop_active[loop_level]=1;  /* mark loop active */
4906
4907             /* Record starting point.  */
4908             loop_iters=mng_get_long(&chunk[1]);
4909
4910             if (logging != MagickFalse)
4911               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4912                 "  LOOP level %.20g has %.20g iterations ",(double) loop_level,
4913                 (double) loop_iters);
4914
4915             if (loop_iters == 0)
4916               skipping_loop=loop_level;
4917
4918             else
4919               {
4920                 mng_info->loop_jump[loop_level]=TellBlob(image);
4921                 mng_info->loop_count[loop_level]=loop_iters;
4922               }
4923
4924             mng_info->loop_iteration[loop_level]=0;
4925             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4926             continue;
4927           }
4928
4929         if (memcmp(type,mng_ENDL,4) == 0)
4930           {
4931             loop_level=chunk[0];
4932
4933             if (skipping_loop > 0)
4934               {
4935                 if (skipping_loop == loop_level)
4936                   {
4937                     /*
4938                       Found end of zero-iteration loop.
4939                     */
4940                     skipping_loop=(-1);
4941                     mng_info->loop_active[loop_level]=0;
4942                   }
4943               }
4944
4945             else
4946               {
4947                 if (mng_info->loop_active[loop_level] == 1)
4948                   {
4949                     mng_info->loop_count[loop_level]--;
4950                     mng_info->loop_iteration[loop_level]++;
4951
4952                     if (logging != MagickFalse)
4953                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4954                         "  ENDL: LOOP level %.20g has %.20g remaining iters ",
4955                         (double) loop_level,(double)
4956                         mng_info->loop_count[loop_level]);
4957
4958                     if (mng_info->loop_count[loop_level] != 0)
4959                       {
4960                         offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4961                           SEEK_SET);
4962
4963                         if (offset < 0)
4964                           ThrowReaderException(CorruptImageError,
4965                             "ImproperImageHeader");
4966                       }
4967
4968                     else
4969                       {
4970                         short
4971                           last_level;
4972
4973                         /*
4974                           Finished loop.
4975                         */
4976                         mng_info->loop_active[loop_level]=0;
4977                         last_level=(-1);
4978                         for (i=0; i < loop_level; i++)
4979                           if (mng_info->loop_active[i] == 1)
4980                             last_level=(short) i;
4981                         loop_level=last_level;
4982                       }
4983                   }
4984               }
4985
4986             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4987             continue;
4988           }
4989
4990         if (memcmp(type,mng_CLON,4) == 0)
4991           {
4992             if (mng_info->clon_warning == 0)
4993               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4994                 CoderError,"CLON is not implemented yet","`%s'",
4995                 image->filename);
4996
4997             mng_info->clon_warning++;
4998           }
4999
5000         if (memcmp(type,mng_MAGN,4) == 0)
5001           {
5002             png_uint_16
5003               magn_first,
5004               magn_last,
5005               magn_mb,
5006               magn_ml,
5007               magn_mr,
5008               magn_mt,
5009               magn_mx,
5010               magn_my,
5011               magn_methx,
5012               magn_methy;
5013
5014             if (length > 1)
5015               magn_first=(p[0] << 8) | p[1];
5016
5017             else
5018               magn_first=0;
5019
5020             if (length > 3)
5021               magn_last=(p[2] << 8) | p[3];
5022
5023             else
5024               magn_last=magn_first;
5025 #ifndef MNG_OBJECT_BUFFERS
5026             if (magn_first || magn_last)
5027               if (mng_info->magn_warning == 0)
5028                 {
5029                   (void) ThrowMagickException(&image->exception,
5030                      GetMagickModule(),CoderError,
5031                      "MAGN is not implemented yet for nonzero objects",
5032                      "`%s'",image->filename);
5033
5034                    mng_info->magn_warning++;
5035                 }
5036 #endif
5037             if (length > 4)
5038               magn_methx=p[4];
5039
5040             else
5041               magn_methx=0;
5042
5043             if (length > 6)
5044               magn_mx=(p[5] << 8) | p[6];
5045
5046             else
5047               magn_mx=1;
5048
5049             if (magn_mx == 0)
5050               magn_mx=1;
5051
5052             if (length > 8)
5053               magn_my=(p[7] << 8) | p[8];
5054
5055             else
5056               magn_my=magn_mx;
5057
5058             if (magn_my == 0)
5059               magn_my=1;
5060
5061             if (length > 10)
5062               magn_ml=(p[9] << 8) | p[10];
5063
5064             else
5065               magn_ml=magn_mx;
5066
5067             if (magn_ml == 0)
5068               magn_ml=1;
5069
5070             if (length > 12)
5071               magn_mr=(p[11] << 8) | p[12];
5072
5073             else
5074               magn_mr=magn_mx;
5075
5076             if (magn_mr == 0)
5077               magn_mr=1;
5078
5079             if (length > 14)
5080               magn_mt=(p[13] << 8) | p[14];
5081
5082             else
5083               magn_mt=magn_my;
5084
5085             if (magn_mt == 0)
5086               magn_mt=1;
5087
5088             if (length > 16)
5089               magn_mb=(p[15] << 8) | p[16];
5090
5091             else
5092               magn_mb=magn_my;
5093
5094             if (magn_mb == 0)
5095               magn_mb=1;
5096
5097             if (length > 17)
5098               magn_methy=p[17];
5099
5100             else
5101               magn_methy=magn_methx;
5102
5103
5104             if (magn_methx > 5 || magn_methy > 5)
5105               if (mng_info->magn_warning == 0)
5106                 {
5107                   (void) ThrowMagickException(&image->exception,
5108                      GetMagickModule(),CoderError,
5109                      "Unknown MAGN method in MNG datastream","`%s'",
5110                      image->filename);
5111
5112                    mng_info->magn_warning++;
5113                 }
5114 #ifdef MNG_OBJECT_BUFFERS
5115           /* Magnify existing objects in the range magn_first to magn_last */
5116 #endif
5117             if (magn_first == 0 || magn_last == 0)
5118               {
5119                 /* Save the magnification factors for object 0 */
5120                 mng_info->magn_mb=magn_mb;
5121                 mng_info->magn_ml=magn_ml;
5122                 mng_info->magn_mr=magn_mr;
5123                 mng_info->magn_mt=magn_mt;
5124                 mng_info->magn_mx=magn_mx;
5125                 mng_info->magn_my=magn_my;
5126                 mng_info->magn_methx=magn_methx;
5127                 mng_info->magn_methy=magn_methy;
5128               }
5129           }
5130
5131         if (memcmp(type,mng_PAST,4) == 0)
5132           {
5133             if (mng_info->past_warning == 0)
5134               (void) ThrowMagickException(&image->exception,GetMagickModule(),
5135                 CoderError,"PAST is not implemented yet","`%s'",
5136                 image->filename);
5137
5138             mng_info->past_warning++;
5139           }
5140
5141         if (memcmp(type,mng_SHOW,4) == 0)
5142           {
5143             if (mng_info->show_warning == 0)
5144               (void) ThrowMagickException(&image->exception,GetMagickModule(),
5145                 CoderError,"SHOW is not implemented yet","`%s'",
5146                 image->filename);
5147
5148             mng_info->show_warning++;
5149           }
5150
5151         if (memcmp(type,mng_sBIT,4) == 0)
5152           {
5153             if (length < 4)
5154               mng_info->have_global_sbit=MagickFalse;
5155
5156             else
5157               {
5158                 mng_info->global_sbit.gray=p[0];
5159                 mng_info->global_sbit.red=p[0];
5160                 mng_info->global_sbit.green=p[1];
5161                 mng_info->global_sbit.blue=p[2];
5162                 mng_info->global_sbit.alpha=p[3];
5163                 mng_info->have_global_sbit=MagickTrue;
5164              }
5165           }
5166         if (memcmp(type,mng_pHYs,4) == 0)
5167           {
5168             if (length > 8)
5169               {
5170                 mng_info->global_x_pixels_per_unit=
5171                     (size_t) mng_get_long(p);
5172                 mng_info->global_y_pixels_per_unit=
5173                     (size_t) mng_get_long(&p[4]);
5174                 mng_info->global_phys_unit_type=p[8];
5175                 mng_info->have_global_phys=MagickTrue;
5176               }
5177
5178             else
5179               mng_info->have_global_phys=MagickFalse;
5180           }
5181         if (memcmp(type,mng_pHYg,4) == 0)
5182           {
5183             if (mng_info->phyg_warning == 0)
5184               (void) ThrowMagickException(&image->exception,GetMagickModule(),
5185                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5186
5187             mng_info->phyg_warning++;
5188           }
5189         if (memcmp(type,mng_BASI,4) == 0)
5190           {
5191             skip_to_iend=MagickTrue;
5192
5193             if (mng_info->basi_warning == 0)
5194               (void) ThrowMagickException(&image->exception,GetMagickModule(),
5195                 CoderError,"BASI is not implemented yet","`%s'",
5196                 image->filename);
5197
5198             mng_info->basi_warning++;
5199 #ifdef MNG_BASI_SUPPORTED
5200             basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5201                (p[2] << 8) | p[3]);
5202             basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5203                (p[6] << 8) | p[7]);
5204             basi_color_type=p[8];
5205             basi_compression_method=p[9];
5206             basi_filter_type=p[10];
5207             basi_interlace_method=p[11];
5208             if (length > 11)
5209               basi_red=(p[12] << 8) & p[13];
5210
5211             else
5212               basi_red=0;
5213
5214             if (length > 13)
5215               basi_green=(p[14] << 8) & p[15];
5216
5217             else
5218               basi_green=0;
5219
5220             if (length > 15)
5221               basi_blue=(p[16] << 8) & p[17];
5222
5223             else
5224               basi_blue=0;
5225
5226             if (length > 17)
5227               basi_alpha=(p[18] << 8) & p[19];
5228
5229             else
5230               {
5231                 if (basi_sample_depth == 16)
5232                   basi_alpha=65535L;
5233                 else
5234                   basi_alpha=255;
5235               }
5236
5237             if (length > 19)
5238               basi_viewable=p[20];
5239
5240             else
5241               basi_viewable=0;
5242
5243 #endif
5244             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5245             continue;
5246           }
5247
5248         if (memcmp(type,mng_IHDR,4)
5249 #if defined(JNG_SUPPORTED)
5250             && memcmp(type,mng_JHDR,4)
5251 #endif
5252             )
5253           {
5254             /* Not an IHDR or JHDR chunk */
5255             if (length)
5256               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5257
5258             continue;
5259           }
5260 /* Process IHDR */
5261         if (logging != MagickFalse)
5262           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5263             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5264
5265         mng_info->exists[object_id]=MagickTrue;
5266         mng_info->viewable[object_id]=MagickTrue;
5267
5268         if (mng_info->invisible[object_id])
5269           {
5270             if (logging != MagickFalse)
5271               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5272                 "  Skipping invisible object");
5273
5274             skip_to_iend=MagickTrue;
5275             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5276             continue;
5277           }
5278 #if defined(MNG_INSERT_LAYERS)
5279         if (length < 8)
5280           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5281
5282         image_width=(size_t) mng_get_long(p);
5283         image_height=(size_t) mng_get_long(&p[4]);
5284 #endif
5285         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5286
5287         /*
5288           Insert a transparent background layer behind the entire animation
5289           if it is not full screen.
5290         */
5291 #if defined(MNG_INSERT_LAYERS)
5292         if (insert_layers && mng_type && first_mng_object)
5293           {
5294             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5295                 (image_width < mng_info->mng_width) ||
5296                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5297                 (image_height < mng_info->mng_height) ||
5298                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5299               {
5300                 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5301                   {
5302                     /*
5303                       Allocate next image structure.
5304                     */
5305                     AcquireNextImage(image_info,image);
5306
5307                     if (GetNextImageInList(image) == (Image *) NULL)
5308                       {
5309                         image=DestroyImageList(image);
5310                         MngInfoFreeStruct(mng_info,&have_mng_structure);
5311                         return((Image *) NULL);
5312                       }
5313
5314                     image=SyncNextImageInList(image);
5315                   }
5316                 mng_info->image=image;
5317
5318                 if (term_chunk_found)
5319                   {
5320                     image->start_loop=MagickTrue;
5321                     image->iterations=mng_iterations;
5322                     term_chunk_found=MagickFalse;
5323                   }
5324
5325                 else
5326                     image->start_loop=MagickFalse;
5327
5328                 /* Make a background rectangle.  */
5329
5330                 image->delay=0;
5331                 image->columns=mng_info->mng_width;
5332                 image->rows=mng_info->mng_height;
5333                 image->page.width=mng_info->mng_width;
5334                 image->page.height=mng_info->mng_height;
5335                 image->page.x=0;
5336                 image->page.y=0;
5337                 image->background_color=mng_background_color;
5338                 (void) SetImageBackgroundColor(image);
5339                 if (logging != MagickFalse)
5340                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5341                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
5342                     (double) mng_info->mng_width,(double) mng_info->mng_height);
5343               }
5344           }
5345         /*
5346           Insert a background layer behind the upcoming image if
5347           framing_mode is 3, and we haven't already inserted one.
5348         */
5349         if (insert_layers && (mng_info->framing_mode == 3) &&
5350                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5351                 (simplicity & 0x08)))
5352           {
5353             if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5354             {
5355               /*
5356                 Allocate next image structure.
5357               */
5358               AcquireNextImage(image_info,image);
5359
5360               if (GetNextImageInList(image) == (Image *) NULL)
5361                 {
5362                   image=DestroyImageList(image);
5363                   MngInfoFreeStruct(mng_info,&have_mng_structure);
5364                   return((Image *) NULL);
5365                 }
5366
5367               image=SyncNextImageInList(image);
5368             }
5369
5370             mng_info->image=image;
5371
5372             if (term_chunk_found)
5373               {
5374                 image->start_loop=MagickTrue;
5375                 image->iterations=mng_iterations;
5376                 term_chunk_found=MagickFalse;
5377               }
5378
5379             else
5380                 image->start_loop=MagickFalse;
5381
5382             image->delay=0;
5383             image->columns=subframe_width;
5384             image->rows=subframe_height;
5385             image->page.width=subframe_width;
5386             image->page.height=subframe_height;
5387             image->page.x=mng_info->clip.left;
5388             image->page.y=mng_info->clip.top;
5389             image->background_color=mng_background_color;
5390             image->matte=MagickFalse;
5391             (void) SetImageBackgroundColor(image);
5392
5393             if (logging != MagickFalse)
5394               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5395                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5396                 (double) mng_info->clip.left,(double) mng_info->clip.right,
5397                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5398           }
5399 #endif /* MNG_INSERT_LAYERS */
5400         first_mng_object=MagickFalse;
5401
5402         if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5403           {
5404             /*
5405               Allocate next image structure.
5406             */
5407             AcquireNextImage(image_info,image);
5408
5409             if (GetNextImageInList(image) == (Image *) NULL)
5410               {
5411                 image=DestroyImageList(image);
5412                 MngInfoFreeStruct(mng_info,&have_mng_structure);
5413                 return((Image *) NULL);
5414               }
5415
5416             image=SyncNextImageInList(image);
5417           }
5418         mng_info->image=image;
5419         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5420           GetBlobSize(image));
5421
5422         if (status == MagickFalse)
5423           break;
5424
5425         if (term_chunk_found)
5426           {
5427             image->start_loop=MagickTrue;
5428             term_chunk_found=MagickFalse;
5429           }
5430
5431         else
5432             image->start_loop=MagickFalse;
5433
5434         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5435           {
5436             image->delay=frame_delay;
5437             frame_delay=default_frame_delay;
5438           }
5439
5440         else
5441           image->delay=0;
5442
5443         image->page.width=mng_info->mng_width;
5444         image->page.height=mng_info->mng_height;
5445         image->page.x=mng_info->x_off[object_id];
5446         image->page.y=mng_info->y_off[object_id];
5447         image->iterations=mng_iterations;
5448
5449         /*
5450           Seek back to the beginning of the IHDR or JHDR chunk's length field.
5451         */
5452
5453         if (logging != MagickFalse)
5454           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5455             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5456             type[2],type[3]);
5457
5458         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
5459
5460         if (offset < 0)
5461           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5462       }
5463
5464     previous=image;
5465     mng_info->image=image;
5466     mng_info->mng_type=mng_type;
5467     mng_info->object_id=object_id;
5468
5469     if (memcmp(type,mng_IHDR,4) == 0)
5470       image=ReadOnePNGImage(mng_info,image_info,exception);
5471
5472 #if defined(JNG_SUPPORTED)
5473     else
5474       image=ReadOneJNGImage(mng_info,image_info,exception);
5475 #endif
5476
5477     if (image == (Image *) NULL)
5478       {
5479         if (IsImageObject(previous) != MagickFalse)
5480           {
5481             (void) DestroyImageList(previous);
5482             (void) CloseBlob(previous);
5483           }
5484
5485         MngInfoFreeStruct(mng_info,&have_mng_structure);
5486         return((Image *) NULL);
5487       }
5488
5489     if (image->columns == 0 || image->rows == 0)
5490       {
5491         (void) CloseBlob(image);
5492         image=DestroyImageList(image);
5493         MngInfoFreeStruct(mng_info,&have_mng_structure);
5494         return((Image *) NULL);
5495       }
5496
5497     mng_info->image=image;
5498
5499     if (mng_type)
5500       {
5501         MngBox
5502           crop_box;
5503
5504         if (mng_info->magn_methx || mng_info->magn_methy)
5505           {
5506             png_uint_32
5507                magnified_height,
5508                magnified_width;
5509
5510             if (logging != MagickFalse)
5511               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5512                 "  Processing MNG MAGN chunk");
5513
5514             if (mng_info->magn_methx == 1)
5515               {
5516                 magnified_width=mng_info->magn_ml;
5517
5518                 if (image->columns > 1)
5519                    magnified_width += mng_info->magn_mr;
5520
5521                 if (image->columns > 2)
5522                    magnified_width += (png_uint_32)
5523                       ((image->columns-2)*(mng_info->magn_mx));
5524               }
5525
5526             else
5527               {
5528                 magnified_width=(png_uint_32) image->columns;
5529
5530                 if (image->columns > 1)
5531                    magnified_width += mng_info->magn_ml-1;
5532
5533                 if (image->columns > 2)
5534                    magnified_width += mng_info->magn_mr-1;
5535
5536                 if (image->columns > 3)
5537                    magnified_width += (png_uint_32)
5538                       ((image->columns-3)*(mng_info->magn_mx-1));
5539               }
5540
5541             if (mng_info->magn_methy == 1)
5542               {
5543                 magnified_height=mng_info->magn_mt;
5544
5545                 if (image->rows > 1)
5546                    magnified_height += mng_info->magn_mb;
5547
5548                 if (image->rows > 2)
5549                    magnified_height += (png_uint_32)
5550                       ((image->rows-2)*(mng_info->magn_my));
5551               }
5552
5553             else
5554               {
5555                 magnified_height=(png_uint_32) image->rows;
5556
5557                 if (image->rows > 1)
5558                    magnified_height += mng_info->magn_mt-1;
5559
5560                 if (image->rows > 2)
5561                    magnified_height += mng_info->magn_mb-1;
5562
5563                 if (image->rows > 3)
5564                    magnified_height += (png_uint_32)
5565                       ((image->rows-3)*(mng_info->magn_my-1));
5566               }
5567
5568             if (magnified_height > image->rows ||
5569                 magnified_width > image->columns)
5570               {
5571                 Image
5572                   *large_image;
5573
5574                 int
5575                   yy;
5576
5577                 ssize_t
5578                   m,
5579                   y;
5580
5581                 register ssize_t
5582                   x;
5583
5584                 register PixelPacket
5585                   *n,
5586                   *q;
5587
5588                 PixelPacket
5589                   *next,
5590                   *prev;
5591
5592                 png_uint_16
5593                   magn_methx,
5594                   magn_methy;
5595
5596                 /* Allocate next image structure.  */
5597
5598                 if (logging != MagickFalse)
5599                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5600                     "    Allocate magnified image");
5601
5602                 AcquireNextImage(image_info,image);
5603
5604                 if (GetNextImageInList(image) == (Image *) NULL)
5605                   {
5606                     image=DestroyImageList(image);
5607                     MngInfoFreeStruct(mng_info,&have_mng_structure);
5608                     return((Image *) NULL);
5609                   }
5610
5611                 large_image=SyncNextImageInList(image);
5612
5613                 large_image->columns=magnified_width;
5614                 large_image->rows=magnified_height;
5615
5616                 magn_methx=mng_info->magn_methx;
5617                 magn_methy=mng_info->magn_methy;
5618
5619 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5620 #define QM unsigned short
5621                 if (magn_methx != 1 || magn_methy != 1)
5622                   {
5623                   /*
5624                      Scale pixels to unsigned shorts to prevent
5625                      overflow of intermediate values of interpolations
5626                   */
5627                      for (y=0; y < (ssize_t) image->rows; y++)
5628                      {
5629                        q=GetAuthenticPixels(image,0,y,image->columns,1,
5630                           exception);
5631
5632                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
5633                        {
5634                           q->red=ScaleQuantumToShort(q->red);
5635                           q->green=ScaleQuantumToShort(q->green);
5636                           q->blue=ScaleQuantumToShort(q->blue);
5637                           q->opacity=ScaleQuantumToShort(q->opacity);
5638                           q++;
5639                        }
5640
5641                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
5642                          break;
5643                      }
5644                   }
5645 #else
5646 #define QM Quantum
5647 #endif
5648
5649                 if (image->matte != MagickFalse)
5650                    (void) SetImageBackgroundColor(large_image);
5651
5652                 else
5653                   {
5654                     large_image->background_color.opacity=OpaqueOpacity;
5655                     (void) SetImageBackgroundColor(large_image);
5656
5657                     if (magn_methx == 4)
5658                       magn_methx=2;
5659
5660                     if (magn_methx == 5)
5661                       magn_methx=3;
5662
5663                     if (magn_methy == 4)
5664                       magn_methy=2;
5665
5666                     if (magn_methy == 5)
5667                       magn_methy=3;
5668                   }
5669
5670                 /* magnify the rows into the right side of the large image */
5671
5672                 if (logging != MagickFalse)
5673                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5674                     "    Magnify the rows to %.20g",(double) large_image->rows);
5675                 m=(ssize_t) mng_info->magn_mt;
5676                 yy=0;
5677                 length=(size_t) image->columns;
5678                 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5679                 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5680
5681                 if ((prev == (PixelPacket *) NULL) ||
5682                     (next == (PixelPacket *) NULL))
5683                   {
5684                      image=DestroyImageList(image);
5685                      MngInfoFreeStruct(mng_info,&have_mng_structure);
5686                      ThrowReaderException(ResourceLimitError,
5687                        "MemoryAllocationFailed");
5688                   }
5689
5690                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5691                 (void) CopyMagickMemory(next,n,length);
5692
5693                 for (y=0; y < (ssize_t) image->rows; y++)
5694                 {
5695                   if (y == 0)
5696                     m=(ssize_t) mng_info->magn_mt;
5697
5698                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5699                     m=(ssize_t) mng_info->magn_mb;
5700
5701                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5702                     m=(ssize_t) mng_info->magn_mb;
5703
5704                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
5705                     m=1;
5706
5707                   else
5708                     m=(ssize_t) mng_info->magn_my;
5709
5710                   n=prev;
5711                   prev=next;
5712                   next=n;
5713
5714                   if (y < (ssize_t) image->rows-1)
5715                     {
5716                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5717                           exception);
5718                       (void) CopyMagickMemory(next,n,length);
5719                     }
5720
5721                   for (i=0; i < m; i++, yy++)
5722                   {
5723                     register PixelPacket
5724                       *pixels;
5725
5726                     assert(yy < (ssize_t) large_image->rows);
5727                     pixels=prev;
5728                     n=next;
5729                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5730                           1,exception);
5731                     q+=(large_image->columns-image->columns);
5732
5733                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
5734                     {
5735                       /* To do: get color as function of indexes[x] */
5736                       /*
5737                       if (image->storage_class == PseudoClass)
5738                         {
5739                         }
5740                       */
5741
5742                       if (magn_methy <= 1)
5743                         {
5744                           *q=(*pixels); /* replicate previous */
5745                         }
5746
5747                       else if (magn_methy == 2 || magn_methy == 4)
5748                         {
5749                           if (i == 0)
5750                              *q=(*pixels);
5751
5752                           else
5753                             {
5754                               /* Interpolate */
5755                               (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5756                                  -(*pixels).red)+m))/((ssize_t) (m*2))
5757                                  +(*pixels).red);
5758                               (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5759                                  -(*pixels).green)+m))/((ssize_t) (m*2))
5760                                  +(*pixels).green);
5761                               (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5762                                  -(*pixels).blue)+m))/((ssize_t) (m*2))
5763                                  +(*pixels).blue);
5764
5765                               if (image->matte != MagickFalse)
5766                                  (*q).opacity=(QM) (((ssize_t)
5767                                  (2*i*((*n).opacity
5768                                  -(*pixels).opacity)+m))
5769                                  /((ssize_t) (m*2))+(*pixels).opacity);
5770                             }
5771
5772                           if (magn_methy == 4)
5773                             {
5774                               /* Replicate nearest */
5775                               if (i <= ((m+1) << 1))
5776                                  (*q).opacity=(*pixels).opacity+0;
5777                               else
5778                                  (*q).opacity=(*n).opacity+0;
5779                             }
5780                         }
5781
5782                       else /* if (magn_methy == 3 || magn_methy == 5) */
5783                         {
5784                           /* Replicate nearest */
5785                           if (i <= ((m+1) << 1))
5786                              *q=(*pixels);
5787
5788                           else
5789                              *q=(*n);
5790
5791                           if (magn_methy == 5)
5792                             {
5793                               (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5794                                  -(*pixels).opacity)+m))/((ssize_t) (m*2))
5795                                  +(*pixels).opacity);
5796                             }
5797                         }
5798                       n++;
5799                       q++;
5800                       pixels++;
5801                     } /* x */
5802
5803                     if (SyncAuthenticPixels(large_image,exception) == 0)
5804                       break;
5805
5806                   } /* i */
5807                 } /* y */
5808
5809                 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5810                 next=(PixelPacket *) RelinquishMagickMemory(next);
5811
5812                 length=image->columns;
5813
5814                 if (logging != MagickFalse)
5815                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5816                     "    Delete original image");
5817
5818                 DeleteImageFromList(&image);
5819
5820                 image=large_image;
5821
5822                 mng_info->image=image;
5823
5824                 /* magnify the columns */
5825                 if (logging != MagickFalse)
5826                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5827                     "    Magnify the columns to %.20g",(double) image->columns);
5828
5829                 for (y=0; y < (ssize_t) image->rows; y++)
5830                 {
5831                   register PixelPacket
5832                     *pixels;
5833
5834                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5835                   pixels=q+(image->columns-length);
5836                   n=pixels+1;
5837
5838                   for (x=(ssize_t) (image->columns-length);
5839                     x < (ssize_t) image->columns; x++)
5840                   {
5841                     if (x == (ssize_t) (image->columns-length))
5842                       m=(ssize_t) mng_info->magn_ml;
5843
5844                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5845                       m=(ssize_t) mng_info->magn_mr;
5846
5847                     else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5848                       m=(ssize_t) mng_info->magn_mr;
5849
5850                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
5851                       m=1;
5852
5853                     else
5854                       m=(ssize_t) mng_info->magn_mx;
5855
5856                     for (i=0; i < m; i++)
5857                     {
5858                       if (magn_methx <= 1)
5859                         {
5860                           /* replicate previous */
5861                           *q=(*pixels);
5862                         }
5863
5864                       else if (magn_methx == 2 || magn_methx == 4)
5865                         {
5866                           if (i == 0)
5867                             *q=(*pixels);
5868
5869                           else
5870                             {
5871                               /* Interpolate */
5872                               (*q).red=(QM) ((2*i*((*n).red
5873                                  -(*pixels).red)+m)
5874                                  /((ssize_t) (m*2))+(*pixels).red);
5875                               (*q).green=(QM) ((2*i*((*n).green
5876                                  -(*pixels).green)
5877                                  +m)/((ssize_t) (m*2))+(*pixels).green);
5878                               (*q).blue=(QM) ((2*i*((*n).blue
5879                                  -(*pixels).blue)+m)
5880                                  /((ssize_t) (m*2))+(*pixels).blue);
5881                               if (image->matte != MagickFalse)
5882                                  (*q).opacity=(QM) ((2*i*((*n).opacity
5883                                    -(*pixels).opacity)+m)/((ssize_t) (m*2))
5884                                    +(*pixels).opacity);
5885                             }
5886
5887                           if (magn_methx == 4)
5888                             {
5889                               /* Replicate nearest */
5890                               if (i <= ((m+1) << 1))
5891                                  (*q).opacity=(*pixels).opacity+0;
5892                               else
5893                                  (*q).opacity=(*n).opacity+0;
5894                             }
5895                         }
5896
5897                       else /* if (magn_methx == 3 || magn_methx == 5) */
5898                         {
5899                           /* Replicate nearest */
5900                           if (i <= ((m+1) << 1))
5901                              *q=(*pixels);
5902
5903                           else
5904                              *q=(*n);
5905
5906                           if (magn_methx == 5)
5907                             {
5908                               /* Interpolate */
5909                               (*q).opacity=(QM) ((2*i*((*n).opacity
5910                                  -(*pixels).opacity)+m) /((ssize_t) (m*2))
5911                                  +(*pixels).opacity);
5912                             }
5913                         }
5914                       q++;
5915                     }
5916                     n++;
5917                     p++;
5918                   }
5919
5920                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
5921                     break;
5922                 }
5923 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5924               if (magn_methx != 1 || magn_methy != 1)
5925                 {
5926                 /*
5927                    Rescale pixels to Quantum
5928                 */
5929                    for (y=0; y < (ssize_t) image->rows; y++)
5930                    {
5931                      q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5932
5933                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
5934                      {
5935                         q->red=ScaleShortToQuantum(q->red);
5936                         q->green=ScaleShortToQuantum(q->green);
5937                         q->blue=ScaleShortToQuantum(q->blue);
5938                         q->opacity=ScaleShortToQuantum(q->opacity);
5939                         q++;
5940                      }
5941
5942                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
5943                        break;
5944                    }
5945                 }
5946 #endif
5947                 if (logging != MagickFalse)
5948                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5949                     "  Finished MAGN processing");
5950               }
5951           }
5952
5953         /*
5954           Crop_box is with respect to the upper left corner of the MNG.
5955         */
5956         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5957         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5958         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5959         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5960         crop_box=mng_minimum_box(crop_box,mng_info->clip);
5961         crop_box=mng_minimum_box(crop_box,mng_info->frame);
5962         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5963         if ((crop_box.left != (mng_info->image_box.left
5964             +mng_info->x_off[object_id])) ||
5965             (crop_box.right != (mng_info->image_box.right
5966             +mng_info->x_off[object_id])) ||
5967             (crop_box.top != (mng_info->image_box.top
5968             +mng_info->y_off[object_id])) ||
5969             (crop_box.bottom != (mng_info->image_box.bottom
5970             +mng_info->y_off[object_id])))
5971           {
5972             if (logging != MagickFalse)
5973               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5974                 "  Crop the PNG image");
5975
5976             if ((crop_box.left < crop_box.right) &&
5977                 (crop_box.top < crop_box.bottom))
5978               {
5979                 Image
5980                   *im;
5981
5982                 RectangleInfo
5983                   crop_info;
5984
5985                 /*
5986                   Crop_info is with respect to the upper left corner of
5987                   the image.
5988                 */
5989                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5990                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
5991                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
5992                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
5993                 image->page.width=image->columns;
5994                 image->page.height=image->rows;
5995                 image->page.x=0;
5996                 image->page.y=0;
5997                 im=CropImage(image,&crop_info,exception);
5998
5999                 if (im != (Image *) NULL)
6000                   {
6001                     image->columns=im->columns;
6002                     image->rows=im->rows;
6003                     im=DestroyImage(im);
6004                     image->page.width=image->columns;
6005                     image->page.height=image->rows;
6006                     image->page.x=crop_box.left;
6007                     image->page.y=crop_box.top;
6008                   }
6009               }
6010
6011             else
6012               {
6013                 /*
6014                   No pixels in crop area.  The MNG spec still requires
6015                   a layer, though, so make a single transparent pixel in
6016                   the top left corner.
6017                 */
6018                 image->columns=1;
6019                 image->rows=1;
6020                 image->colors=2;
6021                 (void) SetImageBackgroundColor(image);
6022                 image->page.width=1;
6023                 image->page.height=1;
6024                 image->page.x=0;
6025                 image->page.y=0;
6026               }
6027           }
6028 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6029         image=mng_info->image;
6030 #endif
6031       }
6032
6033 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6034       /* PNG does not handle depths greater than 16 so reduce it even
6035        * if lossy
6036        */
6037       if (image->depth > 16)
6038          image->depth=16;
6039 #endif
6040
6041 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6042       if (LosslessReduceDepthOK(image) != MagickFalse)
6043          image->depth = 8;
6044 #endif
6045
6046       GetImageException(image,exception);
6047
6048       if (image_info->number_scenes != 0)
6049         {
6050           if (mng_info->scenes_found >
6051              (ssize_t) (image_info->first_scene+image_info->number_scenes))
6052             break;
6053         }
6054
6055       if (logging != MagickFalse)
6056         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6057           "  Finished reading image datastream.");
6058
6059   } while (LocaleCompare(image_info->magick,"MNG") == 0);
6060
6061   (void) CloseBlob(image);
6062
6063   if (logging != MagickFalse)
6064     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6065       "  Finished reading all image datastreams.");
6066
6067 #if defined(MNG_INSERT_LAYERS)
6068   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6069        (mng_info->mng_height))
6070     {
6071       /*
6072         Insert a background layer if nothing else was found.
6073       */
6074       if (logging != MagickFalse)
6075         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6076           "  No images found.  Inserting a background layer.");
6077
6078       if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6079         {
6080           /*
6081             Allocate next image structure.
6082           */
6083           AcquireNextImage(image_info,image);
6084           if (GetNextImageInList(image) == (Image *) NULL)
6085             {
6086               image=DestroyImageList(image);
6087               MngInfoFreeStruct(mng_info,&have_mng_structure);
6088
6089               if (logging != MagickFalse)
6090                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6091                   "  Allocation failed, returning NULL.");
6092
6093               return((Image *) NULL);
6094             }
6095           image=SyncNextImageInList(image);
6096         }
6097       image->columns=mng_info->mng_width;
6098       image->rows=mng_info->mng_height;
6099       image->page.width=mng_info->mng_width;
6100       image->page.height=mng_info->mng_height;
6101       image->page.x=0;
6102       image->page.y=0;
6103       image->background_color=mng_background_color;
6104       image->matte=MagickFalse;
6105
6106       if (image_info->ping == MagickFalse)
6107         (void) SetImageBackgroundColor(image);
6108
6109       mng_info->image_found++;
6110     }
6111 #endif
6112   image->iterations=mng_iterations;
6113
6114   if (mng_iterations == 1)
6115     image->start_loop=MagickTrue;
6116
6117   while (GetPreviousImageInList(image) != (Image *) NULL)
6118   {
6119     image_count++;
6120     if (image_count > 10*mng_info->image_found)
6121       {
6122         if (logging != MagickFalse)
6123           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
6124
6125         (void) ThrowMagickException(&image->exception,GetMagickModule(),
6126           CoderError,"Linked list is corrupted, beginning of list not found",
6127           "`%s'",image_info->filename);
6128
6129         return((Image *) NULL);
6130       }
6131
6132     image=GetPreviousImageInList(image);
6133
6134     if (GetNextImageInList(image) == (Image *) NULL)
6135       {
6136         if (logging != MagickFalse)
6137           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
6138
6139         (void) ThrowMagickException(&image->exception,GetMagickModule(),
6140           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6141           image_info->filename);
6142       }
6143   }
6144
6145   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6146              GetNextImageInList(image) ==
6147      (Image *) NULL)
6148     {
6149       if (logging != MagickFalse)
6150         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6151             "  First image null");
6152
6153       (void) ThrowMagickException(&image->exception,GetMagickModule(),
6154         CoderError,"image->next for first image is NULL but shouldn't be.",
6155         "`%s'",image_info->filename);
6156     }
6157
6158   if (mng_info->image_found == 0)
6159     {
6160       if (logging != MagickFalse)
6161         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6162           "  No visible images found.");
6163
6164       (void) ThrowMagickException(&image->exception,GetMagickModule(),
6165         CoderError,"No visible images in file","`%s'",image_info->filename);
6166
6167       if (image != (Image *) NULL)
6168         image=DestroyImageList(image);
6169
6170       MngInfoFreeStruct(mng_info,&have_mng_structure);
6171       return((Image *) NULL);
6172     }
6173
6174   if (mng_info->ticks_per_second)
6175     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6176             final_delay/mng_info->ticks_per_second;
6177
6178   else
6179     image->start_loop=MagickTrue;
6180
6181   /* Find final nonzero image delay */
6182   final_image_delay=0;
6183
6184   while (GetNextImageInList(image) != (Image *) NULL)
6185     {
6186       if (image->delay)
6187         final_image_delay=image->delay;
6188
6189       image=GetNextImageInList(image);
6190     }
6191
6192   if (final_delay < final_image_delay)
6193     final_delay=final_image_delay;
6194
6195   image->delay=final_delay;
6196
6197   if (logging != MagickFalse)
6198       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6199         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6200         (double) final_delay);
6201
6202   if (logging != MagickFalse)
6203     {
6204       int
6205         scene;
6206
6207       scene=0;
6208       image=GetFirstImageInList(image);
6209
6210       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6211         "  Before coalesce:");
6212
6213       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6214         "    scene 0 delay=%.20g",(double) image->delay);
6215
6216       while (GetNextImageInList(image) != (Image *) NULL)
6217       {
6218         image=GetNextImageInList(image);
6219         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6220           "    scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6221       }
6222     }
6223
6224   image=GetFirstImageInList(image);
6225 #ifdef MNG_COALESCE_LAYERS
6226   if (insert_layers)
6227     {
6228       Image
6229         *next_image,
6230         *next;
6231
6232       size_t
6233         scene;
6234
6235       if (logging != MagickFalse)
6236         (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Coalesce Images");
6237
6238       scene=image->scene;
6239       next_image=CoalesceImages(image,&image->exception);
6240
6241       if (next_image == (Image *) NULL)
6242         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6243
6244       image=DestroyImageList(image);
6245       image=next_image;
6246
6247       for (next=image; next != (Image *) NULL; next=next_image)
6248       {
6249          next->page.width=mng_info->mng_width;
6250          next->page.height=mng_info->mng_height;
6251          next->page.x=0;
6252          next->page.y=0;
6253          next->scene=scene++;
6254          next_image=GetNextImageInList(next);
6255
6256          if (next_image == (Image *) NULL)
6257            break;
6258
6259          if (next->delay == 0)
6260            {
6261              scene--;
6262              next_image->previous=GetPreviousImageInList(next);
6263              if (GetPreviousImageInList(next) == (Image *) NULL)
6264                image=next_image;
6265              else
6266                next->previous->next=next_image;
6267              next=DestroyImage(next);
6268            }
6269       }
6270     }
6271 #endif
6272
6273   while (GetNextImageInList(image) != (Image *) NULL)
6274       image=GetNextImageInList(image);
6275
6276   image->dispose=BackgroundDispose;
6277
6278   if (logging != MagickFalse)
6279     {
6280       int
6281         scene;
6282
6283       scene=0;
6284       image=GetFirstImageInList(image);
6285
6286       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6287         "  After coalesce:");
6288
6289       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6290         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6291         (double) image->dispose);
6292
6293       while (GetNextImageInList(image) != (Image *) NULL)
6294       {
6295         image=GetNextImageInList(image);
6296
6297         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6298           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6299           (double) image->delay,(double) image->dispose);
6300       }
6301    }
6302
6303   image=GetFirstImageInList(image);
6304   MngInfoFreeStruct(mng_info,&have_mng_structure);
6305   have_mng_structure=MagickFalse;
6306
6307   if (logging != MagickFalse)
6308     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6309
6310   return(GetFirstImageInList(image));
6311 }
6312 #else /* PNG_LIBPNG_VER > 10011 */
6313 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6314 {
6315   printf("Your PNG library is too old: You have libpng-%s\n",
6316      PNG_LIBPNG_VER_STRING);
6317
6318   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6319     "PNG library is too old","`%s'",image_info->filename);
6320
6321   return(Image *) NULL;
6322 }
6323
6324 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6325 {
6326   return(ReadPNGImage(image_info,exception));
6327 }
6328 #endif /* PNG_LIBPNG_VER > 10011 */
6329 #endif
6330 \f
6331 /*
6332 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6333 %                                                                             %
6334 %                                                                             %
6335 %                                                                             %
6336 %   R e g i s t e r P N G I m a g e                                           %
6337 %                                                                             %
6338 %                                                                             %
6339 %                                                                             %
6340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6341 %
6342 %  RegisterPNGImage() adds properties for the PNG image format to
6343 %  the list of supported formats.  The properties include the image format
6344 %  tag, a method to read and/or write the format, whether the format
6345 %  supports the saving of more than one frame to the same file or blob,
6346 %  whether the format supports native in-memory I/O, and a brief
6347 %  description of the format.
6348 %
6349 %  The format of the RegisterPNGImage method is:
6350 %
6351 %      size_t RegisterPNGImage(void)
6352 %
6353 */
6354 ModuleExport size_t RegisterPNGImage(void)
6355 {
6356   char
6357     version[MaxTextExtent];
6358
6359   MagickInfo
6360     *entry;
6361
6362   static const char
6363     *PNGNote=
6364     {
6365       "See http://www.libpng.org/ for details about the PNG format."
6366     },
6367
6368     *JNGNote=
6369     {
6370       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6371       "format."
6372     },
6373
6374     *MNGNote=
6375     {
6376       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6377       "format."
6378     };
6379
6380   *version='\0';
6381
6382 #if defined(PNG_LIBPNG_VER_STRING)
6383   (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6384   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
6385
6386   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6387     {
6388       (void) ConcatenateMagickString(version,",",MaxTextExtent);
6389       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6390             MaxTextExtent);
6391     }
6392 #endif
6393
6394   entry=SetMagickInfo("MNG");
6395   entry->seekable_stream=MagickTrue;  /* To do: eliminate this. */
6396
6397 #if defined(MAGICKCORE_PNG_DELEGATE)
6398   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6399   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6400 #endif
6401
6402   entry->magick=(IsImageFormatHandler *) IsMNG;
6403   entry->description=ConstantString("Multiple-image Network Graphics");
6404
6405   if (*version != '\0')
6406     entry->version=ConstantString(version);
6407
6408   entry->module=ConstantString("PNG");
6409   entry->note=ConstantString(MNGNote);
6410   (void) RegisterMagickInfo(entry);
6411
6412   entry=SetMagickInfo("PNG");
6413
6414 #if defined(MAGICKCORE_PNG_DELEGATE)
6415   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6416   entry->encoder=(EncodeImageHandler *) WritePNGImage;
6417 #endif
6418
6419   entry->magick=(IsImageFormatHandler *) IsPNG;
6420   entry->adjoin=MagickFalse;
6421   entry->description=ConstantString("Portable Network Graphics");
6422   entry->module=ConstantString("PNG");
6423
6424   if (*version != '\0')
6425     entry->version=ConstantString(version);
6426
6427   entry->note=ConstantString(PNGNote);
6428   (void) RegisterMagickInfo(entry);
6429
6430   entry=SetMagickInfo("PNG8");
6431
6432 #if defined(MAGICKCORE_PNG_DELEGATE)
6433   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6434   entry->encoder=(EncodeImageHandler *) WritePNGImage;
6435 #endif
6436
6437   entry->magick=(IsImageFormatHandler *) IsPNG;
6438   entry->adjoin=MagickFalse;
6439   entry->description=ConstantString(
6440             "8-bit indexed with optional binary transparency");
6441   entry->module=ConstantString("PNG");
6442   (void) RegisterMagickInfo(entry);
6443
6444   entry=SetMagickInfo("PNG24");
6445   *version='\0';
6446
6447 #if defined(ZLIB_VERSION)
6448   (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6449   (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6450
6451   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6452     {
6453       (void) ConcatenateMagickString(version,",",MaxTextExtent);
6454       (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6455     }
6456 #endif
6457
6458   if (*version != '\0')
6459     entry->version=ConstantString(version);
6460
6461 #if defined(MAGICKCORE_PNG_DELEGATE)
6462   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6463   entry->encoder=(EncodeImageHandler *) WritePNGImage;
6464 #endif
6465
6466   entry->magick=(IsImageFormatHandler *) IsPNG;
6467   entry->adjoin=MagickFalse;
6468   entry->description=ConstantString("opaque 24-bit RGB");
6469   entry->module=ConstantString("PNG");
6470   (void) RegisterMagickInfo(entry);
6471
6472   entry=SetMagickInfo("PNG32");
6473
6474 #if defined(MAGICKCORE_PNG_DELEGATE)
6475   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6476   entry->encoder=(EncodeImageHandler *) WritePNGImage;
6477 #endif
6478
6479   entry->magick=(IsImageFormatHandler *) IsPNG;
6480   entry->adjoin=MagickFalse;
6481   entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6482   entry->module=ConstantString("PNG");
6483   (void) RegisterMagickInfo(entry);
6484
6485   entry=SetMagickInfo("JNG");
6486
6487 #if defined(JNG_SUPPORTED)
6488 #if defined(MAGICKCORE_PNG_DELEGATE)
6489   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6490   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6491 #endif
6492 #endif
6493
6494   entry->magick=(IsImageFormatHandler *) IsJNG;
6495   entry->adjoin=MagickFalse;
6496   entry->description=ConstantString("JPEG Network Graphics");
6497   entry->module=ConstantString("PNG");
6498   entry->note=ConstantString(JNGNote);
6499   (void) RegisterMagickInfo(entry);
6500
6501 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6502   ping_semaphore=AllocateSemaphoreInfo();
6503 #endif
6504
6505   return(MagickImageCoderSignature);
6506 }
6507 \f
6508 /*
6509 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6510 %                                                                             %
6511 %                                                                             %
6512 %                                                                             %
6513 %   U n r e g i s t e r P N G I m a g e                                       %
6514 %                                                                             %
6515 %                                                                             %
6516 %                                                                             %
6517 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6518 %
6519 %  UnregisterPNGImage() removes format registrations made by the
6520 %  PNG module from the list of supported formats.
6521 %
6522 %  The format of the UnregisterPNGImage method is:
6523 %
6524 %      UnregisterPNGImage(void)
6525 %
6526 */
6527 ModuleExport void UnregisterPNGImage(void)
6528 {
6529   (void) UnregisterMagickInfo("MNG");
6530   (void) UnregisterMagickInfo("PNG");
6531   (void) UnregisterMagickInfo("PNG8");
6532   (void) UnregisterMagickInfo("PNG24");
6533   (void) UnregisterMagickInfo("PNG32");
6534   (void) UnregisterMagickInfo("JNG");
6535
6536 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6537   if (ping_semaphore != (SemaphoreInfo *) NULL)
6538     DestroySemaphoreInfo(&ping_semaphore);
6539 #endif
6540 }
6541 \f
6542 #if defined(MAGICKCORE_PNG_DELEGATE)
6543 #if PNG_LIBPNG_VER > 10011
6544 /*
6545 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6546 %                                                                             %
6547 %                                                                             %
6548 %                                                                             %
6549 %   W r i t e M N G I m a g e                                                 %
6550 %                                                                             %
6551 %                                                                             %
6552 %                                                                             %
6553 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6554 %
6555 %  WriteMNGImage() writes an image in the Portable Network Graphics
6556 %  Group's "Multiple-image Network Graphics" encoded image format.
6557 %
6558 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
6559 %
6560 %  The format of the WriteMNGImage method is:
6561 %
6562 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6563 %
6564 %  A description of each parameter follows.
6565 %
6566 %    o image_info: the image info.
6567 %
6568 %    o image:  The image.
6569 %
6570 %
6571 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6572 %    "To do" under ReadPNGImage):
6573 %
6574 %    Preserve all unknown and not-yet-handled known chunks found in input
6575 %    PNG file and copy them  into output PNG files according to the PNG
6576 %    copying rules.
6577 %
6578 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
6579 %
6580 %    Improve selection of color type (use indexed-colour or indexed-colour
6581 %    with tRNS when 256 or fewer unique RGBA values are present).
6582 %
6583 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6584 %    This will be complicated if we limit ourselves to generating MNG-LC
6585 %    files.  For now we ignore disposal method 3 and simply overlay the next
6586 %    image on it.
6587 %
6588 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
6589 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
6590 %    [mostly done 15 June 1999 but still need to take care of tRNS]
6591 %
6592 %    Check for identical sRGB and replace with a global sRGB (and remove
6593 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6594 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6595 %    local gAMA/cHRM with local sRGB if appropriate).
6596 %
6597 %    Check for identical sBIT chunks and write global ones.
6598 %
6599 %    Provide option to skip writing the signature tEXt chunks.
6600 %
6601 %    Use signatures to detect identical objects and reuse the first
6602 %    instance of such objects instead of writing duplicate objects.
6603 %
6604 %    Use a smaller-than-32k value of compression window size when
6605 %    appropriate.
6606 %
6607 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
6608 %    ancillary text chunks and save profiles.
6609 %
6610 %    Provide an option to force LC files (to ensure exact framing rate)
6611 %    instead of VLC.
6612 %
6613 %    Provide an option to force VLC files instead of LC, even when offsets
6614 %    are present.  This will involve expanding the embedded images with a
6615 %    transparent region at the top and/or left.
6616 */
6617
6618 static void
6619 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6620    png_info *ping_info, unsigned char *profile_type, unsigned char
6621    *profile_description, unsigned char *profile_data, png_uint_32 length)
6622 {
6623    png_textp
6624      text;
6625
6626    register ssize_t
6627      i;
6628
6629    unsigned char
6630      *sp;
6631
6632    png_charp
6633      dp;
6634
6635    png_uint_32
6636      allocated_length,
6637      description_length;
6638
6639    unsigned char
6640      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6641
6642    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6643       return;
6644
6645    if (image_info->verbose)
6646      {
6647        (void) printf("writing raw profile: type=%s, length=%.20g\n",
6648          (char *) profile_type, (double) length);
6649      }
6650
6651    text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6652    description_length=(png_uint_32) strlen((const char *) profile_description);
6653    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6654       + description_length);
6655    text[0].text=(png_charp) png_malloc(ping,allocated_length);
6656    text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6657    text[0].key[0]='\0';
6658    (void) ConcatenateMagickString(text[0].key,
6659       "Raw profile type ",MaxTextExtent);
6660    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6661    sp=profile_data;
6662    dp=text[0].text;
6663    *dp++='\n';
6664    (void) CopyMagickString(dp,(const char *) profile_description,
6665      allocated_length);
6666    dp+=description_length;
6667    *dp++='\n';
6668    (void) FormatMagickString(dp,allocated_length-
6669      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6670    dp+=8;
6671
6672    for (i=0; i < (ssize_t) length; i++)
6673    {
6674      if (i%36 == 0)
6675        *dp++='\n';
6676      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6677      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6678    }
6679
6680    *dp++='\n';
6681    *dp='\0';
6682    text[0].text_length=(png_size_t) (dp-text[0].text);
6683    text[0].compression=image_info->compression == NoCompression ||
6684      (image_info->compression == UndefinedCompression &&
6685      text[0].text_length < 128) ? -1 : 0;
6686
6687    if (text[0].text_length <= allocated_length)
6688      png_set_text(ping,ping_info,text,1);
6689
6690    png_free(ping,text[0].text);
6691    png_free(ping,text[0].key);
6692    png_free(ping,text);
6693 }
6694
6695 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
6696   const char *string, MagickBooleanType logging)
6697 {
6698   char
6699     *name;
6700
6701   const StringInfo
6702     *profile;
6703
6704   unsigned char
6705     *data;
6706
6707   png_uint_32 length;
6708
6709   ResetImageProfileIterator(image);
6710
6711   for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6712   {
6713     profile=GetImageProfile(image,name);
6714
6715     if (profile != (const StringInfo *) NULL)
6716       {
6717         StringInfo
6718           *ping_profile;
6719
6720         if (LocaleNCompare(name,string,11) == 0)
6721           {
6722             if (logging != MagickFalse)
6723                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6724                    "  Found %s profile",name);
6725
6726             ping_profile=CloneStringInfo(profile);
6727             data=GetStringInfoDatum(ping_profile),
6728             length=(png_uint_32) GetStringInfoLength(ping_profile);
6729             data[4]=data[3];
6730             data[3]=data[2];
6731             data[2]=data[1];
6732             data[1]=data[0];
6733             (void) WriteBlobMSBULong(image,length-5);  /* data length */
6734             (void) WriteBlob(image,length-1,data+1);
6735             (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6736             ping_profile=DestroyStringInfo(ping_profile);
6737           }
6738       }
6739
6740       name=GetNextImageProfile(image);
6741    }
6742
6743    return(MagickTrue);
6744 }
6745
6746
6747 /* Write one PNG image */
6748 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6749    const ImageInfo *IMimage_info,Image *IMimage)
6750 {
6751   Image
6752     *image;
6753
6754   ImageInfo
6755     *image_info;
6756
6757   char
6758     s[2];
6759
6760   const char
6761     *name,
6762     *property,
6763     *value;
6764
6765   const StringInfo
6766     *profile;
6767
6768   int
6769     num_passes,
6770     pass;
6771
6772   png_byte
6773      ping_trans_alpha[256];
6774
6775   png_color
6776      palette[257];
6777
6778   png_color_16
6779     ping_background,
6780     ping_trans_color;
6781
6782   png_info
6783     *ping_info;
6784
6785   png_struct
6786     *ping;
6787
6788   png_uint_32
6789     ping_height,
6790     ping_width;
6791
6792   ssize_t
6793     y;
6794
6795   MagickBooleanType
6796     image_matte,
6797     logging,
6798     matte,
6799
6800     ping_have_blob,
6801     ping_have_cheap_transparency,
6802     ping_have_color,
6803     ping_have_non_bw,
6804     ping_have_PLTE,
6805     ping_have_bKGD,
6806     ping_have_pHYs,
6807     ping_have_tRNS,
6808
6809     ping_exclude_bKGD,
6810     ping_exclude_cHRM,
6811     /* ping_exclude_EXIF, */
6812     ping_exclude_gAMA,
6813     ping_exclude_iCCP,
6814     /* ping_exclude_iTXt, */
6815     ping_exclude_oFFs,
6816     ping_exclude_pHYs,
6817     ping_exclude_sRGB,
6818     ping_exclude_tEXt,
6819     /* ping_exclude_tRNS, */
6820     ping_exclude_vpAg,
6821     ping_exclude_zCCP, /* hex-encoded iCCP */
6822     ping_exclude_zTXt,
6823
6824     ping_need_colortype_warning,
6825
6826     status,
6827     tried_333,
6828     tried_444;
6829
6830   QuantumInfo
6831     *quantum_info;
6832
6833   register ssize_t
6834     i,
6835     x;
6836
6837   unsigned char
6838     *ping_pixels;
6839
6840   volatile int
6841     image_colors,
6842     ping_bit_depth,
6843     ping_color_type,
6844     ping_interlace_method,
6845     ping_compression_method,
6846     ping_filter_method,
6847     ping_num_trans;
6848
6849   volatile size_t
6850     image_depth,
6851     old_bit_depth;
6852
6853   size_t
6854     quality,
6855     rowbytes,
6856     save_image_depth;
6857
6858   int
6859     j,
6860     number_colors,
6861     number_opaque,
6862     number_semitransparent,
6863     number_transparent,
6864     ping_pHYs_unit_type;
6865
6866   png_uint_32
6867     ping_pHYs_x_resolution,
6868     ping_pHYs_y_resolution;
6869
6870   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6871     "  Enter WriteOnePNGImage()");
6872
6873   image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
6874   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
6875
6876 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6877   LockSemaphoreInfo(ping_semaphore);
6878 #endif
6879
6880   /* Initialize some stuff */
6881   ping_bit_depth=0,
6882   ping_color_type=0,
6883   ping_interlace_method=0,
6884   ping_compression_method=0,
6885   ping_filter_method=0,
6886   ping_num_trans = 0;
6887
6888   ping_background.red = 0;
6889   ping_background.green = 0;
6890   ping_background.blue = 0;
6891   ping_background.gray = 0;
6892   ping_background.index = 0;
6893
6894   ping_trans_color.red=0;
6895   ping_trans_color.green=0;
6896   ping_trans_color.blue=0;
6897   ping_trans_color.gray=0;
6898
6899   ping_pHYs_unit_type = 0;
6900   ping_pHYs_x_resolution = 0;
6901   ping_pHYs_y_resolution = 0;
6902
6903   ping_have_blob=MagickFalse;
6904   ping_have_color=MagickTrue;
6905   ping_have_non_bw=MagickTrue;
6906   ping_have_PLTE=MagickFalse;
6907   ping_have_bKGD=MagickFalse;
6908   ping_have_pHYs=MagickFalse;
6909   ping_have_tRNS=MagickFalse;
6910
6911   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
6912   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6913   /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
6914   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
6915   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6916   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
6917   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
6918   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
6919   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
6920   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
6921   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
6922   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
6923   ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
6924   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
6925   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
6926
6927   ping_need_colortype_warning = MagickFalse;
6928
6929   number_opaque = 0;
6930   number_semitransparent = 0;
6931   number_transparent = 0;
6932
6933   if (logging != MagickFalse)
6934     {
6935       if (image->storage_class == UndefinedClass)
6936           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6937           "    storage_class=UndefinedClass");
6938       if (image->storage_class == DirectClass)
6939           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6940           "    storage_class=DirectClass");
6941       if (image->storage_class == PseudoClass)
6942           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6943           "    storage_class=PseudoClass");
6944     }
6945    
6946   if (image->colorspace != RGBColorspace)
6947     (void) TransformImageColorspace(image,RGBColorspace);
6948
6949   /*
6950     Sometimes we get PseudoClass images whose RGB values don't match
6951     the colors in the colormap.  This code syncs the RGB values.
6952   */
6953   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
6954      (void) SyncImage(image);
6955
6956 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
6957   if (image->depth > 8)
6958     {
6959       if (logging != MagickFalse)
6960         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6961           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
6962
6963       image->depth=8;
6964     }
6965 #endif
6966
6967
6968 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6969   /* PNG does not handle depths greater than 16 so reduce it even
6970    * if lossy
6971    */
6972   if (image->depth > 16)
6973       image->depth=16;
6974 #endif
6975
6976 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6977   if (image->depth == 16 && mng_info->write_png_depth != 16)
6978     if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
6979       image->depth = 8;
6980 #endif
6981
6982   /* Normally we run this just once, but in the case of writing PNG8
6983    * we reduce the transparency to binary and run again, then if there
6984    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
6985    * RGBA palette and run again, and finally to a simple 3-3-2-1 RGBA
6986    * palette.  The final reduction can only fail if there are still 256
6987    * colors present and one of them has both transparent and opaque instances.
6988    */
6989
6990   tried_333 = MagickFalse;
6991   tried_444 = MagickFalse;
6992
6993   for (j=0; j<5; j++)
6994   {
6995     /* BUILD_PALETTE
6996      *
6997      * Sometimes we get DirectClass images that have 256 colors or fewer.
6998      * This code will build a colormap.
6999      *
7000      * Also, sometimes we get PseudoClass images with an out-of-date
7001      * colormap.  This code will replace the colormap with a new one.
7002      * Sometimes we get PseudoClass images that have more than 256 colors.
7003      * This code will delete the colormap and change the image to
7004      * DirectClass.
7005      *
7006      * If image->matte is MagickFalse, we ignore the opacity channel
7007      * even though it sometimes contains left-over non-opaque values.
7008      *
7009      * Also we gather some information (number of opaque, transparent,
7010      * and semitransparent pixels, and whether the image has any non-gray
7011      * pixels or only black-and-white pixels) that we might need later.
7012      *
7013      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7014      * we need to check for bogus non-opaque values, at least.
7015      */
7016
7017 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7018 #  define PNGK 0 /* Shift */
7019 #  define PNGM 1 /* Scale */
7020 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
7021 #  define PNGK 8
7022 #  define PNGM 0x0101
7023 #else
7024 #  define PNGK 24
7025 #  define PNGM 0x01010101
7026 #endif
7027
7028    ExceptionInfo
7029      *exception;
7030
7031    int
7032      n;
7033
7034    PixelPacket
7035      opaque[260],
7036      semitransparent[260],
7037      transparent[260];
7038
7039    register IndexPacket
7040      *indexes;
7041
7042    register const PixelPacket
7043      *s,
7044      *q;
7045
7046    register PixelPacket
7047      *r;
7048
7049    if (logging != MagickFalse)
7050      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7051          "    Enter BUILD_PALETTE:");
7052
7053    if (logging != MagickFalse)
7054      {
7055        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7056              "      image->columns=%.20g",(double) image->columns);
7057        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7058              "      image->rows=%.20g",(double) image->rows);
7059        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7060              "      image->matte=%.20g",(double) image->matte);
7061        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7062              "      image->depth=%.20g",(double) image->depth);
7063
7064        if (image->storage_class == PseudoClass && image->colormap != NULL)
7065        {
7066          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7067              "      Original colormap:");
7068          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7069              "        i    (red,green,blue,opacity)");
7070
7071          for (i=0; i < 256; i++)
7072          {
7073                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7074                    "        %d    (%d,%d,%d,%d)",
7075                     (int) i,
7076                     (int) image->colormap[i].red,
7077                     (int) image->colormap[i].green,
7078                     (int) image->colormap[i].blue,
7079                     (int) image->colormap[i].opacity);
7080          }
7081
7082          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7083          {
7084            if (i > 255)
7085              {
7086                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7087                    "        %d    (%d,%d,%d,%d)",
7088                     (int) i,
7089                     (int) image->colormap[i].red,
7090                     (int) image->colormap[i].green,
7091                     (int) image->colormap[i].blue,
7092                     (int) image->colormap[i].opacity);
7093              }
7094          }
7095        }
7096
7097        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7098            "      image->colors=%d",(int) image->colors);
7099
7100        if (image->colors == 0)
7101        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7102            "        (zero means unknown)");
7103
7104        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7105             "      Regenerate the colormap");
7106      }
7107
7108      exception=(&image->exception);
7109
7110      image_colors=0;
7111      number_opaque = 0;
7112      number_semitransparent = 0;
7113      number_transparent = 0;
7114
7115      for (y=0; y < (ssize_t) image->rows; y++)
7116      {
7117        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7118
7119        if (q == (PixelPacket *) NULL)
7120          break;
7121
7122        for (x=0; x < (ssize_t) image->columns; x++)
7123        {
7124            if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7125              {
7126                if (number_opaque < 259)
7127                  {
7128                    if (number_opaque == 0)
7129                      {
7130                        opaque[0]=*q;
7131                        opaque[0].opacity=OpaqueOpacity;
7132                        number_opaque=1;
7133                      }
7134
7135                    for (i=0; i< (ssize_t) number_opaque; i++)
7136                      {
7137                        if (IsColorEqual(opaque+i, (PixelPacket *) q))
7138                          break;
7139                      }
7140
7141                    if (i ==  (ssize_t) number_opaque &&
7142                        number_opaque < 259)
7143                      {
7144                        number_opaque++;
7145                        opaque[i] = *q;
7146                        opaque[i].opacity = OpaqueOpacity;
7147                      }
7148                  }
7149              }
7150            else if (q->opacity == TransparentOpacity)
7151              {
7152                if (number_transparent < 259)
7153                  {
7154                    if (number_transparent == 0)
7155                      {
7156                        transparent[0]=*q;
7157                        ping_trans_color.red=(unsigned short)(q->red);
7158                        ping_trans_color.green=(unsigned short) (q->green);
7159                        ping_trans_color.blue=(unsigned short) (q->blue);
7160                        ping_trans_color.gray=(unsigned short) (q->blue);
7161                        number_transparent = 1;
7162                      }
7163
7164                    for (i=0; i< (ssize_t) number_transparent; i++)
7165                      {
7166                        if (IsColorEqual(transparent+i, (PixelPacket *) q))
7167                          break;
7168                      }
7169
7170                    if (i ==  (ssize_t) number_transparent &&
7171                        number_transparent < 259)
7172                      {
7173                        number_transparent++;
7174                        transparent[i] = *q;
7175                      }
7176                  }
7177              }
7178            else
7179              {
7180                if (number_semitransparent < 259)
7181                  {
7182                    if (number_semitransparent == 0)
7183                      {
7184                        semitransparent[0]=*q;
7185                        number_semitransparent = 1;
7186                      }
7187
7188                    for (i=0; i< (ssize_t) number_semitransparent; i++)
7189                      {
7190                        if (IsColorEqual(semitransparent+i,
7191                           (PixelPacket *) q) &&
7192                           q->opacity == semitransparent[i].opacity)
7193                          break;
7194                      }
7195
7196                    if (i ==  (ssize_t) number_semitransparent &&
7197                        number_semitransparent < 259)
7198                      {
7199                        number_semitransparent++;
7200                        semitransparent[i] = *q;
7201                      }
7202                  }
7203              }
7204            q++;
7205         }
7206      }
7207
7208      if (ping_exclude_bKGD == MagickFalse)
7209        {
7210          /* Add the background color to the palette, if it
7211           * isn't already there.
7212           */
7213           for (i=0; i<number_opaque; i++)
7214           {
7215              if (IsColorEqual(opaque+i, &image->background_color))
7216              break;
7217           }
7218
7219           if (number_opaque < 259 && i == number_opaque)
7220             {
7221                opaque[i]=image->background_color;
7222                opaque[i].opacity = OpaqueOpacity;
7223                number_opaque++;
7224             }
7225           else if (logging != MagickFalse)
7226               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7227                   "      No room in the colormap to add background color");
7228        }
7229
7230      image_colors=number_opaque+number_transparent+number_semitransparent;
7231
7232      if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7233        {
7234          /* No room for the background color; remove it. */
7235          number_opaque--;
7236          image_colors--;
7237        }
7238
7239      if (logging != MagickFalse)
7240        {
7241          if (image_colors > 256)
7242             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7243                   "      image has more than 256 colors");
7244
7245          else
7246             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7247                   "      image has %d colors",image_colors);
7248        }
7249
7250      if (mng_info->write_png_colortype != 7) /* We won't need this info */
7251        {
7252          ping_have_color=MagickFalse;
7253          ping_have_non_bw=MagickFalse;
7254
7255          if(image_colors > 256)
7256            {
7257              for (y=0; y < (ssize_t) image->rows; y++)
7258              {
7259                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7260
7261                if (q == (PixelPacket *) NULL)
7262                  break;
7263
7264                /* Worst case is black-and-white; we are looking at every
7265                 * pixel twice.
7266                 */
7267
7268                if (ping_have_color == MagickFalse)
7269                  {
7270                    s=q;
7271                    for (x=0; x < (ssize_t) image->columns; x++)
7272                    {
7273                      if (s->red != s->green || s->red != s->blue)
7274                        {
7275                           ping_have_color=MagickTrue;
7276                           ping_have_non_bw=MagickTrue;
7277                           break;
7278                        }
7279                      s++;
7280                    }
7281                  }
7282
7283                if (ping_have_non_bw == MagickFalse)
7284                  {
7285                    s=q;
7286                    for (x=0; x < (ssize_t) image->columns; x++)
7287                    {
7288                      if (s->red != 0 && s->red != QuantumRange)
7289                        {
7290                          ping_have_non_bw=MagickTrue;
7291                        }
7292                      s++;
7293                    }
7294                  }
7295              }
7296            } 
7297        } 
7298
7299      if (image_colors < 257)
7300        {
7301          PixelPacket
7302            colormap[260];
7303              
7304          /*
7305           * Initialize image colormap.
7306           */
7307
7308          if (logging != MagickFalse)
7309             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7310                   "      Sort the new colormap");
7311
7312         /* Sort palette, transparent first */;
7313
7314          n = 0;
7315
7316          for (i=0; i<number_transparent; i++)
7317             colormap[n++] = transparent[i];
7318
7319          for (i=0; i<number_semitransparent; i++)
7320             colormap[n++] = semitransparent[i];
7321
7322          for (i=0; i<number_opaque; i++)
7323             colormap[n++] = opaque[i];
7324
7325          
7326          /* image_colors < 257; search the colormap instead of the pixels
7327           * to get ping_have_color and ping_have_non_bw
7328           */
7329          for (i=0; i<n; i++)
7330          {
7331            if (ping_have_color == MagickFalse)
7332              {
7333                 if (colormap[i].red != colormap[i].green ||
7334                     colormap[i].red != colormap[i].blue)
7335                   {
7336                      ping_have_color=MagickTrue;
7337                      ping_have_non_bw=MagickTrue;
7338                      break;
7339                   }
7340               }
7341
7342            if (ping_have_non_bw == MagickFalse)
7343              {
7344                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
7345                    ping_have_non_bw=MagickTrue;
7346              }
7347           }
7348
7349         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7350             (number_transparent == 0 && number_semitransparent == 0)) &&
7351             (((mng_info->write_png_colortype-1) ==
7352             PNG_COLOR_TYPE_PALETTE) ||
7353             (mng_info->write_png_colortype == 0)))
7354           {
7355             if (logging != MagickFalse)
7356               {
7357                 if (n !=  (ssize_t) image_colors)
7358                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7359                    "   image_colors (%d) and n (%d)  don't match",
7360                    image_colors, n);
7361
7362                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7363                    "      AcquireImageColormap");
7364               }
7365
7366             image->colors = image_colors;
7367
7368             if (AcquireImageColormap(image,image_colors) ==
7369                 MagickFalse)
7370                ThrowWriterException(ResourceLimitError,
7371                   "MemoryAllocationFailed");
7372
7373             for (i=0; i< (ssize_t) image_colors; i++)
7374                image->colormap[i] = colormap[i];
7375
7376             if (logging != MagickFalse)
7377               {
7378                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7379                       "      image->colors=%d (%d)",
7380                       (int) image->colors, image_colors);
7381  
7382                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7383                       "      Update the pixel indexes");
7384               }
7385
7386             /* Sync the pixel indices with the new colormap */
7387
7388             for (y=0; y < (ssize_t) image->rows; y++)
7389             {
7390               q=GetAuthenticPixels(image,0,y,image->columns,1,
7391                   exception);
7392
7393               if (q == (PixelPacket *) NULL)
7394                 break;
7395
7396               indexes=GetAuthenticIndexQueue(image);
7397  
7398               for (x=0; x < (ssize_t) image->columns; x++)
7399               {
7400                 for (i=0; i< (ssize_t) image_colors; i++)
7401                 {
7402                   if ((image->matte == MagickFalse ||
7403                       image->colormap[i].opacity == q->opacity) &&
7404                       (IsColorEqual(&image->colormap[i],
7405                          (PixelPacket *) q)))
7406                   {
7407                     indexes[x]=(IndexPacket) i;
7408                     break;
7409                   }
7410                 }
7411                 q++;
7412               }
7413
7414               if (SyncAuthenticPixels(image,exception) == MagickFalse)
7415                  break;
7416             }
7417           }
7418        }
7419
7420      if (logging != MagickFalse)
7421        {
7422          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7423             "      image->colors=%d", (int) image->colors);
7424
7425          if (image->colormap != NULL)
7426            {
7427              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7428                  "       i     (red,green,blue,opacity)");
7429
7430              for (i=0; i < (ssize_t) image->colors; i++)
7431              {
7432                if (i < 300 || i >= image->colors - 10)
7433                  {
7434                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7435                        "       %d     (%d,%d,%d,%d)",
7436                         (int) i,
7437                         (int) image->colormap[i].red,
7438                         (int) image->colormap[i].green,
7439                         (int) image->colormap[i].blue,
7440                         (int) image->colormap[i].opacity);
7441                  }
7442              }
7443            }
7444
7445            if (number_transparent < 257)
7446              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7447                    "      number_transparent     = %d",
7448                    number_transparent);
7449            else
7450
7451              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7452                    "      number_transparent     > 256");
7453
7454            if (number_opaque < 257)
7455              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7456                    "      number_opaque          = %d",
7457                    number_opaque);
7458
7459            else
7460              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7461                    "      number_opaque          > 256");
7462
7463            if (number_semitransparent < 257)
7464              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7465                    "      number_semitransparent = %d",
7466                    number_semitransparent);
7467
7468            else
7469              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7470                    "      number_semitransparent > 256");
7471
7472            if (ping_have_non_bw == MagickFalse)
7473               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7474                     "      All pixels and the background are black or white");
7475
7476            else if (ping_have_color == MagickFalse)
7477               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7478                     "      All pixels and the background are gray");
7479
7480            else
7481               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7482                     "      At least one pixel or the background is non-gray");
7483
7484            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7485                "    Exit BUILD_PALETTE:");
7486        }
7487
7488    if (mng_info->write_png8 == MagickFalse)
7489       break;
7490
7491    /* Make any reductions necessary for the PNG8 format */
7492     if (image_colors <= 256 &&
7493         image_colors != 0 && image->colormap != NULL &&
7494         number_semitransparent == 0 &&
7495         number_transparent <= 1)
7496       break;
7497
7498     /* PNG8 can't have semitransparent colors so we threshold the
7499      * opacity to 0 or OpaqueOpacity
7500      */
7501     if (number_semitransparent != 0)
7502       {
7503         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7504             "    Thresholding the alpha channel to binary");
7505
7506         for (y=0; y < (ssize_t) image->rows; y++)
7507         {
7508           r=GetAuthenticPixels(image,0,y,image->columns,1,
7509               exception);
7510
7511           if (r == (PixelPacket *) NULL)
7512             break;
7513
7514           for (x=0; x < (ssize_t) image->columns; x++)
7515           {
7516               r->opacity = r->opacity > TransparentOpacity/2 ?
7517                    TransparentOpacity : OpaqueOpacity;
7518               r++;
7519           }
7520   
7521           if (SyncAuthenticPixels(image,exception) == MagickFalse)
7522              break;
7523
7524           if (image_colors != 0 && image_colors <= 256 &&
7525              image->colormap != NULL)
7526             for (i=0; i<image_colors; i++)
7527                 image->colormap[i].opacity =
7528                     image->colormap[i].opacity > TransparentOpacity/2 ?
7529                     TransparentOpacity : OpaqueOpacity;
7530         }
7531       continue;
7532     }
7533
7534     /* PNG8 can't have more than 256 colors so we quantize the pixels and
7535      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
7536      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
7537      * colors or less.
7538      */
7539     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7540       {
7541         if (logging != MagickFalse)
7542            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7543                "    Quantizing the background color to 4-4-4");
7544
7545         tried_444 = MagickTrue;
7546
7547         image->background_color.red=
7548             ((((((size_t)
7549             image->background_color.red) >> PNGK) & 0xf0)     )  |
7550             (((((size_t)
7551             image->background_color.red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7552         image->background_color.green=
7553             ((((((size_t)
7554             image->background_color.green) >> PNGK) & 0xf0)     )  |
7555             (((((size_t)
7556             image->background_color.green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7557         image->background_color.blue=
7558             ((((((size_t)
7559             image->background_color.blue) >> PNGK) & 0xf0)     )  |
7560             (((((size_t)
7561             image->background_color.blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7562
7563         if (logging != MagickFalse)
7564           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7565               "    Quantizing the pixel colors to 4-4-4");
7566
7567         if (image->colormap == NULL)
7568         {
7569           for (y=0; y < (ssize_t) image->rows; y++)
7570           {
7571             r=GetAuthenticPixels(image,0,y,image->columns,1,
7572                 exception);
7573
7574             if (r == (PixelPacket *) NULL)
7575               break;
7576
7577             for (x=0; x < (ssize_t) image->columns; x++)
7578             {
7579               if (r->opacity == TransparentOpacity)
7580                 {
7581                   r->red = image->background_color.red;
7582                   r->green = image->background_color.green;
7583                   r->blue = image->background_color.blue;
7584                 }
7585               else
7586                 {
7587                   r->red=
7588                        ((((((size_t) r->red) >> PNGK) & 0xf0)    )  |
7589                        (((((size_t) r->red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7590                   r->green=
7591                        ((((((size_t) r->green) >> PNGK) & 0xf0)    )  |
7592                        (((((size_t) r->green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7593                   r->blue=
7594                        ((((((size_t) r->blue) >> PNGK) & 0xf0)    )  |
7595                        (((((size_t) r->blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7596                 }
7597               r++;
7598             }
7599     
7600             if (SyncAuthenticPixels(image,exception) == MagickFalse)
7601                break;
7602           }
7603         }
7604
7605         else /* Should not reach this; colormap already exists and
7606                 must be <= 256 */
7607         {
7608           if (logging != MagickFalse)
7609               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7610               "    Quantizing the colormap to 4-4-4");
7611           for (i=0; i<image_colors; i++)
7612           {
7613               image->colormap[i].red=
7614                   ((((((size_t)
7615                   image->colormap[i].red) >> PNGK) & 0xf0)     )  |
7616                   (((((size_t)
7617                   image->colormap[i].red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7618               image->colormap[i].green=
7619                   ((((((size_t)
7620                   image->colormap[i].green) >> PNGK) & 0xf0)     )  |
7621                   (((((size_t)
7622                   image->colormap[i].green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7623               image->colormap[i].blue=
7624                   ((((((size_t)
7625                   image->colormap[i].blue) >> PNGK) & 0xf0)     )  |
7626                   (((((size_t)
7627                   image->colormap[i].blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7628           }
7629         }
7630         continue;
7631       }
7632
7633     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7634       {
7635         if (logging != MagickFalse)
7636            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7637                "    Quantizing the background color to 3-3-3");
7638
7639         tried_333 = MagickTrue;
7640
7641         image->background_color.red=
7642             ((((((size_t)
7643             image->background_color.red) >> PNGK) & 0xe0)     )  |
7644             (((((size_t)
7645             image->background_color.red) >> PNGK) & 0xe0) >> 3)  |
7646             (((((size_t)
7647             image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7648         image->background_color.green=
7649             ((((((size_t)
7650             image->background_color.green) >> PNGK) & 0xe0)     )  |
7651             (((((size_t)
7652             image->background_color.green) >> PNGK) & 0xe0) >> 3)  |
7653             (((((size_t)
7654             image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7655         image->background_color.blue=
7656             ((((((size_t)
7657             image->background_color.blue) >> PNGK) & 0xe0)     )  |
7658             (((((size_t)
7659             image->background_color.blue) >> PNGK) & 0xe0) >> 3)  |
7660             (((((size_t)
7661             image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7662
7663         if (logging != MagickFalse)
7664           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7665               "    Quantizing the pixel colors to 3-3-3-1");
7666
7667         if (image->colormap == NULL)
7668         {
7669           for (y=0; y < (ssize_t) image->rows; y++)
7670           {
7671             r=GetAuthenticPixels(image,0,y,image->columns,1,
7672                 exception);
7673
7674             if (r == (PixelPacket *) NULL)
7675               break;
7676
7677             for (x=0; x < (ssize_t) image->columns; x++)
7678             {
7679               if (r->opacity == TransparentOpacity)
7680                 {
7681                   r->red = image->background_color.red;
7682                   r->green = image->background_color.green;
7683                   r->blue = image->background_color.blue;
7684                 }
7685               else
7686                 {
7687                   r->red=
7688                        ((((((size_t) r->red) >> PNGK) & 0xe0)    )  |
7689                        (((((size_t) r->red) >> PNGK) & 0xe0) >> 3)  |
7690                        (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7691                   r->green=
7692                        ((((((size_t) r->green) >> PNGK) & 0xe0)    )  |
7693                        (((((size_t) r->green) >> PNGK) & 0xe0) >> 3)  |
7694                        (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7695                   r->blue=
7696                        ((((((size_t) r->blue) >> PNGK) & 0xe0)    )  |
7697                        (((((size_t) r->blue) >> PNGK) & 0xe0) >> 3)  |
7698                        (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7699                 }
7700               r++;
7701             }
7702     
7703             if (SyncAuthenticPixels(image,exception) == MagickFalse)
7704                break;
7705           }
7706         }
7707
7708         else /* Should not reach this; colormap already exists and
7709                 must be <= 256 */
7710         {
7711           if (logging != MagickFalse)
7712               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7713               "    Quantizing the colormap to 3-3-3-1");
7714           for (i=0; i<image_colors; i++)
7715           {
7716               image->colormap[i].red=
7717                   ((((((size_t)
7718                   image->colormap[i].red) >> PNGK) & 0xe0)     )  |
7719                   (((((size_t)
7720                   image->colormap[i].red) >> PNGK) & 0xe0) >> 3)  |
7721                   (((((size_t)
7722                   image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7723               image->colormap[i].green=
7724                   ((((((size_t)
7725                   image->colormap[i].green) >> PNGK) & 0xe0)     )  |
7726                   (((((size_t)
7727                   image->colormap[i].green) >> PNGK) & 0xe0) >> 3)  |
7728                   (((((size_t)
7729                   image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7730               image->colormap[i].blue=
7731                   ((((((size_t)
7732                   image->colormap[i].blue) >> PNGK) & 0xe0)     )  |
7733                   (((((size_t)
7734                   image->colormap[i].blue) >> PNGK) & 0xe0) >> 3)  |
7735                   (((((size_t)
7736                   image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7737           }
7738         }
7739         continue;
7740       }
7741
7742     if (image_colors == 0 || image_colors > 256)
7743       {
7744         if (logging != MagickFalse)
7745            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7746                "    Quantizing the background color to 3-3-2");
7747
7748         image->background_color.red=
7749             ((((((size_t)
7750             image->background_color.red) >> PNGK) & 0xe0)     )  |
7751             (((((size_t)
7752             image->background_color.red) >> PNGK) & 0xe0) >> 3)  |
7753             (((((size_t)
7754             image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7755         image->background_color.green=
7756             ((((((size_t)
7757             image->background_color.green) >> PNGK) & 0xe0)     )  |
7758             (((((size_t)
7759             image->background_color.green) >> PNGK) & 0xe0) >> 3)  |
7760             (((((size_t)
7761             image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7762         image->background_color.blue=
7763             ((((((size_t)
7764             image->background_color.blue) >> PNGK) & 0xc0)     )  |
7765             (((((size_t)
7766             image->background_color.blue) >> PNGK) & 0xc0) >> 2)  |
7767             (((((size_t)
7768             image->background_color.blue) >> PNGK) & 0xc0) >> 4)  |
7769             (((((size_t)
7770             image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7771
7772         if (logging != MagickFalse)
7773           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7774               "    Quantizing the pixel colors to 3-3-2-1");
7775
7776         if (image->colormap == NULL)
7777         {
7778           for (y=0; y < (ssize_t) image->rows; y++)
7779           {
7780             r=GetAuthenticPixels(image,0,y,image->columns,1,
7781                 exception);
7782
7783             if (r == (PixelPacket *) NULL)
7784               break;
7785
7786             for (x=0; x < (ssize_t) image->columns; x++)
7787             {
7788               if (r->opacity == TransparentOpacity)
7789                 {
7790                   r->red = image->background_color.red;
7791                   r->green = image->background_color.green;
7792                   r->blue = image->background_color.blue;
7793                 }
7794               else
7795                 {
7796                   r->red=
7797                        ((((((size_t) r->red) >> PNGK) & 0xe0)    )  |
7798                        (((((size_t) r->red) >> PNGK) & 0xe0) >> 3)  |
7799                        (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7800                   r->green=
7801                        ((((((size_t) r->green) >> PNGK) & 0xe0)    )  |
7802                        (((((size_t) r->green) >> PNGK) & 0xe0) >> 3)  |
7803                        (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7804                   r->blue=
7805                        ((((((size_t) r->blue) >> PNGK) & 0xc0)    )  |
7806                        (((((size_t) r->blue) >> PNGK) & 0xc0) >> 2)  |
7807                        (((((size_t) r->blue) >> PNGK) & 0xc0) >> 4)  |
7808                        (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7809                 }
7810               r++;
7811             }
7812     
7813             if (SyncAuthenticPixels(image,exception) == MagickFalse)
7814                break;
7815           }
7816         }
7817
7818         else /* Should not reach this; colormap already exists and
7819                 must be <= 256 */
7820         {
7821           if (logging != MagickFalse)
7822               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7823               "    Quantizing the colormap to 3-3-2-1");
7824           for (i=0; i<image_colors; i++)
7825           {
7826               image->colormap[i].red=
7827                   ((((((size_t)
7828                   image->colormap[i].red) >> PNGK) & 0xe0)     )  |
7829                   (((((size_t)
7830                   image->colormap[i].red) >> PNGK) & 0xe0) >> 3)  |
7831                   (((((size_t)
7832                   image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7833               image->colormap[i].green=
7834                   ((((((size_t)
7835                   image->colormap[i].green) >> PNGK) & 0xe0)     )  |
7836                   (((((size_t)
7837                   image->colormap[i].green) >> PNGK) & 0xe0) >> 3)  |
7838                   (((((size_t)
7839                   image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7840               image->colormap[i].blue=
7841                   ((((((size_t)
7842                   image->colormap[i].blue) >> PNGK) & 0xc0)     )  |
7843                   (((((size_t)
7844                   image->colormap[i].blue) >> PNGK) & 0xc0) >> 2)  |
7845                   (((((size_t)
7846                   image->colormap[i].blue) >> PNGK) & 0xc0) >> 4)  |
7847                   (((((size_t)
7848                   image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7849           }
7850       }
7851       continue;
7852     }
7853     break;
7854   }
7855   /* END OF BUILD_PALETTE */
7856
7857   /* If we are excluding the tRNS chunk and there is transparency,
7858    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
7859    * PNG.
7860    */
7861   if (mng_info->ping_exclude_tRNS != MagickFalse &&
7862      (number_transparent != 0 || number_semitransparent != 0))
7863     {
7864       int colortype=mng_info->write_png_colortype;
7865
7866       if (ping_have_color == MagickFalse)
7867         mng_info->write_png_colortype = 5;
7868
7869       else
7870         mng_info->write_png_colortype = 7;
7871
7872       if (colortype != 0 &&
7873          mng_info->write_png_colortype != (ssize_t) colortype)
7874         ping_need_colortype_warning=MagickTrue;
7875
7876     }
7877
7878   /* See if cheap transparency is possible.  It is only possible
7879    * when there is a single transparent color, no semitransparent
7880    * color, and no opaque color that has the same RGB components
7881    * as the transparent color.  We only need this information if
7882    * we are writing a PNG with colortype 0 or 2, and we have not
7883    * excluded the tRNS chunk.
7884    */
7885   if (number_transparent == 1 &&
7886       mng_info->write_png_colortype < 4)
7887     {
7888        ping_have_cheap_transparency = MagickTrue;
7889
7890        if (number_semitransparent != 0)
7891          ping_have_cheap_transparency = MagickFalse;
7892
7893        else if (image_colors == 0 || image_colors > 256 ||
7894            image->colormap == NULL)
7895          {
7896            ExceptionInfo
7897              *exception;
7898
7899            register const PixelPacket
7900              *q;
7901
7902            exception=(&image->exception);
7903
7904            for (y=0; y < (ssize_t) image->rows; y++)
7905            {
7906              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
7907
7908              if (q == (PixelPacket *) NULL)
7909                break;
7910
7911              for (x=0; x < (ssize_t) image->columns; x++)
7912              {
7913                  if (q->opacity != TransparentOpacity &&
7914                      (unsigned short) q->red == ping_trans_color.red &&
7915                      (unsigned short) q->green == ping_trans_color.green &&
7916                      (unsigned short) q->blue == ping_trans_color.blue)
7917                    {
7918                      ping_have_cheap_transparency = MagickFalse;
7919                      break;
7920                    }
7921
7922                  q++;
7923              }
7924     
7925              if (ping_have_cheap_transparency == MagickFalse)
7926                 break;
7927            }
7928          }
7929        else
7930          {
7931             /* Assuming that image->colormap[0] is the one transparent color
7932              * and that all others are opaque.
7933              */
7934             if (image_colors > 1)
7935               for (i=1; i<image_colors; i++)
7936                 if (image->colormap[i].red == image->colormap[0].red &&
7937                     image->colormap[i].green == image->colormap[0].green &&
7938                     image->colormap[i].blue == image->colormap[0].blue)
7939                   {
7940                      ping_have_cheap_transparency = MagickFalse;
7941                      break;
7942                   }
7943          }
7944        
7945        if (logging != MagickFalse)
7946          {
7947            if (ping_have_cheap_transparency == MagickFalse)
7948              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7949                  "   Cheap transparency is not possible.");
7950
7951            else
7952              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7953                  "   Cheap transparency is possible.");
7954          }
7955      }
7956   else
7957     ping_have_cheap_transparency = MagickFalse;
7958
7959   image_depth=image->depth;
7960
7961   quantum_info = (QuantumInfo *) NULL;
7962   number_colors=0;
7963   image_colors=(int) image->colors;
7964   image_matte=image->matte;
7965
7966   mng_info->IsPalette=image->storage_class == PseudoClass &&
7967     image_colors <= 256 && image->colormap != NULL;
7968
7969   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
7970      (image->colors == 0 || image->colormap == NULL))
7971     {
7972       image_info=DestroyImageInfo(image_info);
7973       image=DestroyImage(image);
7974       (void) ThrowMagickException(&IMimage->exception,
7975           GetMagickModule(),CoderError,
7976           "Cannot write PNG8 or color-type 3; colormap is NULL",
7977           "`%s'",IMimage->filename);
7978 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7979       UnlockSemaphoreInfo(ping_semaphore);
7980 #endif
7981       return(MagickFalse);
7982     }
7983
7984   /*
7985     Allocate the PNG structures
7986   */
7987 #ifdef PNG_USER_MEM_SUPPORTED
7988   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
7989     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
7990     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
7991
7992 #else
7993   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
7994     MagickPNGErrorHandler,MagickPNGWarningHandler);
7995
7996 #endif
7997   if (ping == (png_struct *) NULL)
7998     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7999
8000   ping_info=png_create_info_struct(ping);
8001
8002   if (ping_info == (png_info *) NULL)
8003     {
8004       png_destroy_write_struct(&ping,(png_info **) NULL);
8005       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8006     }
8007
8008   png_set_write_fn(ping,image,png_put_data,png_flush_data);
8009   ping_pixels=(unsigned char *) NULL;
8010
8011   if (setjmp(png_jmpbuf(ping)))
8012     {
8013       /*
8014         PNG write failed.
8015       */
8016 #ifdef PNG_DEBUG
8017      if (image_info->verbose)
8018         (void) printf("PNG write has failed.\n");
8019 #endif
8020       png_destroy_write_struct(&ping,&ping_info);
8021 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8022       UnlockSemaphoreInfo(ping_semaphore);
8023 #endif
8024       if (ping_have_blob != MagickFalse)
8025           (void) CloseBlob(image);
8026       image_info=DestroyImageInfo(image_info);
8027       image=DestroyImage(image);
8028       return(MagickFalse);
8029     }
8030   /*
8031     Prepare PNG for writing.
8032   */
8033 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8034   if (mng_info->write_mng)
8035      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8036
8037 #else
8038 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8039   if (mng_info->write_mng)
8040      png_permit_empty_plte(ping,MagickTrue);
8041
8042 # endif
8043 #endif
8044
8045   x=0;
8046
8047   ping_width=(png_uint_32) image->columns;
8048   ping_height=(png_uint_32) image->rows;
8049
8050   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8051      image_depth=8;
8052
8053   if (mng_info->write_png_depth != 0)
8054      image_depth=mng_info->write_png_depth;
8055
8056   /* Adjust requested depth to next higher valid depth if necessary */
8057   if (image_depth > 8)
8058      image_depth=16;
8059
8060   if ((image_depth > 4) && (image_depth < 8))
8061      image_depth=8;
8062
8063   if (image_depth == 3)
8064      image_depth=4;
8065
8066   if (logging != MagickFalse)
8067     {
8068      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8069         "    width=%.20g",(double) ping_width);
8070      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8071         "    height=%.20g",(double) ping_height);
8072      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8073         "    image_matte=%.20g",(double) image->matte);
8074      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8075         "    image->depth=%.20g",(double) image->depth);
8076      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8077         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
8078     }
8079
8080   save_image_depth=image_depth;
8081   ping_bit_depth=(png_byte) save_image_depth;
8082
8083
8084 #if defined(PNG_pHYs_SUPPORTED)
8085   if (ping_exclude_pHYs == MagickFalse)
8086   {
8087   if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8088       (!mng_info->write_mng || !mng_info->equal_physs))
8089     {
8090       if (logging != MagickFalse)
8091         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8092             "    Setting up pHYs chunk");
8093
8094       if (image->units == PixelsPerInchResolution)
8095         {
8096           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8097           ping_pHYs_x_resolution=
8098              (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8099           ping_pHYs_y_resolution=
8100              (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8101         }
8102
8103       else if (image->units == PixelsPerCentimeterResolution)
8104         {
8105           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8106           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8107           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
8108         }
8109
8110       else
8111         {
8112           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8113           ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8114           ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
8115         }
8116
8117       if (logging != MagickFalse)
8118         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8119           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8120           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8121           (int) ping_pHYs_unit_type);
8122        ping_have_pHYs = MagickTrue;
8123     }
8124   }
8125 #endif
8126
8127   if (ping_exclude_bKGD == MagickFalse)
8128   {
8129   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
8130     {
8131        unsigned int
8132          mask;
8133
8134        mask=0xffff;
8135        if (ping_bit_depth == 8)
8136           mask=0x00ff;
8137
8138        if (ping_bit_depth == 4)
8139           mask=0x000f;
8140
8141        if (ping_bit_depth == 2)
8142           mask=0x0003;
8143
8144        if (ping_bit_depth == 1)
8145           mask=0x0001;
8146
8147        ping_background.red=(png_uint_16)
8148          (ScaleQuantumToShort(image->background_color.red) & mask);
8149
8150        ping_background.green=(png_uint_16)
8151          (ScaleQuantumToShort(image->background_color.green) & mask);
8152
8153        ping_background.blue=(png_uint_16)
8154          (ScaleQuantumToShort(image->background_color.blue) & mask);
8155     }
8156
8157   if (logging != MagickFalse)
8158     {
8159       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8160           "    Setting up bKGD chunk (1)");
8161
8162       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8163           "    ping_bit_depth=%d",ping_bit_depth);
8164     }
8165
8166   ping_have_bKGD = MagickTrue;
8167   }
8168
8169   /*
8170     Select the color type.
8171   */
8172   matte=image_matte;
8173   old_bit_depth=0;
8174
8175   if (mng_info->IsPalette && mng_info->write_png8)
8176     {
8177
8178       /* To do: make this a function cause it's used twice, except
8179          for reducing the sample depth from 8. */
8180
8181       number_colors=image_colors;
8182
8183       ping_have_tRNS=MagickFalse;
8184
8185       /*
8186         Set image palette.
8187       */
8188       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8189
8190       if (logging != MagickFalse)
8191         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8192             "  Setting up PLTE chunk with %d colors (%d)",
8193             number_colors, image_colors);
8194
8195       for (i=0; i < (ssize_t) number_colors; i++)
8196       {
8197         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8198         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8199         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8200         if (logging != MagickFalse)
8201           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8202 #if MAGICKCORE_QUANTUM_DEPTH == 8
8203             "    %3ld (%3d,%3d,%3d)",
8204 #else
8205             "    %5ld (%5d,%5d,%5d)",
8206 #endif
8207             (long) i,palette[i].red,palette[i].green,palette[i].blue);
8208
8209       }
8210
8211       ping_have_PLTE=MagickTrue;
8212       image_depth=ping_bit_depth;
8213       ping_num_trans=0;
8214
8215       if (matte != MagickFalse)
8216       {
8217           /*
8218             Identify which colormap entry is transparent.
8219           */
8220           assert(number_colors <= 256);
8221           assert(image->colormap != NULL);
8222
8223           for (i=0; i < (ssize_t) number_transparent; i++)
8224              ping_trans_alpha[i]=0;
8225
8226
8227           ping_num_trans=(unsigned short) (number_transparent +
8228              number_semitransparent);
8229
8230           if (ping_num_trans == 0)
8231              ping_have_tRNS=MagickFalse;
8232
8233           else
8234              ping_have_tRNS=MagickTrue;
8235       }
8236
8237       if (ping_exclude_bKGD == MagickFalse)
8238       {
8239        /*
8240         * Identify which colormap entry is the background color.
8241         */
8242
8243         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8244           if (IsPNGColorEqual(ping_background,image->colormap[i]))
8245             break;
8246
8247         ping_background.index=(png_byte) i;
8248       }
8249     } /* end of write_png8 */
8250
8251   else if (mng_info->write_png24)
8252     {
8253       image_matte=MagickFalse;
8254       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8255     }
8256
8257   else if (mng_info->write_png32)
8258     {
8259       image_matte=MagickTrue;
8260       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8261     }
8262
8263   else /* mng_info->write_pngNN not specified */
8264     {
8265       image_depth=ping_bit_depth;
8266
8267       if (mng_info->write_png_colortype != 0)
8268         {
8269           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
8270
8271           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8272               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8273             image_matte=MagickTrue;
8274
8275           else
8276             image_matte=MagickFalse;
8277
8278           if (logging != MagickFalse)
8279              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8280              "   PNG colortype %d was specified:",(int) ping_color_type);
8281         }
8282
8283       else /* write_png_colortype not specified */
8284         {
8285           if (logging != MagickFalse)
8286              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8287              "  Selecting PNG colortype:");
8288
8289           ping_color_type=(png_byte) ((matte != MagickFalse)?
8290             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
8291
8292           if (image_info->type == TrueColorType)
8293             {
8294               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8295               image_matte=MagickFalse;
8296             }
8297
8298           if (image_info->type == TrueColorMatteType)
8299             {
8300               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8301               image_matte=MagickTrue;
8302             }
8303
8304           if (image_info->type == PaletteType ||
8305               image_info->type == PaletteMatteType)
8306             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8307
8308           if (mng_info->write_png_colortype == 0 &&
8309              (image_info->type == UndefinedType ||
8310              image_info->type == OptimizeType))
8311             {
8312               if (ping_have_color == MagickFalse)
8313                 {
8314                   if (image_matte == MagickFalse)
8315                     {
8316                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8317                       image_matte=MagickFalse;
8318                     }
8319
8320                   else
8321                     {
8322                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8323                       image_matte=MagickTrue;
8324                     }
8325                 }
8326               else
8327                 {
8328                   if (image_matte == MagickFalse)
8329                     {
8330                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8331                       image_matte=MagickFalse;
8332                     }
8333
8334                   else
8335                     {
8336                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8337                       image_matte=MagickTrue;
8338                     }
8339                  }
8340             }
8341
8342         }
8343
8344       if (logging != MagickFalse)
8345          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8346          "    Selected PNG colortype=%d",ping_color_type);
8347
8348       if (ping_bit_depth < 8)
8349         {
8350           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8351               ping_color_type == PNG_COLOR_TYPE_RGB ||
8352               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8353             ping_bit_depth=8;
8354         }
8355
8356       old_bit_depth=ping_bit_depth;
8357
8358       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8359         {
8360           if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8361              ping_bit_depth=1;
8362         }
8363
8364       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
8365         {
8366            size_t one = 1;
8367            ping_bit_depth=1;
8368
8369            if (image->colors == 0)
8370            {
8371               /* DO SOMETHING */
8372               (void) ThrowMagickException(&image->exception,
8373                  GetMagickModule(),CoderError,
8374                 "image has 0 colors", "`%s'","");
8375            }
8376
8377            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
8378              ping_bit_depth <<= 1;
8379         }
8380
8381       if (logging != MagickFalse)
8382          {
8383            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8384             "    Number of colors: %.20g",(double) image_colors);
8385
8386            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8387             "    Tentative PNG bit depth: %d",ping_bit_depth);
8388          }
8389
8390       if (ping_bit_depth < (int) mng_info->write_png_depth)
8391          ping_bit_depth = mng_info->write_png_depth;
8392     }
8393
8394   image_depth=ping_bit_depth;
8395
8396   if (logging != MagickFalse)
8397     {
8398       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8399         "    Tentative PNG color type: %.20g",(double) ping_color_type);
8400
8401       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8402         "    image_info->type: %.20g",(double) image_info->type);
8403
8404       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8405         "    image_depth: %.20g",(double) image_depth);
8406
8407       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8408
8409         "    image->depth: %.20g",(double) image->depth);
8410
8411       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8412         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
8413     }
8414
8415   if (matte != MagickFalse)
8416     {
8417       if (mng_info->IsPalette)
8418         {
8419           if (mng_info->write_png_colortype == 0)
8420             {
8421               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8422
8423               if (ping_have_color != MagickFalse)
8424                  ping_color_type=PNG_COLOR_TYPE_RGBA;
8425             }
8426
8427           /*
8428            * Determine if there is any transparent color.
8429           */
8430           if (number_transparent + number_semitransparent == 0)
8431             {
8432               /*
8433                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
8434               */
8435
8436               image_matte=MagickFalse;
8437
8438               if (mng_info->write_png_colortype == 0)
8439                 ping_color_type&=0x03;
8440             }
8441
8442           else
8443             {
8444               unsigned int
8445                 mask; 
8446
8447               mask=0xffff;
8448
8449               if (ping_bit_depth == 8)
8450                  mask=0x00ff;
8451
8452               if (ping_bit_depth == 4)
8453                  mask=0x000f;
8454
8455               if (ping_bit_depth == 2)
8456                  mask=0x0003;
8457
8458               if (ping_bit_depth == 1)
8459                  mask=0x0001;
8460
8461               ping_trans_color.red=(png_uint_16)
8462                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8463
8464               ping_trans_color.green=(png_uint_16)
8465                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8466
8467               ping_trans_color.blue=(png_uint_16)
8468                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8469
8470               ping_trans_color.gray=(png_uint_16)
8471                 (ScaleQuantumToShort(PixelIntensityToQuantum(
8472                    image->colormap)) & mask);
8473
8474               ping_trans_color.index=(png_byte) 0;
8475
8476               ping_have_tRNS=MagickTrue;
8477             }
8478
8479           if (ping_have_tRNS != MagickFalse)
8480             {
8481               /*
8482                * Determine if there is one and only one transparent color
8483                * and if so if it is fully transparent.
8484                */
8485               if (ping_have_cheap_transparency == MagickFalse)
8486                 ping_have_tRNS=MagickFalse;
8487             }
8488
8489           if (ping_have_tRNS != MagickFalse)
8490             {
8491               if (mng_info->write_png_colortype == 0)
8492                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
8493
8494               if (image_depth == 8)
8495                 {
8496                   ping_trans_color.red&=0xff;
8497                   ping_trans_color.green&=0xff;
8498                   ping_trans_color.blue&=0xff;
8499                   ping_trans_color.gray&=0xff;
8500                 }
8501             }
8502         }
8503       else
8504         {
8505           if (image_depth == 8)
8506             {
8507               ping_trans_color.red&=0xff;
8508               ping_trans_color.green&=0xff;
8509               ping_trans_color.blue&=0xff;
8510               ping_trans_color.gray&=0xff;
8511             }
8512         }
8513     }
8514
8515     matte=image_matte;
8516
8517     if (ping_have_tRNS != MagickFalse)
8518       image_matte=MagickFalse;
8519
8520     if ((mng_info->IsPalette) &&
8521         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
8522         ping_have_color == MagickFalse &&
8523         (image_matte == MagickFalse || image_depth >= 8))
8524       {
8525         size_t one=1;
8526
8527         if (image_matte != MagickFalse)
8528           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8529
8530         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
8531           {
8532             ping_color_type=PNG_COLOR_TYPE_GRAY;
8533
8534             if (save_image_depth == 16 && image_depth == 8)
8535               {
8536                 if (logging != MagickFalse)
8537                   {
8538                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8539                         "  Scaling ping_trans_color (0)");
8540                   }
8541                     ping_trans_color.gray*=0x0101;
8542               }
8543           }
8544
8545         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8546           image_depth=MAGICKCORE_QUANTUM_DEPTH;
8547
8548         if (image_colors == 0 || image_colors-1 > MaxColormapSize)
8549           image_colors=(int) (one << image_depth);
8550
8551         if (image_depth > 8)
8552           ping_bit_depth=16;
8553
8554         else
8555           {
8556             ping_bit_depth=8;
8557             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8558               {
8559                 if(!mng_info->write_png_depth)
8560                   {
8561                     ping_bit_depth=1;
8562
8563                     while ((int) (one << ping_bit_depth)
8564                         < (ssize_t) image_colors)
8565                       ping_bit_depth <<= 1;
8566                   }
8567               }
8568
8569             else if (ping_color_type ==
8570                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
8571                 mng_info->IsPalette)
8572               {
8573               /* Check if grayscale is reducible */
8574
8575                 int
8576                   depth_4_ok=MagickTrue,
8577                   depth_2_ok=MagickTrue,
8578                   depth_1_ok=MagickTrue;
8579
8580                 for (i=0; i < (ssize_t) image_colors; i++)
8581                 {
8582                    unsigned char
8583                      intensity;
8584
8585                    intensity=ScaleQuantumToChar(image->colormap[i].red);
8586
8587                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8588                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8589                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8590                      depth_2_ok=depth_1_ok=MagickFalse;
8591                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
8592                      depth_1_ok=MagickFalse;
8593                 }
8594
8595                 if (depth_1_ok && mng_info->write_png_depth <= 1)
8596                   ping_bit_depth=1;
8597
8598                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
8599                   ping_bit_depth=2;
8600
8601                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
8602                   ping_bit_depth=4;
8603               }
8604           }
8605
8606           image_depth=ping_bit_depth;
8607       }
8608
8609     else
8610
8611       if (mng_info->IsPalette)
8612       {
8613         number_colors=image_colors;
8614
8615         if (image_depth <= 8)
8616           {
8617             /*
8618               Set image palette.
8619             */
8620             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8621
8622             if (mng_info->have_write_global_plte && matte == MagickFalse)
8623               {
8624                 png_set_PLTE(ping,ping_info,NULL,0);
8625
8626                 if (logging != MagickFalse)
8627                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8628                     "  Setting up empty PLTE chunk");
8629               }
8630
8631             else
8632               {
8633                 for (i=0; i < (ssize_t) number_colors; i++)
8634                 {
8635                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8636                   palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8637                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8638                 }
8639
8640                 if (logging != MagickFalse)
8641                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8642                     "  Setting up PLTE chunk with %d colors",
8643                     number_colors);
8644
8645                 ping_have_PLTE=MagickTrue;
8646               }
8647
8648             /* color_type is PNG_COLOR_TYPE_PALETTE */
8649             if (mng_info->write_png_depth == 0)
8650               {
8651                 size_t
8652                   one;
8653
8654                 ping_bit_depth=1;
8655                 one=1;
8656
8657                 while ((one << ping_bit_depth) < number_colors)
8658                   ping_bit_depth <<= 1;
8659               }
8660
8661             ping_num_trans=0;
8662
8663             if (matte != MagickFalse)
8664               {
8665                 /*
8666                  * Set up trans_colors array.
8667                  */
8668                 assert(number_colors <= 256);
8669
8670                 ping_num_trans=(unsigned short) (number_transparent +
8671                   number_semitransparent);
8672
8673                 if (ping_num_trans == 0)
8674                   ping_have_tRNS=MagickFalse;
8675
8676                 else
8677                   {
8678                     if (logging != MagickFalse)
8679                       {
8680                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8681                           "  Scaling ping_trans_color (1)");
8682                       }
8683                     ping_have_tRNS=MagickTrue;
8684
8685                     for (i=0; i < ping_num_trans; i++)
8686                     {
8687                        ping_trans_alpha[i]= (png_byte) (255-
8688                           ScaleQuantumToChar(image->colormap[i].opacity));
8689                     }
8690                   }
8691               }
8692           }
8693       }
8694
8695     else
8696       {
8697
8698         if (image_depth < 8)
8699           image_depth=8;
8700
8701         if ((save_image_depth == 16) && (image_depth == 8))
8702           {
8703             if (logging != MagickFalse)
8704               {
8705                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8706                   "    Scaling ping_trans_color from (%d,%d,%d)",
8707                   (int) ping_trans_color.red,
8708                   (int) ping_trans_color.green,
8709                   (int) ping_trans_color.blue);
8710               }
8711
8712             ping_trans_color.red*=0x0101;
8713             ping_trans_color.green*=0x0101;
8714             ping_trans_color.blue*=0x0101;
8715             ping_trans_color.gray*=0x0101;
8716
8717             if (logging != MagickFalse)
8718               {
8719                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8720                   "    to (%d,%d,%d)",
8721                   (int) ping_trans_color.red,
8722                   (int) ping_trans_color.green,
8723                   (int) ping_trans_color.blue);
8724               }
8725           }
8726       }
8727
8728     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
8729          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
8730
8731     /*
8732       Adjust background and transparency samples in sub-8-bit grayscale files.
8733     */
8734     if (ping_bit_depth < 8 && ping_color_type ==
8735         PNG_COLOR_TYPE_GRAY)
8736       {
8737          png_uint_16
8738            maxval;
8739
8740          size_t
8741            one=1;
8742
8743          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
8744
8745          if (ping_exclude_bKGD == MagickFalse)
8746          {
8747
8748          ping_background.gray=(png_uint_16)
8749            (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8750
8751          if (logging != MagickFalse)
8752            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8753              "  Setting up bKGD chunk (2)");
8754
8755          ping_have_bKGD = MagickTrue;
8756          }
8757
8758          ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8759            ping_trans_color.gray));
8760       }
8761
8762   if (ping_exclude_bKGD == MagickFalse)
8763   {
8764     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8765       {
8766         /*
8767            Identify which colormap entry is the background color.
8768         */
8769
8770         number_colors=image_colors;
8771
8772         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8773           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
8774             break;
8775
8776         ping_background.index=(png_byte) i;
8777
8778         if (logging != MagickFalse)
8779           {
8780             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8781               "  Setting up bKGD chunk with index=%d",(int) i);
8782           }
8783
8784         if (i < (ssize_t) number_colors)
8785           {
8786             ping_have_bKGD = MagickTrue;
8787
8788             if (logging != MagickFalse)
8789               {
8790                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8791                   "     background   =(%d,%d,%d)",
8792                         (int) ping_background.red,
8793                         (int) ping_background.green,
8794                         (int) ping_background.blue);
8795               }
8796           }
8797
8798         else  /* Can't happen */
8799           {
8800             if (logging != MagickFalse)
8801               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8802                   "      No room in PLTE to add bKGD color");
8803             ping_have_bKGD = MagickFalse;
8804           }
8805       }
8806   }
8807
8808   if (logging != MagickFalse)
8809     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8810       "    PNG color type: %d",ping_color_type);
8811   /*
8812     Initialize compression level and filtering.
8813   */
8814   if (logging != MagickFalse)
8815     {
8816       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8817         "  Setting up deflate compression");
8818
8819       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8820         "    Compression buffer size: 32768");
8821     }
8822
8823   png_set_compression_buffer_size(ping,32768L);
8824
8825   if (logging != MagickFalse)
8826     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8827       "    Compression mem level: 9");
8828
8829   png_set_compression_mem_level(ping, 9);
8830
8831   quality=image->quality == UndefinedCompressionQuality ? 75UL :
8832      image->quality;
8833
8834   if (quality > 9)
8835     {
8836       int
8837         level;
8838
8839       level=(int) MagickMin((ssize_t) quality/10,9);
8840
8841       if (logging != MagickFalse)
8842         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8843           "    Compression level: %d",level);
8844
8845       png_set_compression_level(ping,level);
8846     }
8847
8848   else
8849     {
8850       if (logging != MagickFalse)
8851         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8852           "    Compression strategy: Z_HUFFMAN_ONLY");
8853
8854       png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
8855     }
8856
8857   if (logging != MagickFalse)
8858     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8859       "  Setting up filtering");
8860
8861 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
8862   /* This became available in libpng-1.0.9.  Output must be a MNG. */
8863   if (mng_info->write_mng && ((quality % 10) == 7))
8864     {
8865       if (logging != MagickFalse)
8866         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8867           "    Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
8868
8869       ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
8870     }
8871
8872   else
8873     if (logging != MagickFalse)
8874       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8875         "    Filter_type: 0");
8876 #endif
8877
8878   {
8879     int
8880       base_filter;
8881
8882     if ((quality % 10) > 5)
8883       base_filter=PNG_ALL_FILTERS;
8884
8885     else
8886       if ((quality % 10) != 5)
8887         base_filter=(int) quality % 10;
8888
8889       else
8890         if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
8891             ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
8892             (quality < 50))
8893           base_filter=PNG_NO_FILTERS;
8894
8895         else
8896           base_filter=PNG_ALL_FILTERS;
8897
8898     if (logging != MagickFalse)
8899       {
8900         if (base_filter == PNG_ALL_FILTERS)
8901           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8902             "    Base filter method: ADAPTIVE");
8903         else
8904           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8905             "    Base filter method: NONE");
8906       }
8907
8908     png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
8909   }
8910
8911   if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
8912      (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
8913     {
8914       ResetImageProfileIterator(image);
8915       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8916       {
8917         profile=GetImageProfile(image,name);
8918
8919         if (profile != (StringInfo *) NULL)
8920           {
8921 #ifdef PNG_WRITE_iCCP_SUPPORTED
8922             if ((LocaleCompare(name,"ICC") == 0) ||
8923                 (LocaleCompare(name,"ICM") == 0))
8924              {
8925
8926                if (ping_exclude_iCCP == MagickFalse)
8927                  {
8928                        png_set_iCCP(ping,ping_info,(const png_charp) name,0,
8929 #if (PNG_LIBPNG_VER < 10500)
8930                          (png_charp) GetStringInfoDatum(profile),
8931 #else
8932                          (png_const_bytep) GetStringInfoDatum(profile),
8933 #endif
8934                          (png_uint_32) GetStringInfoLength(profile));
8935                  }
8936              }
8937
8938             else
8939 #endif
8940               if (ping_exclude_zCCP == MagickFalse)
8941                 {
8942                   Magick_png_write_raw_profile(image_info,ping,ping_info,
8943                     (unsigned char *) name,(unsigned char *) name,
8944                     GetStringInfoDatum(profile),
8945                     (png_uint_32) GetStringInfoLength(profile));
8946                 }
8947           }
8948
8949           if (logging != MagickFalse)
8950             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8951               "  Setting up text chunk with %s profile",name);
8952
8953         name=GetNextImageProfile(image);
8954       }
8955   }
8956
8957 #if defined(PNG_WRITE_sRGB_SUPPORTED)
8958   if ((mng_info->have_write_global_srgb == 0) &&
8959       ((image->rendering_intent != UndefinedIntent) ||
8960       (image->colorspace == sRGBColorspace)))
8961     {
8962       if (ping_exclude_sRGB == MagickFalse)
8963         {
8964           /*
8965             Note image rendering intent.
8966           */
8967           if (logging != MagickFalse)
8968             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8969                 "  Setting up sRGB chunk");
8970
8971           (void) png_set_sRGB(ping,ping_info,(
8972             Magick_RenderingIntent_to_PNG_RenderingIntent(
8973               image->rendering_intent)));
8974
8975           if (ping_exclude_gAMA == MagickFalse)
8976             png_set_gAMA(ping,ping_info,0.45455);
8977         }
8978     }
8979
8980   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
8981 #endif
8982     {
8983       if (ping_exclude_gAMA == MagickFalse &&
8984           (ping_exclude_sRGB == MagickFalse ||
8985           (image->gamma < .45 || image->gamma > .46)))
8986       {
8987       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
8988         {
8989           /*
8990             Note image gamma.
8991             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8992           */
8993           if (logging != MagickFalse)
8994             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8995               "  Setting up gAMA chunk");
8996
8997           png_set_gAMA(ping,ping_info,image->gamma);
8998         }
8999       }
9000
9001       if (ping_exclude_cHRM == MagickFalse)
9002         {
9003           if ((mng_info->have_write_global_chrm == 0) &&
9004               (image->chromaticity.red_primary.x != 0.0))
9005             {
9006               /*
9007                 Note image chromaticity.
9008                 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9009               */
9010                PrimaryInfo
9011                  bp,
9012                  gp,
9013                  rp,
9014                  wp;
9015
9016                wp=image->chromaticity.white_point;
9017                rp=image->chromaticity.red_primary;
9018                gp=image->chromaticity.green_primary;
9019                bp=image->chromaticity.blue_primary;
9020
9021                if (logging != MagickFalse)
9022                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9023                    "  Setting up cHRM chunk");
9024
9025                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9026                    bp.x,bp.y);
9027            }
9028         }
9029     }
9030
9031   ping_interlace_method=image_info->interlace != NoInterlace;
9032
9033   if (mng_info->write_mng)
9034     png_set_sig_bytes(ping,8);
9035
9036   /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9037
9038   if (mng_info->write_png_colortype != 0)
9039     {
9040      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
9041        if (ping_have_color != MagickFalse)
9042          {
9043            ping_color_type = PNG_COLOR_TYPE_RGB;
9044
9045            if (ping_bit_depth < 8)
9046              ping_bit_depth=8;
9047          }
9048
9049      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
9050        if (ping_have_color != MagickFalse)
9051          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
9052     }
9053
9054   if (ping_need_colortype_warning != MagickFalse ||
9055      ((mng_info->write_png_depth &&
9056      (int) mng_info->write_png_depth != ping_bit_depth) ||
9057      (mng_info->write_png_colortype &&
9058      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
9059       mng_info->write_png_colortype != 7 &&
9060       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
9061     {
9062       if (logging != MagickFalse)
9063         {
9064           if (ping_need_colortype_warning != MagickFalse)
9065             {
9066               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9067                  "  Image has transparency but tRNS chunk was excluded");
9068             }
9069
9070           if (mng_info->write_png_depth)
9071             {
9072               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9073                   "  Defined PNG:bit-depth=%u, Computed depth=%u",
9074                   mng_info->write_png_depth,
9075                   ping_bit_depth);
9076             }
9077
9078           if (mng_info->write_png_colortype)
9079             {
9080               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9081                   "  Defined PNG:color-type=%u, Computed color type=%u",
9082                   mng_info->write_png_colortype-1,
9083                   ping_color_type);
9084             }
9085         }
9086
9087       png_warning(ping,
9088         "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9089     }
9090
9091   if (image_matte != MagickFalse && image->matte == MagickFalse)
9092     {
9093       /* Add an opaque matte channel */
9094       image->matte = MagickTrue;
9095       (void) SetImageOpacity(image,0);
9096
9097       if (logging != MagickFalse)
9098         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9099           "  Added an opaque matte channel");
9100     }
9101
9102   if (number_transparent != 0 || number_semitransparent != 0)
9103     {
9104       if (ping_color_type < 4)
9105         {
9106            ping_have_tRNS=MagickTrue;
9107            if (logging != MagickFalse)
9108              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9109                "  Setting ping_have_tRNS=MagickTrue.");
9110         }
9111     }
9112
9113   if (logging != MagickFalse)
9114     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9115       "  Writing PNG header chunks");
9116
9117   png_set_IHDR(ping,ping_info,ping_width,ping_height,
9118                ping_bit_depth,ping_color_type,
9119                ping_interlace_method,ping_compression_method,
9120                ping_filter_method);
9121
9122   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9123     {
9124       png_set_PLTE(ping,ping_info,palette,number_colors);
9125
9126       if (logging != MagickFalse)
9127         {
9128           for (i=0; i< (ssize_t) number_colors; i++)
9129           {
9130             if (i < ping_num_trans)
9131               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9132                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9133                       (int) i,
9134                       (int) palette[i].red,
9135                       (int) palette[i].green,
9136                       (int) palette[i].blue,
9137                       (int) i,
9138                       (int) ping_trans_alpha[i]);
9139              else
9140               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9141                 "     PLTE[%d] = (%d,%d,%d)",
9142                       (int) i,
9143                       (int) palette[i].red,
9144                       (int) palette[i].green,
9145                       (int) palette[i].blue);
9146            }
9147          }
9148     }
9149
9150   if (ping_exclude_bKGD == MagickFalse)
9151     {
9152       if (ping_have_bKGD != MagickFalse)
9153           png_set_bKGD(ping,ping_info,&ping_background);
9154     }
9155
9156   if (ping_exclude_pHYs == MagickFalse)
9157     {
9158       if (ping_have_pHYs != MagickFalse)
9159         {
9160           png_set_pHYs(ping,ping_info,
9161              ping_pHYs_x_resolution,
9162              ping_pHYs_y_resolution,
9163              ping_pHYs_unit_type);
9164
9165           if (logging)
9166             {
9167               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9168                    "    Setting up pHYs chunk");
9169               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9170                    "      x_resolution=%lu",
9171                    (unsigned long) ping_pHYs_x_resolution);
9172               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9173                    "      y_resolution=%lu",
9174                    (unsigned long) ping_pHYs_y_resolution);
9175               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9176                    "      unit_type=%lu",
9177                    (unsigned long) ping_pHYs_unit_type);
9178             }
9179         }
9180     }
9181
9182 #if defined(PNG_oFFs_SUPPORTED)
9183   if (ping_exclude_oFFs == MagickFalse)
9184     {
9185       if (image->page.x || image->page.y)
9186         {
9187            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9188               (png_int_32) image->page.y, 0);
9189
9190            if (logging != MagickFalse)
9191              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9192                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
9193                  (int) image->page.x, (int) image->page.y);
9194         }
9195     }
9196 #endif
9197
9198   if (mng_info->need_blob != MagickFalse)
9199   {
9200     if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9201        MagickFalse)
9202        png_error(ping,"WriteBlob Failed");
9203
9204      ping_have_blob=MagickTrue;
9205   }
9206
9207   png_write_info_before_PLTE(ping, ping_info);
9208
9209   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
9210     {
9211       if (logging != MagickFalse)
9212         {
9213           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9214               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9215         }
9216
9217       if (ping_color_type == 3)
9218          (void) png_set_tRNS(ping, ping_info,
9219                 ping_trans_alpha,
9220                 ping_num_trans,
9221                 NULL);
9222
9223       else
9224         {
9225            (void) png_set_tRNS(ping, ping_info,
9226                   NULL,
9227                   0,
9228                   &ping_trans_color);
9229
9230            if (logging != MagickFalse)
9231              {
9232                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9233                  "     tRNS color   =(%d,%d,%d)",
9234                        (int) ping_trans_color.red,
9235                        (int) ping_trans_color.green,
9236                        (int) ping_trans_color.blue);
9237              }
9238          }
9239     }
9240
9241   /* write any png-chunk-b profiles */
9242   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
9243
9244   png_write_info(ping,ping_info);
9245
9246   /* write any PNG-chunk-m profiles */
9247   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
9248
9249   if (ping_exclude_vpAg == MagickFalse)
9250     {
9251       if ((image->page.width != 0 && image->page.width != image->columns) ||
9252           (image->page.height != 0 && image->page.height != image->rows))
9253         {
9254           unsigned char
9255             chunk[14];
9256
9257           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
9258           PNGType(chunk,mng_vpAg);
9259           LogPNGChunk(logging,mng_vpAg,9L);
9260           PNGLong(chunk+4,(png_uint_32) image->page.width);
9261           PNGLong(chunk+8,(png_uint_32) image->page.height);
9262           chunk[12]=0;   /* unit = pixels */
9263           (void) WriteBlob(image,13,chunk);
9264           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9265         }
9266     }
9267
9268 #if (PNG_LIBPNG_VER == 10206)
9269     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
9270 #define PNG_HAVE_IDAT               0x04
9271     ping->mode |= PNG_HAVE_IDAT;
9272 #undef PNG_HAVE_IDAT
9273 #endif
9274
9275   png_set_packing(ping);
9276   /*
9277     Allocate memory.
9278   */
9279   rowbytes=image->columns;
9280   if (image_depth > 8)
9281     rowbytes*=2;
9282   switch (ping_color_type)
9283     {
9284       case PNG_COLOR_TYPE_RGB:
9285         rowbytes*=3;
9286         break;
9287
9288       case PNG_COLOR_TYPE_GRAY_ALPHA:
9289         rowbytes*=2;
9290         break;
9291
9292       case PNG_COLOR_TYPE_RGBA:
9293         rowbytes*=4;
9294         break;
9295
9296       default:
9297         break;
9298     }
9299
9300   if (logging != MagickFalse)
9301     {
9302       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9303         "  Writing PNG image data");
9304
9305       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9306         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
9307     }
9308   ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9309     sizeof(*ping_pixels));
9310
9311   if (ping_pixels == (unsigned char *) NULL)
9312     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9313
9314   /*
9315     Initialize image scanlines.
9316   */
9317   if (setjmp(png_jmpbuf(ping)))
9318     {
9319       /*
9320         PNG write failed.
9321       */
9322 #ifdef PNG_DEBUG
9323      if (image_info->verbose)
9324         (void) printf("PNG write has failed.\n");
9325 #endif
9326       png_destroy_write_struct(&ping,&ping_info);
9327       if (quantum_info != (QuantumInfo *) NULL)
9328         quantum_info=DestroyQuantumInfo(quantum_info);
9329       if (ping_pixels != (unsigned char *) NULL)
9330         ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9331 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9332       UnlockSemaphoreInfo(ping_semaphore);
9333 #endif
9334       if (ping_have_blob != MagickFalse)
9335           (void) CloseBlob(image);
9336       image_info=DestroyImageInfo(image_info);
9337       image=DestroyImage(image);
9338       return(MagickFalse);
9339     }
9340   quantum_info=AcquireQuantumInfo(image_info,image);
9341   if (quantum_info == (QuantumInfo *) NULL)
9342     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9343   quantum_info->format=UndefinedQuantumFormat;
9344   quantum_info->depth=image_depth;
9345   num_passes=png_set_interlace_handling(ping);
9346
9347   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9348        !mng_info->write_png32) &&
9349        (mng_info->IsPalette ||
9350        (image_info->type == BilevelType)) &&
9351        image_matte == MagickFalse &&
9352        ping_have_non_bw == MagickFalse)
9353     {
9354       /* Palette, Bilevel, or Opaque Monochrome */
9355       register const PixelPacket
9356         *p;
9357
9358       quantum_info->depth=8;
9359       for (pass=0; pass < num_passes; pass++)
9360       {
9361         /*
9362           Convert PseudoClass image to a PNG monochrome image.
9363         */
9364         for (y=0; y < (ssize_t) image->rows; y++)
9365         {
9366           if (logging != MagickFalse && y == 0)
9367              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9368                  "    Writing row of pixels (0)");
9369
9370           p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9371
9372           if (p == (const PixelPacket *) NULL)
9373             break;
9374
9375           if (mng_info->IsPalette)
9376             {
9377               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9378                 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9379               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9380                   mng_info->write_png_depth &&
9381                   mng_info->write_png_depth != old_bit_depth)
9382                 {
9383                   /* Undo pixel scaling */
9384                   for (i=0; i < (ssize_t) image->columns; i++)
9385                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
9386                      >> (8-old_bit_depth));
9387                 }
9388             }
9389
9390           else
9391             {
9392               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9393                 quantum_info,RedQuantum,ping_pixels,&image->exception);
9394             }
9395
9396           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
9397             for (i=0; i < (ssize_t) image->columns; i++)
9398                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
9399                       255 : 0);
9400
9401           if (logging != MagickFalse && y == 0)
9402             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9403                 "    Writing row of pixels (1)");
9404
9405           png_write_row(ping,ping_pixels);
9406         }
9407         if (image->previous == (Image *) NULL)
9408           {
9409             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9410             if (status == MagickFalse)
9411               break;
9412           }
9413       }
9414     }
9415
9416   else   /* Not Palette, Bilevel, or Opaque Monochrome */
9417     {
9418       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9419          !mng_info->write_png32) &&
9420          (image_matte != MagickFalse ||
9421          (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
9422          (mng_info->IsPalette) && ping_have_color == MagickFalse)
9423         {
9424           register const PixelPacket
9425             *p;
9426
9427           for (pass=0; pass < num_passes; pass++)
9428           {
9429
9430           for (y=0; y < (ssize_t) image->rows; y++)
9431           {
9432             p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9433
9434             if (p == (const PixelPacket *) NULL)
9435               break;
9436
9437             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9438               {
9439                 if (mng_info->IsPalette)
9440                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9441                     quantum_info,GrayQuantum,ping_pixels,&image->exception);
9442
9443                 else
9444                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9445                     quantum_info,RedQuantum,ping_pixels,&image->exception);
9446
9447                 if (logging != MagickFalse && y == 0)
9448                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9449                        "    Writing GRAY PNG pixels (2)");
9450               }
9451
9452             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9453               {
9454                 if (logging != MagickFalse && y == 0)
9455                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9456                          "    Writing GRAY_ALPHA PNG pixels (2)");
9457
9458                 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9459                   quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
9460               }
9461
9462             if (logging != MagickFalse && y == 0)
9463               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9464                   "    Writing row of pixels (2)");
9465
9466             png_write_row(ping,ping_pixels);
9467           }
9468
9469           if (image->previous == (Image *) NULL)
9470             {
9471               status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9472               if (status == MagickFalse)
9473                 break;
9474             }
9475           }
9476         }
9477
9478       else
9479         {
9480           register const PixelPacket
9481             *p;
9482
9483           for (pass=0; pass < num_passes; pass++)
9484           {
9485             if ((image_depth > 8) || (mng_info->write_png24 ||
9486                 mng_info->write_png32 ||
9487                 (!mng_info->write_png8 && !mng_info->IsPalette)))
9488             {
9489               for (y=0; y < (ssize_t) image->rows; y++)
9490               {
9491                 p=GetVirtualPixels(image,0,y,image->columns,1,
9492                    &image->exception);
9493
9494                 if (p == (const PixelPacket *) NULL)
9495                   break;
9496
9497                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9498                   {
9499                     if (image->storage_class == DirectClass)
9500                       (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9501                         quantum_info,RedQuantum,ping_pixels,&image->exception);
9502
9503                     else
9504                       (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9505                         quantum_info,GrayQuantum,ping_pixels,&image->exception);
9506                   }
9507
9508                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9509                   {
9510                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9511                       quantum_info,GrayAlphaQuantum,ping_pixels,
9512                       &image->exception);
9513
9514                     if (logging != MagickFalse && y == 0)
9515                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9516                            "    Writing GRAY_ALPHA PNG pixels (3)");
9517                   }
9518
9519                 else if (image_matte != MagickFalse)
9520                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9521                     quantum_info,RGBAQuantum,ping_pixels,&image->exception);
9522
9523                 else
9524                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9525                     quantum_info,RGBQuantum,ping_pixels,&image->exception);
9526
9527                 if (logging != MagickFalse && y == 0)
9528                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9529                       "    Writing row of pixels (3)");
9530
9531                 png_write_row(ping,ping_pixels);
9532               }
9533             }
9534
9535           else
9536             /* not ((image_depth > 8) || (mng_info->write_png24 ||
9537                 mng_info->write_png32 ||
9538                 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9539             {
9540               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9541                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9542                 {
9543                   if (logging != MagickFalse)
9544                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9545                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
9546
9547                   quantum_info->depth=8;
9548                   image_depth=8;
9549                 }
9550
9551               for (y=0; y < (ssize_t) image->rows; y++)
9552               {
9553                 if (logging != MagickFalse && y == 0)
9554                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9555                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
9556
9557                 p=GetVirtualPixels(image,0,y,image->columns,1,
9558                    &image->exception);
9559
9560                 if (p == (const PixelPacket *) NULL)
9561                   break;
9562
9563                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9564                   {
9565                     quantum_info->depth=image->depth;
9566
9567                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9568                        quantum_info,GrayQuantum,ping_pixels,&image->exception);
9569                   }
9570
9571                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9572                   {
9573                     if (logging != MagickFalse && y == 0)
9574                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9575                            "  Writing GRAY_ALPHA PNG pixels (4)");
9576
9577                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9578                          quantum_info,GrayAlphaQuantum,ping_pixels,
9579                          &image->exception);
9580                   }
9581
9582                 else
9583                   {
9584                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9585                       quantum_info,IndexQuantum,ping_pixels,&image->exception);
9586
9587                     if (logging != MagickFalse && y <= 2)
9588                     {
9589                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9590                           "  Writing row of non-gray pixels (4)");
9591
9592                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9593                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
9594                           (int)ping_pixels[0],(int)ping_pixels[1]);
9595                     }
9596                   }
9597                 png_write_row(ping,ping_pixels);
9598               }
9599             }
9600
9601             if (image->previous == (Image *) NULL)
9602               {
9603                 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9604                 if (status == MagickFalse)
9605                   break;
9606               }
9607           }
9608         }
9609     }
9610
9611   if (quantum_info != (QuantumInfo *) NULL)
9612     quantum_info=DestroyQuantumInfo(quantum_info);
9613
9614   if (logging != MagickFalse)
9615     {
9616       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9617         "  Wrote PNG image data");
9618
9619       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9620         "    Width: %.20g",(double) ping_width);
9621
9622       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9623         "    Height: %.20g",(double) ping_height);
9624
9625       if (mng_info->write_png_depth)
9626         {
9627           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9628             "    Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9629         }
9630
9631       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9632         "    PNG bit-depth written: %d",ping_bit_depth);
9633
9634       if (mng_info->write_png_colortype)
9635         {
9636           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9637             "    Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9638         }
9639
9640       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9641         "    PNG color-type written: %d",ping_color_type);
9642
9643       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9644         "    PNG Interlace method: %d",ping_interlace_method);
9645     }
9646   /*
9647     Generate text chunks.
9648   */
9649   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
9650   {
9651     ResetImagePropertyIterator(image);
9652     property=GetNextImageProperty(image);
9653     while (property != (const char *) NULL)
9654     {
9655       png_textp
9656         text;
9657
9658       value=GetImageProperty(image,property);
9659       if (ping_exclude_pHYs != MagickFalse       ||
9660           LocaleCompare(property,"density") != 0 ||
9661           LocaleCompare(property,"units") != 0)
9662         {
9663         if (value != (const char *) NULL)
9664           {
9665             text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9666             text[0].key=(char *) property;
9667             text[0].text=(char *) value;
9668             text[0].text_length=strlen(value);
9669
9670             if (ping_exclude_tEXt != MagickFalse)
9671                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9672
9673             else if (ping_exclude_zTXt != MagickFalse)
9674                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9675
9676             else
9677             {
9678                text[0].compression=image_info->compression == NoCompression ||
9679                  (image_info->compression == UndefinedCompression &&
9680                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9681                  PNG_TEXT_COMPRESSION_zTXt ;
9682             }
9683
9684             if (logging != MagickFalse)
9685               {
9686                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9687                   "  Setting up text chunk");
9688
9689                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9690                   "    keyword: %s",text[0].key);
9691               }
9692
9693             png_set_text(ping,ping_info,text,1);
9694             png_free(ping,text);
9695           }
9696         }
9697       property=GetNextImageProperty(image);
9698     }
9699   }
9700
9701   /* write any PNG-chunk-e profiles */
9702   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
9703
9704   if (logging != MagickFalse)
9705     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9706       "  Writing PNG end info");
9707
9708   png_write_end(ping,ping_info);
9709
9710   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9711     {
9712       if (mng_info->page.x || mng_info->page.y ||
9713           (ping_width != mng_info->page.width) ||
9714           (ping_height != mng_info->page.height))
9715         {
9716           unsigned char
9717             chunk[32];
9718
9719           /*
9720             Write FRAM 4 with clipping boundaries followed by FRAM 1.
9721           */
9722           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
9723           PNGType(chunk,mng_FRAM);
9724           LogPNGChunk(logging,mng_FRAM,27L);
9725           chunk[4]=4;
9726           chunk[5]=0;  /* frame name separator (no name) */
9727           chunk[6]=1;  /* flag for changing delay, for next frame only */
9728           chunk[7]=0;  /* flag for changing frame timeout */
9729           chunk[8]=1;  /* flag for changing frame clipping for next frame */
9730           chunk[9]=0;  /* flag for changing frame sync_id */
9731           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9732           chunk[14]=0; /* clipping boundaries delta type */
9733           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9734           PNGLong(chunk+19,
9735              (png_uint_32) (mng_info->page.x + ping_width));
9736           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9737           PNGLong(chunk+27,
9738              (png_uint_32) (mng_info->page.y + ping_height));
9739           (void) WriteBlob(image,31,chunk);
9740           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9741           mng_info->old_framing_mode=4;
9742           mng_info->framing_mode=1;
9743         }
9744
9745       else
9746         mng_info->framing_mode=3;
9747     }
9748   if (mng_info->write_mng && !mng_info->need_fram &&
9749       ((int) image->dispose == 3))
9750      (void) ThrowMagickException(&image->exception,GetMagickModule(),
9751        CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
9752        "`%s'",image->filename);
9753
9754   /*
9755     Free PNG resources.
9756   */
9757
9758   png_destroy_write_struct(&ping,&ping_info);
9759
9760   ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9761
9762 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9763   UnlockSemaphoreInfo(ping_semaphore);
9764 #endif
9765
9766   if (ping_have_blob != MagickFalse)
9767      (void) CloseBlob(image);
9768
9769   image_info=DestroyImageInfo(image_info);
9770   image=DestroyImage(image);
9771
9772   /* Store bit depth actually written */
9773   s[0]=(char) ping_bit_depth;
9774   s[1]='\0';
9775
9776   (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9777
9778   if (logging != MagickFalse)
9779     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9780       "  exit WriteOnePNGImage()");
9781
9782   return(MagickTrue);
9783 /*  End write one PNG image */
9784 }
9785
9786 /*
9787 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9788 %                                                                             %
9789 %                                                                             %
9790 %                                                                             %
9791 %   W r i t e P N G I m a g e                                                 %
9792 %                                                                             %
9793 %                                                                             %
9794 %                                                                             %
9795 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9796 %
9797 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
9798 %  Multiple-image Network Graphics (MNG) image file.
9799 %
9800 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
9801 %
9802 %  The format of the WritePNGImage method is:
9803 %
9804 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9805 %
9806 %  A description of each parameter follows:
9807 %
9808 %    o image_info: the image info.
9809 %
9810 %    o image:  The image.
9811 %
9812 %  Returns MagickTrue on success, MagickFalse on failure.
9813 %
9814 %  Communicating with the PNG encoder:
9815 %
9816 %  While the datastream written is always in PNG format and normally would
9817 %  be given the "png" file extension, this method also writes the following
9818 %  pseudo-formats which are subsets of PNG:
9819 %
9820 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
9821 %               a depth greater than 8, the depth is reduced. If transparency
9822 %               is present, the tRNS chunk must only have values 0 and 255
9823 %               (i.e., transparency is binary: fully opaque or fully
9824 %               transparent).  If other values are present they will be
9825 %               50%-thresholded to binary transparency.  If more than 256
9826 %               colors are present, they will be quantized to the 4-4-4-1,
9827 %               3-3-3-1, or  3-3-2-1 palette.
9828 %
9829 %               If you want better quantization or dithering of the colors
9830 %               or alpha than that, you need to do it before calling the
9831 %               PNG encoder. The pixels contain 8-bit indices even if
9832 %               they could be represented with 1, 2, or 4 bits.  Grayscale
9833 %               images will be written as indexed PNG files even though the
9834 %               PNG grayscale type might be slightly more efficient.  Please
9835 %               note that writing to the PNG8 format may result in loss
9836 %               of color and alpha data.
9837 %
9838 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
9839 %               chunk can be present to convey binary transparency by naming
9840 %               one of the colors as transparent.  The only loss incurred
9841 %               is reduction of sample depth to 8.  If the image has more
9842 %               than one transparent color, has semitransparent pixels, or
9843 %               has an opaque pixel with the same RGB components as the
9844 %               transparent color, an image is not written.
9845 %
9846 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
9847 %               transparency is permitted, i.e., the alpha sample for
9848 %               each pixel can have any value from 0 to 255. The alpha
9849 %               channel is present even if the image is fully opaque.
9850 %               The only loss in data is the reduction of the sample depth
9851 %               to 8.
9852 %
9853 %    o -define: For more precise control of the PNG output, you can use the
9854 %               Image options "png:bit-depth" and "png:color-type".  These
9855 %               can be set from the commandline with "-define" and also
9856 %               from the application programming interfaces.  The options
9857 %               are case-independent and are converted to lowercase before
9858 %               being passed to this encoder.
9859 %
9860 %               png:color-type can be 0, 2, 3, 4, or 6.
9861 %
9862 %               When png:color-type is 0 (Grayscale), png:bit-depth can
9863 %               be 1, 2, 4, 8, or 16.
9864 %
9865 %               When png:color-type is 2 (RGB), png:bit-depth can
9866 %               be 8 or 16.
9867 %
9868 %               When png:color-type is 3 (Indexed), png:bit-depth can
9869 %               be 1, 2, 4, or 8.  This refers to the number of bits
9870 %               used to store the index.  The color samples always have
9871 %               bit-depth 8 in indexed PNG files.
9872 %
9873 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
9874 %               png:bit-depth can be 8 or 16.
9875 %
9876 %  If the image cannot be written without loss with the requested bit-depth
9877 %  and color-type, a PNG file will not be written, and the encoder will
9878 %  return MagickFalse.
9879 %
9880 %  Since image encoders should not be responsible for the "heavy lifting",
9881 %  the user should make sure that ImageMagick has already reduced the
9882 %  image depth and number of colors and limit transparency to binary
9883 %  transparency prior to attempting to write the image with depth, color,
9884 %   or transparency limitations.
9885 %
9886 %  TODO: Enforce the previous paragraph.
9887 %
9888 %  Note that another definition, "png:bit-depth-written" exists, but it
9889 %  is not intended for external use.  It is only used internally by the
9890 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
9891 %
9892 %  It is possible to request that the PNG encoder write previously-formatted
9893 %  ancillary chunks in the output PNG file, using the "-profile" commandline
9894 %  option as shown below or by setting the profile via a programming
9895 %  interface:
9896 %
9897 %     -profile PNG-chunk-x:<file>
9898 %
9899 %  where x is a location flag and <file> is a file containing the chunk
9900 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
9901 %  This encoder will compute the chunk length and CRC, so those must not
9902 %  be included in the file.
9903 %
9904 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
9905 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
9906 %  of the same type, then add a short unique string after the "x" to prevent
9907 %  subsequent profiles from overwriting the preceding ones, e.g.,
9908 %
9909 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
9910 %
9911 %  As of version 6.6.6 the following optimizations are always done:
9912 %
9913 %   o  32-bit depth is reduced to 16.
9914 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
9915 %      high byte and low byte are identical.
9916 %   o  Palette is sorted to remove unused entries and to put a
9917 %      transparent color first, if BUILD_PNG_PALETTE is defined.
9918 %   o  Opaque matte channel is removed when writing an indexed PNG.
9919 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
9920 %      this can be done without loss and a larger bit depth N was not
9921 %      requested via the "-define PNG:bit-depth=N" option.
9922 %   o  If matte channel is present but only one transparent color is
9923 %      present, RGB+tRNS is written instead of RGBA
9924 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
9925 %      was requested when converting an opaque image).
9926 %
9927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9928 */
9929 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
9930   Image *image)
9931 {
9932   MagickBooleanType
9933     excluding,
9934     logging,
9935     have_mng_structure,
9936     status;
9937
9938   MngInfo
9939     *mng_info;
9940
9941   const char
9942     *value;
9943
9944   int
9945     i,
9946     source;
9947
9948   /*
9949     Open image file.
9950   */
9951   assert(image_info != (const ImageInfo *) NULL);
9952   assert(image_info->signature == MagickSignature);
9953   assert(image != (Image *) NULL);
9954   assert(image->signature == MagickSignature);
9955   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
9956   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
9957   /*
9958     Allocate a MngInfo structure.
9959   */
9960   have_mng_structure=MagickFalse;
9961   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
9962
9963   if (mng_info == (MngInfo *) NULL)
9964     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9965
9966   /*
9967     Initialize members of the MngInfo structure.
9968   */
9969   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
9970   mng_info->image=image;
9971   mng_info->equal_backgrounds=MagickTrue;
9972   have_mng_structure=MagickTrue;
9973
9974   /* See if user has requested a specific PNG subformat */
9975
9976   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
9977   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
9978   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
9979
9980   if (mng_info->write_png8)
9981     {
9982       mng_info->write_png_colortype = /* 3 */ 4;
9983       mng_info->write_png_depth = 8;
9984       image->depth = 8;
9985     }
9986
9987   if (mng_info->write_png24)
9988     {
9989       mng_info->write_png_colortype = /* 2 */ 3;
9990       mng_info->write_png_depth = 8;
9991       image->depth = 8;
9992
9993       if (image->matte == MagickTrue)
9994         (void) SetImageType(image,TrueColorMatteType);
9995
9996       else
9997         (void) SetImageType(image,TrueColorType);
9998
9999       (void) SyncImage(image);
10000     }
10001
10002   if (mng_info->write_png32)
10003     {
10004       mng_info->write_png_colortype = /* 6 */  7;
10005       mng_info->write_png_depth = 8;
10006       image->depth = 8;
10007
10008       if (image->matte == MagickTrue)
10009         (void) SetImageType(image,TrueColorMatteType);
10010
10011       else
10012         (void) SetImageType(image,TrueColorType);
10013
10014       (void) SyncImage(image);
10015     }
10016
10017   value=GetImageOption(image_info,"png:bit-depth");
10018
10019   if (value != (char *) NULL)
10020     {
10021       if (LocaleCompare(value,"1") == 0)
10022         mng_info->write_png_depth = 1;
10023
10024       else if (LocaleCompare(value,"2") == 0)
10025         mng_info->write_png_depth = 2;
10026
10027       else if (LocaleCompare(value,"4") == 0)
10028         mng_info->write_png_depth = 4;
10029
10030       else if (LocaleCompare(value,"8") == 0)
10031         mng_info->write_png_depth = 8;
10032
10033       else if (LocaleCompare(value,"16") == 0)
10034         mng_info->write_png_depth = 16;
10035
10036       else
10037         (void) ThrowMagickException(&image->exception,
10038              GetMagickModule(),CoderWarning,
10039              "ignoring invalid defined png:bit-depth",
10040              "=%s",value);
10041
10042       if (logging != MagickFalse)
10043         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10044           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
10045     }
10046
10047   value=GetImageOption(image_info,"png:color-type");
10048
10049   if (value != (char *) NULL)
10050     {
10051       /* We must store colortype+1 because 0 is a valid colortype */
10052       if (LocaleCompare(value,"0") == 0)
10053         mng_info->write_png_colortype = 1;
10054
10055       else if (LocaleCompare(value,"2") == 0)
10056         mng_info->write_png_colortype = 3;
10057
10058       else if (LocaleCompare(value,"3") == 0)
10059         mng_info->write_png_colortype = 4;
10060
10061       else if (LocaleCompare(value,"4") == 0)
10062         mng_info->write_png_colortype = 5;
10063
10064       else if (LocaleCompare(value,"6") == 0)
10065         mng_info->write_png_colortype = 7;
10066
10067       else
10068         (void) ThrowMagickException(&image->exception,
10069              GetMagickModule(),CoderWarning,
10070              "ignoring invalid defined png:color-type",
10071              "=%s",value);
10072
10073       if (logging != MagickFalse)
10074         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10075           "  png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
10076     }
10077
10078   /* Check for chunks to be excluded:
10079    *
10080    * The default is to not exclude any known chunks except for any
10081    * listed in the "unused_chunks" array, above.
10082    *
10083    * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10084    * define (in the image properties or in the image artifacts)
10085    * or via a mng_info member.  For convenience, in addition
10086    * to or instead of a comma-separated list of chunks, the
10087    * "exclude-chunk" string can be simply "all" or "none".
10088    *
10089    * The exclude-chunk define takes priority over the mng_info.
10090    *
10091    * A "PNG:include-chunk" define takes  priority over both the
10092    * mng_info and the "PNG:exclude-chunk" define.  Like the
10093    * "exclude-chunk" string, it can define "all" or "none" as
10094    * well as a comma-separated list.  Chunks that are unknown to
10095    * ImageMagick are always excluded, regardless of their "copy-safe"
10096    * status according to the PNG specification, and even if they
10097    * appear in the "include-chunk" list.
10098    *
10099    * Finally, all chunks listed in the "unused_chunks" array are
10100    * automatically excluded, regardless of the other instructions
10101    * or lack thereof.
10102    *
10103    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10104    * will not be written and the gAMA chunk will only be written if it
10105    * is not between .45 and .46, or approximately (1.0/2.2).
10106    *
10107    * If you exclude tRNS and the image has transparency, the colortype
10108    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10109    *
10110    * The -strip option causes StripImage() to set the png:include-chunk
10111    * artifact to "none,gama".
10112    */
10113
10114   mng_info->ping_exclude_bKGD=MagickFalse;
10115   mng_info->ping_exclude_cHRM=MagickFalse;
10116   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10117   mng_info->ping_exclude_gAMA=MagickFalse;
10118   mng_info->ping_exclude_cHRM=MagickFalse;
10119   mng_info->ping_exclude_iCCP=MagickFalse;
10120   /* mng_info->ping_exclude_iTXt=MagickFalse; */
10121   mng_info->ping_exclude_oFFs=MagickFalse;
10122   mng_info->ping_exclude_pHYs=MagickFalse;
10123   mng_info->ping_exclude_sRGB=MagickFalse;
10124   mng_info->ping_exclude_tEXt=MagickFalse;
10125   mng_info->ping_exclude_tRNS=MagickFalse;
10126   mng_info->ping_exclude_vpAg=MagickFalse;
10127   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10128   mng_info->ping_exclude_zTXt=MagickFalse;
10129
10130   excluding=MagickFalse;
10131
10132   for (source=0; source<1; source++)
10133   {
10134     if (source==0)
10135       {
10136        value=GetImageArtifact(image,"png:exclude-chunk");
10137
10138        if (value == NULL)
10139          value=GetImageArtifact(image,"png:exclude-chunks");
10140       }
10141     else
10142       {
10143        value=GetImageOption(image_info,"png:exclude-chunk");
10144
10145        if (value == NULL)
10146          value=GetImageOption(image_info,"png:exclude-chunks");
10147       }
10148
10149     if (value != NULL)
10150     {
10151
10152     size_t
10153       last;
10154
10155     excluding=MagickTrue;
10156
10157     if (logging != MagickFalse)
10158       {
10159         if (source == 0)
10160            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10161               "  png:exclude-chunk=%s found in image artifacts.\n", value);
10162         else
10163            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10164               "  png:exclude-chunk=%s found in image properties.\n", value);
10165       }
10166
10167     last=strlen(value);
10168
10169     for (i=0; i<(int) last; i+=5)
10170     {
10171
10172       if (LocaleNCompare(value+i,"all",3) == 0)
10173       {
10174         mng_info->ping_exclude_bKGD=MagickTrue;
10175         mng_info->ping_exclude_cHRM=MagickTrue;
10176         mng_info->ping_exclude_EXIF=MagickTrue;
10177         mng_info->ping_exclude_gAMA=MagickTrue;
10178         mng_info->ping_exclude_iCCP=MagickTrue;
10179         /* mng_info->ping_exclude_iTXt=MagickTrue; */
10180         mng_info->ping_exclude_oFFs=MagickTrue;
10181         mng_info->ping_exclude_pHYs=MagickTrue;
10182         mng_info->ping_exclude_sRGB=MagickTrue;
10183         mng_info->ping_exclude_tEXt=MagickTrue;
10184         mng_info->ping_exclude_tRNS=MagickTrue;
10185         mng_info->ping_exclude_vpAg=MagickTrue;
10186         mng_info->ping_exclude_zCCP=MagickTrue;
10187         mng_info->ping_exclude_zTXt=MagickTrue;
10188         i--;
10189       }
10190
10191       if (LocaleNCompare(value+i,"none",4) == 0)
10192       {
10193         mng_info->ping_exclude_bKGD=MagickFalse;
10194         mng_info->ping_exclude_cHRM=MagickFalse;
10195         mng_info->ping_exclude_EXIF=MagickFalse;
10196         mng_info->ping_exclude_gAMA=MagickFalse;
10197         mng_info->ping_exclude_iCCP=MagickFalse;
10198         /* mng_info->ping_exclude_iTXt=MagickFalse; */
10199         mng_info->ping_exclude_oFFs=MagickFalse;
10200         mng_info->ping_exclude_pHYs=MagickFalse;
10201         mng_info->ping_exclude_sRGB=MagickFalse;
10202         mng_info->ping_exclude_tEXt=MagickFalse;
10203         mng_info->ping_exclude_tRNS=MagickFalse;
10204         mng_info->ping_exclude_vpAg=MagickFalse;
10205         mng_info->ping_exclude_zCCP=MagickFalse;
10206         mng_info->ping_exclude_zTXt=MagickFalse;
10207       }
10208
10209       if (LocaleNCompare(value+i,"bkgd",4) == 0)
10210         mng_info->ping_exclude_bKGD=MagickTrue;
10211
10212       if (LocaleNCompare(value+i,"chrm",4) == 0)
10213         mng_info->ping_exclude_cHRM=MagickTrue;
10214
10215       if (LocaleNCompare(value+i,"exif",4) == 0)
10216         mng_info->ping_exclude_EXIF=MagickTrue;
10217
10218       if (LocaleNCompare(value+i,"gama",4) == 0)
10219         mng_info->ping_exclude_gAMA=MagickTrue;
10220
10221       if (LocaleNCompare(value+i,"iccp",4) == 0)
10222         mng_info->ping_exclude_iCCP=MagickTrue;
10223
10224     /*
10225       if (LocaleNCompare(value+i,"itxt",4) == 0)
10226         mng_info->ping_exclude_iTXt=MagickTrue;
10227      */
10228
10229       if (LocaleNCompare(value+i,"gama",4) == 0)
10230         mng_info->ping_exclude_gAMA=MagickTrue;
10231
10232       if (LocaleNCompare(value+i,"offs",4) == 0)
10233         mng_info->ping_exclude_oFFs=MagickTrue;
10234
10235       if (LocaleNCompare(value+i,"phys",4) == 0)
10236         mng_info->ping_exclude_pHYs=MagickTrue;
10237
10238       if (LocaleNCompare(value+i,"srgb",4) == 0)
10239         mng_info->ping_exclude_sRGB=MagickTrue;
10240
10241       if (LocaleNCompare(value+i,"text",4) == 0)
10242         mng_info->ping_exclude_tEXt=MagickTrue;
10243
10244       if (LocaleNCompare(value+i,"trns",4) == 0)
10245         mng_info->ping_exclude_tRNS=MagickTrue;
10246
10247       if (LocaleNCompare(value+i,"vpag",4) == 0)
10248         mng_info->ping_exclude_vpAg=MagickTrue;
10249
10250       if (LocaleNCompare(value+i,"zccp",4) == 0)
10251         mng_info->ping_exclude_zCCP=MagickTrue;
10252
10253       if (LocaleNCompare(value+i,"ztxt",4) == 0)
10254         mng_info->ping_exclude_zTXt=MagickTrue;
10255
10256       }
10257     }
10258   }
10259
10260   for (source=0; source<1; source++)
10261   {
10262     if (source==0)
10263       {
10264        value=GetImageArtifact(image,"png:include-chunk");
10265
10266        if (value == NULL)
10267          value=GetImageArtifact(image,"png:include-chunks");
10268       }
10269     else
10270       {
10271        value=GetImageOption(image_info,"png:include-chunk");
10272
10273        if (value == NULL)
10274          value=GetImageOption(image_info,"png:include-chunks");
10275       }
10276
10277     if (value != NULL)
10278     {
10279     size_t
10280       last;
10281
10282     excluding=MagickTrue;
10283
10284     if (logging != MagickFalse)
10285       {
10286         if (source == 0)
10287            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10288               "  png:include-chunk=%s found in image artifacts.\n", value);
10289         else
10290            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10291               "  png:include-chunk=%s found in image properties.\n", value);
10292       }
10293
10294     last=strlen(value);
10295
10296     for (i=0; i<(int) last; i+=5)
10297       {
10298       if (LocaleNCompare(value+i,"all",3) == 0)
10299         {
10300           mng_info->ping_exclude_bKGD=MagickFalse;
10301           mng_info->ping_exclude_cHRM=MagickFalse;
10302           mng_info->ping_exclude_EXIF=MagickFalse;
10303           mng_info->ping_exclude_gAMA=MagickFalse;
10304           mng_info->ping_exclude_iCCP=MagickFalse;
10305           /* mng_info->ping_exclude_iTXt=MagickFalse; */
10306           mng_info->ping_exclude_oFFs=MagickFalse;
10307           mng_info->ping_exclude_pHYs=MagickFalse;
10308           mng_info->ping_exclude_sRGB=MagickFalse;
10309           mng_info->ping_exclude_tEXt=MagickFalse;
10310           mng_info->ping_exclude_tRNS=MagickFalse;
10311           mng_info->ping_exclude_vpAg=MagickFalse;
10312           mng_info->ping_exclude_zCCP=MagickFalse;
10313           mng_info->ping_exclude_zTXt=MagickFalse;
10314           i--;
10315         }
10316
10317       if (LocaleNCompare(value+i,"none",4) == 0)
10318         {
10319           mng_info->ping_exclude_bKGD=MagickTrue;
10320           mng_info->ping_exclude_cHRM=MagickTrue;
10321           mng_info->ping_exclude_EXIF=MagickTrue;
10322           mng_info->ping_exclude_gAMA=MagickTrue;
10323           mng_info->ping_exclude_iCCP=MagickTrue;
10324           /* mng_info->ping_exclude_iTXt=MagickTrue; */
10325           mng_info->ping_exclude_oFFs=MagickTrue;
10326           mng_info->ping_exclude_pHYs=MagickTrue;
10327           mng_info->ping_exclude_sRGB=MagickTrue;
10328           mng_info->ping_exclude_tEXt=MagickTrue;
10329           mng_info->ping_exclude_tRNS=MagickTrue;
10330           mng_info->ping_exclude_vpAg=MagickTrue;
10331           mng_info->ping_exclude_zCCP=MagickTrue;
10332           mng_info->ping_exclude_zTXt=MagickTrue;
10333         }
10334
10335       if (LocaleNCompare(value+i,"bkgd",4) == 0)
10336         mng_info->ping_exclude_bKGD=MagickFalse;
10337
10338       if (LocaleNCompare(value+i,"chrm",4) == 0)
10339         mng_info->ping_exclude_cHRM=MagickFalse;
10340
10341       if (LocaleNCompare(value+i,"exif",4) == 0)
10342         mng_info->ping_exclude_EXIF=MagickFalse;
10343
10344       if (LocaleNCompare(value+i,"gama",4) == 0)
10345         mng_info->ping_exclude_gAMA=MagickFalse;
10346
10347       if (LocaleNCompare(value+i,"iccp",4) == 0)
10348         mng_info->ping_exclude_iCCP=MagickFalse;
10349
10350     /*
10351       if (LocaleNCompare(value+i,"itxt",4) == 0)
10352         mng_info->ping_exclude_iTXt=MagickFalse;
10353      */
10354
10355       if (LocaleNCompare(value+i,"gama",4) == 0)
10356         mng_info->ping_exclude_gAMA=MagickFalse;
10357
10358       if (LocaleNCompare(value+i,"offs",4) == 0)
10359         mng_info->ping_exclude_oFFs=MagickFalse;
10360
10361       if (LocaleNCompare(value+i,"phys",4) == 0)
10362         mng_info->ping_exclude_pHYs=MagickFalse;
10363
10364       if (LocaleNCompare(value+i,"srgb",4) == 0)
10365         mng_info->ping_exclude_sRGB=MagickFalse;
10366
10367       if (LocaleNCompare(value+i,"text",4) == 0)
10368         mng_info->ping_exclude_tEXt=MagickFalse;
10369
10370       if (LocaleNCompare(value+i,"trns",4) == 0)
10371         mng_info->ping_exclude_tRNS=MagickFalse;
10372
10373       if (LocaleNCompare(value+i,"vpag",4) == 0)
10374         mng_info->ping_exclude_vpAg=MagickFalse;
10375
10376       if (LocaleNCompare(value+i,"zccp",4) == 0)
10377         mng_info->ping_exclude_zCCP=MagickFalse;
10378
10379       if (LocaleNCompare(value+i,"ztxt",4) == 0)
10380         mng_info->ping_exclude_zTXt=MagickFalse;
10381
10382       }
10383     }
10384   }
10385
10386   if (excluding != MagickFalse && logging != MagickFalse)
10387   {
10388     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10389       "  Chunks to be excluded from the output PNG:");
10390     if (mng_info->ping_exclude_bKGD != MagickFalse)
10391       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10392           "    bKGD");
10393     if (mng_info->ping_exclude_cHRM != MagickFalse)
10394       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10395           "    cHRM");
10396     if (mng_info->ping_exclude_EXIF != MagickFalse)
10397       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10398           "    EXIF");
10399     if (mng_info->ping_exclude_gAMA != MagickFalse)
10400       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10401           "    gAMA");
10402     if (mng_info->ping_exclude_iCCP != MagickFalse)
10403       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10404           "    iCCP");
10405 /*
10406     if (mng_info->ping_exclude_iTXt != MagickFalse)
10407       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10408           "    iTXt");
10409 */
10410     if (mng_info->ping_exclude_oFFs != MagickFalse)
10411       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10412           "    oFFs");
10413     if (mng_info->ping_exclude_pHYs != MagickFalse)
10414       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10415           "    pHYs");
10416     if (mng_info->ping_exclude_sRGB != MagickFalse)
10417       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10418           "    sRGB");
10419     if (mng_info->ping_exclude_tEXt != MagickFalse)
10420       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10421           "    tEXt");
10422     if (mng_info->ping_exclude_tRNS != MagickFalse)
10423       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10424           "    tRNS");
10425     if (mng_info->ping_exclude_vpAg != MagickFalse)
10426       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10427           "    vpAg");
10428     if (mng_info->ping_exclude_zCCP != MagickFalse)
10429       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10430           "    zCCP");
10431     if (mng_info->ping_exclude_zTXt != MagickFalse)
10432       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10433           "    zTXt");
10434   }
10435
10436   mng_info->need_blob = MagickTrue;
10437
10438   status=WriteOnePNGImage(mng_info,image_info,image);
10439
10440   MngInfoFreeStruct(mng_info,&have_mng_structure);
10441
10442   if (logging != MagickFalse)
10443     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
10444
10445   return(status);
10446 }
10447
10448 #if defined(JNG_SUPPORTED)
10449
10450 /* Write one JNG image */
10451 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10452    const ImageInfo *image_info,Image *image)
10453 {
10454   Image
10455     *jpeg_image;
10456
10457   ImageInfo
10458     *jpeg_image_info;
10459
10460   MagickBooleanType
10461     logging,
10462     status;
10463
10464   size_t
10465     length;
10466
10467   unsigned char
10468     *blob,
10469     chunk[80],
10470     *p;
10471
10472   unsigned int
10473     jng_alpha_compression_method,
10474     jng_alpha_sample_depth,
10475     jng_color_type,
10476     transparent;
10477
10478   size_t
10479     jng_quality;
10480
10481   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
10482     "  Enter WriteOneJNGImage()");
10483
10484   blob=(unsigned char *) NULL;
10485   jpeg_image=(Image *) NULL;
10486   jpeg_image_info=(ImageInfo *) NULL;
10487
10488   status=MagickTrue;
10489   transparent=image_info->type==GrayscaleMatteType ||
10490      image_info->type==TrueColorMatteType;
10491   jng_color_type=10;
10492   jng_alpha_sample_depth=0;
10493   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10494   jng_alpha_compression_method=0;
10495
10496   if (image->matte != MagickFalse)
10497     {
10498       /* if any pixels are transparent */
10499       transparent=MagickTrue;
10500       if (image_info->compression==JPEGCompression)
10501         jng_alpha_compression_method=8;
10502     }
10503
10504   if (transparent)
10505     {
10506       jng_color_type=14;
10507
10508       /* Create JPEG blob, image, and image_info */
10509       if (logging != MagickFalse)
10510         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10511           "  Creating jpeg_image_info for opacity.");
10512
10513       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10514
10515       if (jpeg_image_info == (ImageInfo *) NULL)
10516         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10517
10518       if (logging != MagickFalse)
10519         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10520           "  Creating jpeg_image.");
10521
10522       jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10523
10524       if (jpeg_image == (Image *) NULL)
10525         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10526
10527       (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10528       status=SeparateImageChannel(jpeg_image,OpacityChannel);
10529       status=NegateImage(jpeg_image,MagickFalse);
10530       jpeg_image->matte=MagickFalse;
10531
10532       if (jng_quality >= 1000)
10533         jpeg_image_info->quality=jng_quality/1000;
10534
10535       else
10536         jpeg_image_info->quality=jng_quality;
10537
10538       jpeg_image_info->type=GrayscaleType;
10539       (void) SetImageType(jpeg_image,GrayscaleType);
10540       (void) AcquireUniqueFilename(jpeg_image->filename);
10541       (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10542         "%s",jpeg_image->filename);
10543     }
10544
10545   /* To do: check bit depth of PNG alpha channel */
10546
10547   /* Check if image is grayscale. */
10548   if (image_info->type != TrueColorMatteType && image_info->type !=
10549     TrueColorType && ImageIsGray(image))
10550     jng_color_type-=2;
10551
10552   if (transparent)
10553     {
10554       if (jng_alpha_compression_method==0)
10555         {
10556           const char
10557             *value;
10558
10559           /* Encode opacity as a grayscale PNG blob */
10560           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10561             &image->exception);
10562           if (logging != MagickFalse)
10563             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10564               "  Creating PNG blob.");
10565           length=0;
10566
10567           (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10568           (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10569           jpeg_image_info->interlace=NoInterlace;
10570
10571           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10572             &image->exception);
10573
10574           /* Retrieve sample depth used */
10575           value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10576           if (value != (char *) NULL)
10577             jng_alpha_sample_depth= (unsigned int) value[0];
10578         }
10579       else
10580         {
10581           /* Encode opacity as a grayscale JPEG blob */
10582
10583           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10584             &image->exception);
10585
10586           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10587           (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10588           jpeg_image_info->interlace=NoInterlace;
10589           if (logging != MagickFalse)
10590             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10591               "  Creating blob.");
10592           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10593            &image->exception);
10594           jng_alpha_sample_depth=8;
10595
10596           if (logging != MagickFalse)
10597             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10598               "  Successfully read jpeg_image into a blob, length=%.20g.",
10599               (double) length);
10600
10601         }
10602       /* Destroy JPEG image and image_info */
10603       jpeg_image=DestroyImage(jpeg_image);
10604       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10605       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10606     }
10607
10608   /* Write JHDR chunk */
10609   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
10610   PNGType(chunk,mng_JHDR);
10611   LogPNGChunk(logging,mng_JHDR,16L);
10612   PNGLong(chunk+4,(png_uint_32) image->columns);
10613   PNGLong(chunk+8,(png_uint_32) image->rows);
10614   chunk[12]=jng_color_type;
10615   chunk[13]=8;  /* sample depth */
10616   chunk[14]=8; /*jng_image_compression_method */
10617   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10618   chunk[16]=jng_alpha_sample_depth;
10619   chunk[17]=jng_alpha_compression_method;
10620   chunk[18]=0; /*jng_alpha_filter_method */
10621   chunk[19]=0; /*jng_alpha_interlace_method */
10622   (void) WriteBlob(image,20,chunk);
10623   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10624   if (logging != MagickFalse)
10625     {
10626       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10627         "    JNG width:%15lu",(unsigned long) image->columns);
10628
10629       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10630         "    JNG height:%14lu",(unsigned long) image->rows);
10631
10632       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10633         "    JNG color type:%10d",jng_color_type);
10634
10635       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10636         "    JNG sample depth:%8d",8);
10637
10638       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10639         "    JNG compression:%9d",8);
10640
10641       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10642         "    JNG interlace:%11d",0);
10643
10644       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10645         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
10646
10647       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10648         "    JNG alpha compression:%3d",jng_alpha_compression_method);
10649
10650       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10651         "    JNG alpha filter:%8d",0);
10652
10653       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10654         "    JNG alpha interlace:%5d",0);
10655     }
10656
10657   /* Write any JNG-chunk-b profiles */
10658   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
10659
10660   /*
10661      Write leading ancillary chunks
10662   */
10663
10664   if (transparent)
10665   {
10666     /*
10667       Write JNG bKGD chunk
10668     */
10669
10670     unsigned char
10671       blue,
10672       green,
10673       red;
10674
10675     ssize_t
10676       num_bytes;
10677
10678     if (jng_color_type == 8 || jng_color_type == 12)
10679       num_bytes=6L;
10680     else
10681       num_bytes=10L;
10682     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
10683     PNGType(chunk,mng_bKGD);
10684     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
10685     red=ScaleQuantumToChar(image->background_color.red);
10686     green=ScaleQuantumToChar(image->background_color.green);
10687     blue=ScaleQuantumToChar(image->background_color.blue);
10688     *(chunk+4)=0;
10689     *(chunk+5)=red;
10690     *(chunk+6)=0;
10691     *(chunk+7)=green;
10692     *(chunk+8)=0;
10693     *(chunk+9)=blue;
10694     (void) WriteBlob(image,(size_t) num_bytes,chunk);
10695     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10696   }
10697
10698   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10699     {
10700       /*
10701         Write JNG sRGB chunk
10702       */
10703       (void) WriteBlobMSBULong(image,1L);
10704       PNGType(chunk,mng_sRGB);
10705       LogPNGChunk(logging,mng_sRGB,1L);
10706
10707       if (image->rendering_intent != UndefinedIntent)
10708         chunk[4]=(unsigned char)
10709           Magick_RenderingIntent_to_PNG_RenderingIntent(
10710           (image->rendering_intent));
10711
10712       else
10713         chunk[4]=(unsigned char)
10714           Magick_RenderingIntent_to_PNG_RenderingIntent(
10715           (PerceptualIntent));
10716
10717       (void) WriteBlob(image,5,chunk);
10718       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10719     }
10720   else
10721     {
10722       if (image->gamma != 0.0)
10723         {
10724           /*
10725              Write JNG gAMA chunk
10726           */
10727           (void) WriteBlobMSBULong(image,4L);
10728           PNGType(chunk,mng_gAMA);
10729           LogPNGChunk(logging,mng_gAMA,4L);
10730           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10731           (void) WriteBlob(image,8,chunk);
10732           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10733         }
10734
10735       if ((mng_info->equal_chrms == MagickFalse) &&
10736           (image->chromaticity.red_primary.x != 0.0))
10737         {
10738           PrimaryInfo
10739             primary;
10740
10741           /*
10742              Write JNG cHRM chunk
10743           */
10744           (void) WriteBlobMSBULong(image,32L);
10745           PNGType(chunk,mng_cHRM);
10746           LogPNGChunk(logging,mng_cHRM,32L);
10747           primary=image->chromaticity.white_point;
10748           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10749           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
10750           primary=image->chromaticity.red_primary;
10751           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10752           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
10753           primary=image->chromaticity.green_primary;
10754           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10755           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
10756           primary=image->chromaticity.blue_primary;
10757           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10758           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
10759           (void) WriteBlob(image,36,chunk);
10760           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10761         }
10762     }
10763
10764   if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10765     {
10766       /*
10767          Write JNG pHYs chunk
10768       */
10769       (void) WriteBlobMSBULong(image,9L);
10770       PNGType(chunk,mng_pHYs);
10771       LogPNGChunk(logging,mng_pHYs,9L);
10772       if (image->units == PixelsPerInchResolution)
10773         {
10774           PNGLong(chunk+4,(png_uint_32)
10775             (image->x_resolution*100.0/2.54+0.5));
10776
10777           PNGLong(chunk+8,(png_uint_32)
10778             (image->y_resolution*100.0/2.54+0.5));
10779
10780           chunk[12]=1;
10781         }
10782
10783       else
10784         {
10785           if (image->units == PixelsPerCentimeterResolution)
10786             {
10787               PNGLong(chunk+4,(png_uint_32)
10788                 (image->x_resolution*100.0+0.5));
10789
10790               PNGLong(chunk+8,(png_uint_32)
10791                 (image->y_resolution*100.0+0.5));
10792
10793               chunk[12]=1;
10794             }
10795
10796           else
10797             {
10798               PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10799               PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
10800               chunk[12]=0;
10801             }
10802         }
10803       (void) WriteBlob(image,13,chunk);
10804       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10805     }
10806
10807   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10808     {
10809       /*
10810          Write JNG oFFs chunk
10811       */
10812       (void) WriteBlobMSBULong(image,9L);
10813       PNGType(chunk,mng_oFFs);
10814       LogPNGChunk(logging,mng_oFFs,9L);
10815       PNGsLong(chunk+4,(ssize_t) (image->page.x));
10816       PNGsLong(chunk+8,(ssize_t) (image->page.y));
10817       chunk[12]=0;
10818       (void) WriteBlob(image,13,chunk);
10819       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10820     }
10821   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10822     {
10823        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
10824        PNGType(chunk,mng_vpAg);
10825        LogPNGChunk(logging,mng_vpAg,9L);
10826        PNGLong(chunk+4,(png_uint_32) image->page.width);
10827        PNGLong(chunk+8,(png_uint_32) image->page.height);
10828        chunk[12]=0;   /* unit = pixels */
10829        (void) WriteBlob(image,13,chunk);
10830        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10831     }
10832
10833
10834   if (transparent)
10835     {
10836       if (jng_alpha_compression_method==0)
10837         {
10838           register ssize_t
10839             i;
10840
10841           ssize_t
10842             len;
10843
10844           /* Write IDAT chunk header */
10845           if (logging != MagickFalse)
10846             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10847               "  Write IDAT chunks from blob, length=%.20g.",(double)
10848               length);
10849
10850           /* Copy IDAT chunks */
10851           len=0;
10852           p=blob+8;
10853           for (i=8; i<(ssize_t) length; i+=len+12)
10854           {
10855             len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
10856             p+=4;
10857
10858             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
10859               {
10860                 /* Found an IDAT chunk. */
10861                 (void) WriteBlobMSBULong(image,(size_t) len);
10862                 LogPNGChunk(logging,mng_IDAT,(size_t) len);
10863                 (void) WriteBlob(image,(size_t) len+4,p);
10864                 (void) WriteBlobMSBULong(image,
10865                     crc32(0,p,(uInt) len+4));
10866               }
10867
10868             else
10869               {
10870                 if (logging != MagickFalse)
10871                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10872                     "    Skipping %c%c%c%c chunk, length=%.20g.",
10873                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
10874               }
10875             p+=(8+len);
10876           }
10877         }
10878       else
10879         {
10880           /* Write JDAA chunk header */
10881           if (logging != MagickFalse)
10882             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10883               "  Write JDAA chunk, length=%.20g.",(double) length);
10884           (void) WriteBlobMSBULong(image,(size_t) length);
10885           PNGType(chunk,mng_JDAA);
10886           LogPNGChunk(logging,mng_JDAA,length);
10887           /* Write JDAT chunk(s) data */
10888           (void) WriteBlob(image,4,chunk);
10889           (void) WriteBlob(image,length,blob);
10890           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
10891              (uInt) length));
10892         }
10893       blob=(unsigned char *) RelinquishMagickMemory(blob);
10894     }
10895
10896   /* Encode image as a JPEG blob */
10897   if (logging != MagickFalse)
10898     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10899       "  Creating jpeg_image_info.");
10900   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10901   if (jpeg_image_info == (ImageInfo *) NULL)
10902     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10903
10904   if (logging != MagickFalse)
10905     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10906       "  Creating jpeg_image.");
10907
10908   jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10909   if (jpeg_image == (Image *) NULL)
10910     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10911   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10912
10913   (void) AcquireUniqueFilename(jpeg_image->filename);
10914   (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
10915     jpeg_image->filename);
10916
10917   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10918     &image->exception);
10919
10920   if (logging != MagickFalse)
10921     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10922       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
10923       (double) jpeg_image->rows);
10924
10925   if (jng_color_type == 8 || jng_color_type == 12)
10926     jpeg_image_info->type=GrayscaleType;
10927
10928   jpeg_image_info->quality=jng_quality % 1000;
10929   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10930   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10931
10932   if (logging != MagickFalse)
10933     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10934       "  Creating blob.");
10935
10936   blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
10937
10938   if (logging != MagickFalse)
10939     {
10940       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10941         "  Successfully read jpeg_image into a blob, length=%.20g.",
10942         (double) length);
10943
10944       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10945         "  Write JDAT chunk, length=%.20g.",(double) length);
10946     }
10947
10948   /* Write JDAT chunk(s) */
10949   (void) WriteBlobMSBULong(image,(size_t) length);
10950   PNGType(chunk,mng_JDAT);
10951   LogPNGChunk(logging,mng_JDAT,length);
10952   (void) WriteBlob(image,4,chunk);
10953   (void) WriteBlob(image,length,blob);
10954   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
10955
10956   jpeg_image=DestroyImage(jpeg_image);
10957   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10958   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10959   blob=(unsigned char *) RelinquishMagickMemory(blob);
10960
10961   /* Write any JNG-chunk-e profiles */
10962   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
10963
10964   /* Write IEND chunk */
10965   (void) WriteBlobMSBULong(image,0L);
10966   PNGType(chunk,mng_IEND);
10967   LogPNGChunk(logging,mng_IEND,0);
10968   (void) WriteBlob(image,4,chunk);
10969   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
10970
10971   if (logging != MagickFalse)
10972     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10973       "  exit WriteOneJNGImage()");
10974
10975   return(status);
10976 }
10977
10978
10979 /*
10980 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10981 %                                                                             %
10982 %                                                                             %
10983 %                                                                             %
10984 %   W r i t e J N G I m a g e                                                 %
10985 %                                                                             %
10986 %                                                                             %
10987 %                                                                             %
10988 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10989 %
10990 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
10991 %
10992 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
10993 %
10994 %  The format of the WriteJNGImage method is:
10995 %
10996 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10997 %
10998 %  A description of each parameter follows:
10999 %
11000 %    o image_info: the image info.
11001 %
11002 %    o image:  The image.
11003 %
11004 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11005 */
11006 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11007 {
11008   MagickBooleanType
11009     have_mng_structure,
11010     logging,
11011     status;
11012
11013   MngInfo
11014     *mng_info;
11015
11016   /*
11017     Open image file.
11018   */
11019   assert(image_info != (const ImageInfo *) NULL);
11020   assert(image_info->signature == MagickSignature);
11021   assert(image != (Image *) NULL);
11022   assert(image->signature == MagickSignature);
11023   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11024   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
11025   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11026   if (status == MagickFalse)
11027     return(status);
11028
11029   /*
11030     Allocate a MngInfo structure.
11031   */
11032   have_mng_structure=MagickFalse;
11033   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11034   if (mng_info == (MngInfo *) NULL)
11035     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11036   /*
11037     Initialize members of the MngInfo structure.
11038   */
11039   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11040   mng_info->image=image;
11041   have_mng_structure=MagickTrue;
11042
11043   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11044
11045   status=WriteOneJNGImage(mng_info,image_info,image);
11046   (void) CloseBlob(image);
11047
11048   (void) CatchImageException(image);
11049   MngInfoFreeStruct(mng_info,&have_mng_structure);
11050   if (logging != MagickFalse)
11051     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11052   return(status);
11053 }
11054 #endif
11055
11056
11057
11058 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11059 {
11060   const char
11061     *option;
11062
11063   Image
11064     *next_image;
11065
11066   MagickBooleanType
11067     have_mng_structure,
11068     status;
11069
11070   volatile MagickBooleanType
11071     logging;
11072
11073   MngInfo
11074     *mng_info;
11075
11076   int
11077     image_count,
11078     need_iterations,
11079     need_matte;
11080
11081   volatile int
11082 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11083     defined(PNG_MNG_FEATURES_SUPPORTED)
11084     need_local_plte,
11085 #endif
11086     all_images_are_gray,
11087     need_defi,
11088     use_global_plte;
11089
11090   register ssize_t
11091     i;
11092
11093   unsigned char
11094     chunk[800];
11095
11096   volatile unsigned int
11097     write_jng,
11098     write_mng;
11099
11100   volatile size_t
11101     scene;
11102
11103   size_t
11104     final_delay=0,
11105     initial_delay;
11106
11107 #if (PNG_LIBPNG_VER < 10200)
11108     if (image_info->verbose)
11109       printf("Your PNG library (libpng-%s) is rather old.\n",
11110          PNG_LIBPNG_VER_STRING);
11111 #endif
11112
11113   /*
11114     Open image file.
11115   */
11116   assert(image_info != (const ImageInfo *) NULL);
11117   assert(image_info->signature == MagickSignature);
11118   assert(image != (Image *) NULL);
11119   assert(image->signature == MagickSignature);
11120   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11121   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
11122   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11123   if (status == MagickFalse)
11124     return(status);
11125
11126   /*
11127     Allocate a MngInfo structure.
11128   */
11129   have_mng_structure=MagickFalse;
11130   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11131   if (mng_info == (MngInfo *) NULL)
11132     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11133   /*
11134     Initialize members of the MngInfo structure.
11135   */
11136   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11137   mng_info->image=image;
11138   have_mng_structure=MagickTrue;
11139   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11140
11141   /*
11142    * See if user has requested a specific PNG subformat to be used
11143    * for all of the PNGs in the MNG being written, e.g.,
11144    *
11145    *    convert *.png png8:animation.mng
11146    *
11147    * To do: check -define png:bit_depth and png:color_type as well,
11148    * or perhaps use mng:bit_depth and mng:color_type instead for
11149    * global settings.
11150    */
11151
11152   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11153   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11154   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11155
11156   write_jng=MagickFalse;
11157   if (image_info->compression == JPEGCompression)
11158     write_jng=MagickTrue;
11159
11160   mng_info->adjoin=image_info->adjoin &&
11161     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11162
11163   if (logging != MagickFalse)
11164     {
11165       /* Log some info about the input */
11166       Image
11167         *p;
11168
11169       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11170         "  Checking input image(s)");
11171
11172       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11173         "    Image_info depth: %.20g",(double) image_info->depth);
11174
11175       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11176         "    Type: %d",image_info->type);
11177
11178       scene=0;
11179       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11180       {
11181         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11182           "    Scene: %.20g",(double) scene++);
11183
11184         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11185           "      Image depth: %.20g",(double) p->depth);
11186
11187         if (p->matte)
11188           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11189             "      Matte: True");
11190
11191         else
11192           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11193             "      Matte: False");
11194
11195         if (p->storage_class == PseudoClass)
11196           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11197             "      Storage class: PseudoClass");
11198
11199         else
11200           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11201             "      Storage class: DirectClass");
11202
11203         if (p->colors)
11204           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11205             "      Number of colors: %.20g",(double) p->colors);
11206
11207         else
11208           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11209             "      Number of colors: unspecified");
11210
11211         if (mng_info->adjoin == MagickFalse)
11212           break;
11213       }
11214     }
11215
11216   use_global_plte=MagickFalse;
11217   all_images_are_gray=MagickFalse;
11218 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11219   need_local_plte=MagickTrue;
11220 #endif
11221   need_defi=MagickFalse;
11222   need_matte=MagickFalse;
11223   mng_info->framing_mode=1;
11224   mng_info->old_framing_mode=1;
11225
11226   if (write_mng)
11227       if (image_info->page != (char *) NULL)
11228         {
11229           /*
11230             Determine image bounding box.
11231           */
11232           SetGeometry(image,&mng_info->page);
11233           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11234             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11235         }
11236   if (write_mng)
11237     {
11238       unsigned int
11239         need_geom;
11240
11241       unsigned short
11242         red,
11243         green,
11244         blue;
11245
11246       mng_info->page=image->page;
11247       need_geom=MagickTrue;
11248       if (mng_info->page.width || mng_info->page.height)
11249          need_geom=MagickFalse;
11250       /*
11251         Check all the scenes.
11252       */
11253       initial_delay=image->delay;
11254       need_iterations=MagickFalse;
11255       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11256       mng_info->equal_physs=MagickTrue,
11257       mng_info->equal_gammas=MagickTrue;
11258       mng_info->equal_srgbs=MagickTrue;
11259       mng_info->equal_backgrounds=MagickTrue;
11260       image_count=0;
11261 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11262     defined(PNG_MNG_FEATURES_SUPPORTED)
11263       all_images_are_gray=MagickTrue;
11264       mng_info->equal_palettes=MagickFalse;
11265       need_local_plte=MagickFalse;
11266 #endif
11267       for (next_image=image; next_image != (Image *) NULL; )
11268       {
11269         if (need_geom)
11270           {
11271             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11272               mng_info->page.width=next_image->columns+next_image->page.x;
11273
11274             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11275               mng_info->page.height=next_image->rows+next_image->page.y;
11276           }
11277
11278         if (next_image->page.x || next_image->page.y)
11279           need_defi=MagickTrue;
11280
11281         if (next_image->matte)
11282           need_matte=MagickTrue;
11283
11284         if ((int) next_image->dispose >= BackgroundDispose)
11285           if (next_image->matte || next_image->page.x || next_image->page.y ||
11286               ((next_image->columns < mng_info->page.width) &&
11287                (next_image->rows < mng_info->page.height)))
11288             mng_info->need_fram=MagickTrue;
11289
11290         if (next_image->iterations)
11291           need_iterations=MagickTrue;
11292
11293         final_delay=next_image->delay;
11294
11295         if (final_delay != initial_delay || final_delay > 1UL*
11296            next_image->ticks_per_second)
11297           mng_info->need_fram=1;
11298
11299 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11300     defined(PNG_MNG_FEATURES_SUPPORTED)
11301         /*
11302           check for global palette possibility.
11303         */
11304         if (image->matte != MagickFalse)
11305            need_local_plte=MagickTrue;
11306
11307         if (need_local_plte == 0)
11308           {
11309             if (ImageIsGray(image) == MagickFalse)
11310               all_images_are_gray=MagickFalse;
11311             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11312             if (use_global_plte == 0)
11313               use_global_plte=mng_info->equal_palettes;
11314             need_local_plte=!mng_info->equal_palettes;
11315           }
11316 #endif
11317         if (GetNextImageInList(next_image) != (Image *) NULL)
11318           {
11319             if (next_image->background_color.red !=
11320                 next_image->next->background_color.red ||
11321                 next_image->background_color.green !=
11322                 next_image->next->background_color.green ||
11323                 next_image->background_color.blue !=
11324                 next_image->next->background_color.blue)
11325               mng_info->equal_backgrounds=MagickFalse;
11326
11327             if (next_image->gamma != next_image->next->gamma)
11328               mng_info->equal_gammas=MagickFalse;
11329
11330             if (next_image->rendering_intent !=
11331                 next_image->next->rendering_intent)
11332               mng_info->equal_srgbs=MagickFalse;
11333
11334             if ((next_image->units != next_image->next->units) ||
11335                 (next_image->x_resolution != next_image->next->x_resolution) ||
11336                 (next_image->y_resolution != next_image->next->y_resolution))
11337               mng_info->equal_physs=MagickFalse;
11338
11339             if (mng_info->equal_chrms)
11340               {
11341                 if (next_image->chromaticity.red_primary.x !=
11342                     next_image->next->chromaticity.red_primary.x ||
11343                     next_image->chromaticity.red_primary.y !=
11344                     next_image->next->chromaticity.red_primary.y ||
11345                     next_image->chromaticity.green_primary.x !=
11346                     next_image->next->chromaticity.green_primary.x ||
11347                     next_image->chromaticity.green_primary.y !=
11348                     next_image->next->chromaticity.green_primary.y ||
11349                     next_image->chromaticity.blue_primary.x !=
11350                     next_image->next->chromaticity.blue_primary.x ||
11351                     next_image->chromaticity.blue_primary.y !=
11352                     next_image->next->chromaticity.blue_primary.y ||
11353                     next_image->chromaticity.white_point.x !=
11354                     next_image->next->chromaticity.white_point.x ||
11355                     next_image->chromaticity.white_point.y !=
11356                     next_image->next->chromaticity.white_point.y)
11357                   mng_info->equal_chrms=MagickFalse;
11358               }
11359           }
11360         image_count++;
11361         next_image=GetNextImageInList(next_image);
11362       }
11363       if (image_count < 2)
11364         {
11365           mng_info->equal_backgrounds=MagickFalse;
11366           mng_info->equal_chrms=MagickFalse;
11367           mng_info->equal_gammas=MagickFalse;
11368           mng_info->equal_srgbs=MagickFalse;
11369           mng_info->equal_physs=MagickFalse;
11370           use_global_plte=MagickFalse;
11371 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11372           need_local_plte=MagickTrue;
11373 #endif
11374           need_iterations=MagickFalse;
11375         }
11376
11377      if (mng_info->need_fram == MagickFalse)
11378        {
11379          /*
11380            Only certain framing rates 100/n are exactly representable without
11381            the FRAM chunk but we'll allow some slop in VLC files
11382          */
11383          if (final_delay == 0)
11384            {
11385              if (need_iterations != MagickFalse)
11386                {
11387                  /*
11388                    It's probably a GIF with loop; don't run it *too* fast.
11389                  */
11390                  if (mng_info->adjoin)
11391                    {
11392                      final_delay=10;
11393                      (void) ThrowMagickException(&image->exception,
11394                         GetMagickModule(),CoderWarning,
11395                        "input has zero delay between all frames; assuming",
11396                        " 10 cs `%s'","");
11397                    }
11398                }
11399              else
11400                mng_info->ticks_per_second=0;
11401            }
11402          if (final_delay != 0)
11403            mng_info->ticks_per_second=(png_uint_32)
11404               (image->ticks_per_second/final_delay);
11405          if (final_delay > 50)
11406            mng_info->ticks_per_second=2;
11407
11408          if (final_delay > 75)
11409            mng_info->ticks_per_second=1;
11410
11411          if (final_delay > 125)
11412            mng_info->need_fram=MagickTrue;
11413
11414          if (need_defi && final_delay > 2 && (final_delay != 4) &&
11415             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11416             (final_delay != 25) && (final_delay != 50) && (final_delay !=
11417                1UL*image->ticks_per_second))
11418            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
11419        }
11420
11421      if (mng_info->need_fram != MagickFalse)
11422         mng_info->ticks_per_second=1UL*image->ticks_per_second;
11423      /*
11424         If pseudocolor, we should also check to see if all the
11425         palettes are identical and write a global PLTE if they are.
11426         ../glennrp Feb 99.
11427      */
11428      /*
11429         Write the MNG version 1.0 signature and MHDR chunk.
11430      */
11431      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11432      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
11433      PNGType(chunk,mng_MHDR);
11434      LogPNGChunk(logging,mng_MHDR,28L);
11435      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11436      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
11437      PNGLong(chunk+12,mng_info->ticks_per_second);
11438      PNGLong(chunk+16,0L);  /* layer count=unknown */
11439      PNGLong(chunk+20,0L);  /* frame count=unknown */
11440      PNGLong(chunk+24,0L);  /* play time=unknown   */
11441      if (write_jng)
11442        {
11443          if (need_matte)
11444            {
11445              if (need_defi || mng_info->need_fram || use_global_plte)
11446                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
11447
11448              else
11449                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
11450            }
11451
11452          else
11453            {
11454              if (need_defi || mng_info->need_fram || use_global_plte)
11455                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
11456
11457              else
11458                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
11459            }
11460        }
11461
11462      else
11463        {
11464          if (need_matte)
11465            {
11466              if (need_defi || mng_info->need_fram || use_global_plte)
11467                PNGLong(chunk+28,11L);    /* simplicity=LC */
11468
11469              else
11470                PNGLong(chunk+28,9L);    /* simplicity=VLC */
11471            }
11472
11473          else
11474            {
11475              if (need_defi || mng_info->need_fram || use_global_plte)
11476                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
11477
11478              else
11479                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
11480            }
11481        }
11482      (void) WriteBlob(image,32,chunk);
11483      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11484      option=GetImageOption(image_info,"mng:need-cacheoff");
11485      if (option != (const char *) NULL)
11486        {
11487          size_t
11488            length;
11489
11490          /*
11491            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11492          */
11493          PNGType(chunk,mng_nEED);
11494          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
11495          (void) WriteBlobMSBULong(image,(size_t) length);
11496          LogPNGChunk(logging,mng_nEED,(size_t) length);
11497          length+=4;
11498          (void) WriteBlob(image,length,chunk);
11499          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11500        }
11501      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11502          (GetNextImageInList(image) != (Image *) NULL) &&
11503          (image->iterations != 1))
11504        {
11505          /*
11506            Write MNG TERM chunk
11507          */
11508          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
11509          PNGType(chunk,mng_TERM);
11510          LogPNGChunk(logging,mng_TERM,10L);
11511          chunk[4]=3;  /* repeat animation */
11512          chunk[5]=0;  /* show last frame when done */
11513          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11514             final_delay/MagickMax(image->ticks_per_second,1)));
11515
11516          if (image->iterations == 0)
11517            PNGLong(chunk+10,PNG_UINT_31_MAX);
11518
11519          else
11520            PNGLong(chunk+10,(png_uint_32) image->iterations);
11521
11522          if (logging != MagickFalse)
11523            {
11524              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11525                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11526               final_delay/MagickMax(image->ticks_per_second,1)));
11527
11528              if (image->iterations == 0)
11529                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11530                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
11531
11532              else
11533                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11534                  "     Image iterations: %.20g",(double) image->iterations);
11535            }
11536          (void) WriteBlob(image,14,chunk);
11537          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11538        }
11539      /*
11540        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11541      */
11542      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11543           mng_info->equal_srgbs)
11544        {
11545          /*
11546            Write MNG sRGB chunk
11547          */
11548          (void) WriteBlobMSBULong(image,1L);
11549          PNGType(chunk,mng_sRGB);
11550          LogPNGChunk(logging,mng_sRGB,1L);
11551
11552          if (image->rendering_intent != UndefinedIntent)
11553            chunk[4]=(unsigned char)
11554              Magick_RenderingIntent_to_PNG_RenderingIntent(
11555              (image->rendering_intent));
11556
11557          else
11558            chunk[4]=(unsigned char)
11559              Magick_RenderingIntent_to_PNG_RenderingIntent(
11560                (PerceptualIntent));
11561
11562          (void) WriteBlob(image,5,chunk);
11563          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11564          mng_info->have_write_global_srgb=MagickTrue;
11565        }
11566
11567      else
11568        {
11569          if (image->gamma && mng_info->equal_gammas)
11570            {
11571              /*
11572                 Write MNG gAMA chunk
11573              */
11574              (void) WriteBlobMSBULong(image,4L);
11575              PNGType(chunk,mng_gAMA);
11576              LogPNGChunk(logging,mng_gAMA,4L);
11577              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11578              (void) WriteBlob(image,8,chunk);
11579              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11580              mng_info->have_write_global_gama=MagickTrue;
11581            }
11582          if (mng_info->equal_chrms)
11583            {
11584              PrimaryInfo
11585                primary;
11586
11587              /*
11588                 Write MNG cHRM chunk
11589              */
11590              (void) WriteBlobMSBULong(image,32L);
11591              PNGType(chunk,mng_cHRM);
11592              LogPNGChunk(logging,mng_cHRM,32L);
11593              primary=image->chromaticity.white_point;
11594              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11595              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11596              primary=image->chromaticity.red_primary;
11597              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11598              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11599              primary=image->chromaticity.green_primary;
11600              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11601              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11602              primary=image->chromaticity.blue_primary;
11603              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11604              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11605              (void) WriteBlob(image,36,chunk);
11606              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11607              mng_info->have_write_global_chrm=MagickTrue;
11608            }
11609        }
11610      if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11611        {
11612          /*
11613             Write MNG pHYs chunk
11614          */
11615          (void) WriteBlobMSBULong(image,9L);
11616          PNGType(chunk,mng_pHYs);
11617          LogPNGChunk(logging,mng_pHYs,9L);
11618
11619          if (image->units == PixelsPerInchResolution)
11620            {
11621              PNGLong(chunk+4,(png_uint_32)
11622                (image->x_resolution*100.0/2.54+0.5));
11623
11624              PNGLong(chunk+8,(png_uint_32)
11625                (image->y_resolution*100.0/2.54+0.5));
11626
11627              chunk[12]=1;
11628            }
11629
11630          else
11631            {
11632              if (image->units == PixelsPerCentimeterResolution)
11633                {
11634                  PNGLong(chunk+4,(png_uint_32)
11635                    (image->x_resolution*100.0+0.5));
11636
11637                  PNGLong(chunk+8,(png_uint_32)
11638                    (image->y_resolution*100.0+0.5));
11639
11640                  chunk[12]=1;
11641                }
11642
11643              else
11644                {
11645                  PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11646                  PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11647                  chunk[12]=0;
11648                }
11649            }
11650          (void) WriteBlob(image,13,chunk);
11651          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11652        }
11653      /*
11654        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11655        or does not cover the entire frame.
11656      */
11657      if (write_mng && (image->matte || image->page.x > 0 ||
11658          image->page.y > 0 || (image->page.width &&
11659          (image->page.width+image->page.x < mng_info->page.width))
11660          || (image->page.height && (image->page.height+image->page.y
11661          < mng_info->page.height))))
11662        {
11663          (void) WriteBlobMSBULong(image,6L);
11664          PNGType(chunk,mng_BACK);
11665          LogPNGChunk(logging,mng_BACK,6L);
11666          red=ScaleQuantumToShort(image->background_color.red);
11667          green=ScaleQuantumToShort(image->background_color.green);
11668          blue=ScaleQuantumToShort(image->background_color.blue);
11669          PNGShort(chunk+4,red);
11670          PNGShort(chunk+6,green);
11671          PNGShort(chunk+8,blue);
11672          (void) WriteBlob(image,10,chunk);
11673          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11674          if (mng_info->equal_backgrounds)
11675            {
11676              (void) WriteBlobMSBULong(image,6L);
11677              PNGType(chunk,mng_bKGD);
11678              LogPNGChunk(logging,mng_bKGD,6L);
11679              (void) WriteBlob(image,10,chunk);
11680              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11681            }
11682        }
11683
11684 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11685      if ((need_local_plte == MagickFalse) &&
11686          (image->storage_class == PseudoClass) &&
11687          (all_images_are_gray == MagickFalse))
11688        {
11689          size_t
11690            data_length;
11691
11692          /*
11693            Write MNG PLTE chunk
11694          */
11695          data_length=3*image->colors;
11696          (void) WriteBlobMSBULong(image,data_length);
11697          PNGType(chunk,mng_PLTE);
11698          LogPNGChunk(logging,mng_PLTE,data_length);
11699
11700          for (i=0; i < (ssize_t) image->colors; i++)
11701          {
11702            chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11703            chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11704            chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11705          }
11706
11707          (void) WriteBlob(image,data_length+4,chunk);
11708          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11709          mng_info->have_write_global_plte=MagickTrue;
11710        }
11711 #endif
11712     }
11713   scene=0;
11714   mng_info->delay=0;
11715 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11716     defined(PNG_MNG_FEATURES_SUPPORTED)
11717   mng_info->equal_palettes=MagickFalse;
11718 #endif
11719   do
11720   {
11721     if (mng_info->adjoin)
11722     {
11723 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11724     defined(PNG_MNG_FEATURES_SUPPORTED)
11725     /*
11726       If we aren't using a global palette for the entire MNG, check to
11727       see if we can use one for two or more consecutive images.
11728     */
11729     if (need_local_plte && use_global_plte && !all_images_are_gray)
11730       {
11731         if (mng_info->IsPalette)
11732           {
11733             /*
11734               When equal_palettes is true, this image has the same palette
11735               as the previous PseudoClass image
11736             */
11737             mng_info->have_write_global_plte=mng_info->equal_palettes;
11738             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11739             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11740               {
11741                 /*
11742                   Write MNG PLTE chunk
11743                 */
11744                 size_t
11745                   data_length;
11746
11747                 data_length=3*image->colors;
11748                 (void) WriteBlobMSBULong(image,data_length);
11749                 PNGType(chunk,mng_PLTE);
11750                 LogPNGChunk(logging,mng_PLTE,data_length);
11751
11752                 for (i=0; i < (ssize_t) image->colors; i++)
11753                 {
11754                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11755                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11756                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11757                 }
11758
11759                 (void) WriteBlob(image,data_length+4,chunk);
11760                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11761                    (uInt) (data_length+4)));
11762                 mng_info->have_write_global_plte=MagickTrue;
11763               }
11764           }
11765         else
11766           mng_info->have_write_global_plte=MagickFalse;
11767       }
11768 #endif
11769     if (need_defi)
11770       {
11771         ssize_t
11772           previous_x,
11773           previous_y;
11774
11775         if (scene)
11776           {
11777             previous_x=mng_info->page.x;
11778             previous_y=mng_info->page.y;
11779           }
11780         else
11781           {
11782             previous_x=0;
11783             previous_y=0;
11784           }
11785         mng_info->page=image->page;
11786         if ((mng_info->page.x !=  previous_x) ||
11787             (mng_info->page.y != previous_y))
11788           {
11789              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
11790              PNGType(chunk,mng_DEFI);
11791              LogPNGChunk(logging,mng_DEFI,12L);
11792              chunk[4]=0; /* object 0 MSB */
11793              chunk[5]=0; /* object 0 LSB */
11794              chunk[6]=0; /* visible  */
11795              chunk[7]=0; /* abstract */
11796              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11797              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11798              (void) WriteBlob(image,16,chunk);
11799              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11800           }
11801       }
11802     }
11803
11804    mng_info->write_mng=write_mng;
11805
11806    if ((int) image->dispose >= 3)
11807      mng_info->framing_mode=3;
11808
11809    if (mng_info->need_fram && mng_info->adjoin &&
11810        ((image->delay != mng_info->delay) ||
11811         (mng_info->framing_mode != mng_info->old_framing_mode)))
11812      {
11813        if (image->delay == mng_info->delay)
11814          {
11815            /*
11816              Write a MNG FRAM chunk with the new framing mode.
11817            */
11818            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
11819            PNGType(chunk,mng_FRAM);
11820            LogPNGChunk(logging,mng_FRAM,1L);
11821            chunk[4]=(unsigned char) mng_info->framing_mode;
11822            (void) WriteBlob(image,5,chunk);
11823            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11824          }
11825        else
11826          {
11827            /*
11828              Write a MNG FRAM chunk with the delay.
11829            */
11830            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
11831            PNGType(chunk,mng_FRAM);
11832            LogPNGChunk(logging,mng_FRAM,10L);
11833            chunk[4]=(unsigned char) mng_info->framing_mode;
11834            chunk[5]=0;  /* frame name separator (no name) */
11835            chunk[6]=2;  /* flag for changing default delay */
11836            chunk[7]=0;  /* flag for changing frame timeout */
11837            chunk[8]=0;  /* flag for changing frame clipping */
11838            chunk[9]=0;  /* flag for changing frame sync_id */
11839            PNGLong(chunk+10,(png_uint_32)
11840              ((mng_info->ticks_per_second*
11841              image->delay)/MagickMax(image->ticks_per_second,1)));
11842            (void) WriteBlob(image,14,chunk);
11843            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11844            mng_info->delay=(png_uint_32) image->delay;
11845          }
11846        mng_info->old_framing_mode=mng_info->framing_mode;
11847      }
11848
11849 #if defined(JNG_SUPPORTED)
11850    if (image_info->compression == JPEGCompression)
11851      {
11852        ImageInfo
11853          *write_info;
11854
11855        if (logging != MagickFalse)
11856          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11857            "  Writing JNG object.");
11858        /* To do: specify the desired alpha compression method. */
11859        write_info=CloneImageInfo(image_info);
11860        write_info->compression=UndefinedCompression;
11861        status=WriteOneJNGImage(mng_info,write_info,image);
11862        write_info=DestroyImageInfo(write_info);
11863      }
11864    else
11865 #endif
11866      {
11867        if (logging != MagickFalse)
11868          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11869            "  Writing PNG object.");
11870
11871        mng_info->need_blob = MagickFalse;
11872
11873        /* We don't want any ancillary chunks written */
11874        mng_info->ping_exclude_bKGD=MagickTrue;
11875        mng_info->ping_exclude_cHRM=MagickTrue;
11876        mng_info->ping_exclude_EXIF=MagickTrue;
11877        mng_info->ping_exclude_gAMA=MagickTrue;
11878        mng_info->ping_exclude_cHRM=MagickTrue;
11879        mng_info->ping_exclude_iCCP=MagickTrue;
11880        /* mng_info->ping_exclude_iTXt=MagickTrue; */
11881        mng_info->ping_exclude_oFFs=MagickTrue;
11882        mng_info->ping_exclude_pHYs=MagickTrue;
11883        mng_info->ping_exclude_sRGB=MagickTrue;
11884        mng_info->ping_exclude_tEXt=MagickTrue;
11885        mng_info->ping_exclude_tRNS=MagickTrue;
11886        mng_info->ping_exclude_vpAg=MagickTrue;
11887        mng_info->ping_exclude_zCCP=MagickTrue;
11888        mng_info->ping_exclude_zTXt=MagickTrue;
11889
11890        status=WriteOnePNGImage(mng_info,image_info,image);
11891      }
11892
11893     if (status == MagickFalse)
11894       {
11895         MngInfoFreeStruct(mng_info,&have_mng_structure);
11896         (void) CloseBlob(image);
11897         return(MagickFalse);
11898       }
11899     (void) CatchImageException(image);
11900     if (GetNextImageInList(image) == (Image *) NULL)
11901       break;
11902     image=SyncNextImageInList(image);
11903     status=SetImageProgress(image,SaveImagesTag,scene++,
11904       GetImageListLength(image));
11905
11906     if (status == MagickFalse)
11907       break;
11908
11909   } while (mng_info->adjoin);
11910
11911   if (write_mng)
11912     {
11913       while (GetPreviousImageInList(image) != (Image *) NULL)
11914         image=GetPreviousImageInList(image);
11915       /*
11916         Write the MEND chunk.
11917       */
11918       (void) WriteBlobMSBULong(image,0x00000000L);
11919       PNGType(chunk,mng_MEND);
11920       LogPNGChunk(logging,mng_MEND,0L);
11921       (void) WriteBlob(image,4,chunk);
11922       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11923     }
11924   /*
11925     Relinquish resources.
11926   */
11927   (void) CloseBlob(image);
11928   MngInfoFreeStruct(mng_info,&have_mng_structure);
11929
11930   if (logging != MagickFalse)
11931     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
11932
11933   return(MagickTrue);
11934 }
11935 #else /* PNG_LIBPNG_VER > 10011 */
11936
11937 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
11938 {
11939   image=image;
11940   printf("Your PNG library is too old: You have libpng-%s\n",
11941      PNG_LIBPNG_VER_STRING);
11942
11943   ThrowBinaryException(CoderError,"PNG library is too old",
11944      image_info->filename);
11945 }
11946
11947 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11948 {
11949   return(WritePNGImage(image_info,image));
11950 }
11951 #endif /* PNG_LIBPNG_VER > 10011 */
11952 #endif