]> granicus.if.org Git - imagemagick/blob - coders/png.c
Force PNG decoder to return image->matte=MagickTrue when the PNG has
[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 reduce
6984    * the colors to a simple 3-3-3 palette and run once more, and finally
6985    * to a simple 3-3-2 palette.
6986    */
6987
6988   tried_333 = MagickFalse;
6989   tried_444 = MagickFalse;
6990
6991   for (j=0; j<5; j++)
6992   {
6993     /* BUILD_PALETTE
6994      *
6995      * Sometimes we get DirectClass images that have 256 colors or fewer.
6996      * This code will build a colormap.
6997      *
6998      * Also, sometimes we get PseudoClass images with an out-of-date
6999      * colormap.  This code will replace the colormap with a new one.
7000      * Sometimes we get PseudoClass images that have more than 256 colors.
7001      * This code will delete the colormap and change the image to
7002      * DirectClass.
7003      *
7004      * If image->matte is MagickFalse, we ignore the opacity channel
7005      * even though it sometimes contains left-over non-opaque values.
7006      *
7007      * Also we gather some information (number of opaque, transparent,
7008      * and semitransparent pixels, and whether the image has any non-gray
7009      * pixels or only black-and-white pixels) that we might need later.
7010      *
7011      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7012      * we need to check for bogus non-opaque values, at least.
7013      */
7014
7015 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7016 #  define PNGK 0 /* Shift */
7017 #  define PNGM 1 /* Scale */
7018 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
7019 #  define PNGK 8
7020 #  define PNGM 0x0101
7021 #else
7022 #  define PNGK 24
7023 #  define PNGM 0x01010101
7024 #endif
7025
7026    ExceptionInfo
7027      *exception;
7028
7029    int
7030      n;
7031
7032    PixelPacket
7033      opaque[260],
7034      semitransparent[260],
7035      transparent[260];
7036
7037    register IndexPacket
7038      *indexes;
7039
7040    register const PixelPacket
7041      *s,
7042      *q;
7043
7044    register PixelPacket
7045      *r;
7046
7047    if (logging != MagickFalse)
7048      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7049          "    Enter BUILD_PALETTE:");
7050
7051    if (logging != MagickFalse)
7052      {
7053        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7054              "      image->columns=%.20g",(double) image->columns);
7055        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7056              "      image->rows=%.20g",(double) image->rows);
7057        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7058              "      image->matte=%.20g",(double) image->matte);
7059        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7060              "      image->depth=%.20g",(double) image->depth);
7061
7062        if (image->storage_class == PseudoClass && image->colormap != NULL)
7063        {
7064          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7065              "      Original colormap:");
7066          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7067              "        i    (red,green,blue,opacity)");
7068
7069          for (i=0; i < 256; i++)
7070          {
7071                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7072                    "        %d    (%d,%d,%d,%d)",
7073                     (int) i,
7074                     (int) image->colormap[i].red,
7075                     (int) image->colormap[i].green,
7076                     (int) image->colormap[i].blue,
7077                     (int) image->colormap[i].opacity);
7078          }
7079
7080          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7081          {
7082            if (i > 255)
7083              {
7084                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7085                    "        %d    (%d,%d,%d,%d)",
7086                     (int) i,
7087                     (int) image->colormap[i].red,
7088                     (int) image->colormap[i].green,
7089                     (int) image->colormap[i].blue,
7090                     (int) image->colormap[i].opacity);
7091              }
7092          }
7093        }
7094
7095        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7096            "      image->colors=%d",(int) image->colors);
7097
7098        if (image->colors == 0)
7099        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7100            "        (zero means unknown)");
7101
7102        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7103             "      Regenerate the colormap");
7104      }
7105
7106      exception=(&image->exception);
7107
7108      image_colors=0;
7109      number_opaque = 0;
7110      number_semitransparent = 0;
7111      number_transparent = 0;
7112
7113      for (y=0; y < (ssize_t) image->rows; y++)
7114      {
7115        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7116
7117        if (q == (PixelPacket *) NULL)
7118          break;
7119
7120        for (x=0; x < (ssize_t) image->columns; x++)
7121        {
7122            if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7123              {
7124                if (number_opaque < 259)
7125                  {
7126                    if (number_opaque == 0)
7127                      {
7128                        opaque[0]=*q;
7129                        opaque[0].opacity=OpaqueOpacity;
7130                        number_opaque=1;
7131                      }
7132
7133                    for (i=0; i< (ssize_t) number_opaque; i++)
7134                      {
7135                        if (IsColorEqual(opaque+i, (PixelPacket *) q))
7136                          break;
7137                      }
7138
7139                    if (i ==  (ssize_t) number_opaque &&
7140                        number_opaque < 259)
7141                      {
7142                        number_opaque++;
7143                        opaque[i] = *q;
7144                        opaque[i].opacity = OpaqueOpacity;
7145                      }
7146                  }
7147              }
7148            else if (q->opacity == TransparentOpacity)
7149              {
7150                if (number_transparent < 259)
7151                  {
7152                    if (number_transparent == 0)
7153                      {
7154                        transparent[0]=*q;
7155                        ping_trans_color.red=(unsigned short)(q->red);
7156                        ping_trans_color.green=(unsigned short) (q->green);
7157                        ping_trans_color.blue=(unsigned short) (q->blue);
7158                        ping_trans_color.gray=(unsigned short) (q->blue);
7159                        number_transparent = 1;
7160                      }
7161
7162                    for (i=0; i< (ssize_t) number_transparent; i++)
7163                      {
7164                        if (IsColorEqual(transparent+i, (PixelPacket *) q))
7165                          break;
7166                      }
7167
7168                    if (i ==  (ssize_t) number_transparent &&
7169                        number_transparent < 259)
7170                      {
7171                        number_transparent++;
7172                        transparent[i] = *q;
7173                      }
7174                  }
7175              }
7176            else
7177              {
7178                if (number_semitransparent < 259)
7179                  {
7180                    if (number_semitransparent == 0)
7181                      {
7182                        semitransparent[0]=*q;
7183                        number_semitransparent = 1;
7184                      }
7185
7186                    for (i=0; i< (ssize_t) number_semitransparent; i++)
7187                      {
7188                        if (IsColorEqual(semitransparent+i,
7189                           (PixelPacket *) q) &&
7190                           q->opacity == semitransparent[i].opacity)
7191                          break;
7192                      }
7193
7194                    if (i ==  (ssize_t) number_semitransparent &&
7195                        number_semitransparent < 259)
7196                      {
7197                        number_semitransparent++;
7198                        semitransparent[i] = *q;
7199                      }
7200                  }
7201              }
7202            q++;
7203         }
7204      }
7205
7206      if (ping_exclude_bKGD == MagickFalse)
7207        {
7208          /* Add the background color to the palette, if it
7209           * isn't already there.
7210           */
7211           for (i=0; i<number_opaque; i++)
7212           {
7213              if (IsColorEqual(opaque+i, &image->background_color))
7214              break;
7215           }
7216
7217           if (number_opaque < 259 && i == number_opaque)
7218             {
7219                opaque[i]=image->background_color;
7220                opaque[i].opacity = OpaqueOpacity;
7221                number_opaque++;
7222             }
7223           else if (logging != MagickFalse)
7224               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7225                   "      No room in the colormap to add background color");
7226        }
7227
7228      image_colors=number_opaque+number_transparent+number_semitransparent;
7229
7230      if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7231        {
7232          /* No room for the background color; remove it. */
7233          number_opaque--;
7234          image_colors--;
7235        }
7236
7237      if (logging != MagickFalse)
7238        {
7239          if (image_colors > 256)
7240             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7241                   "      image has more than 256 colors");
7242
7243          else
7244             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7245                   "      image has %d colors",image_colors);
7246        }
7247
7248      if (mng_info->write_png_colortype != 7) /* We won't need this info */
7249        {
7250          ping_have_color=MagickFalse;
7251          ping_have_non_bw=MagickFalse;
7252
7253          if(image_colors > 256)
7254            {
7255              for (y=0; y < (ssize_t) image->rows; y++)
7256              {
7257                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7258
7259                if (q == (PixelPacket *) NULL)
7260                  break;
7261
7262                /* Worst case is black-and-white; we are looking at every
7263                 * pixel twice.
7264                 */
7265
7266                if (ping_have_color == MagickFalse)
7267                  {
7268                    s=q;
7269                    for (x=0; x < (ssize_t) image->columns; x++)
7270                    {
7271                      if (s->red != s->green || s->red != s->blue)
7272                        {
7273                           ping_have_color=MagickTrue;
7274                           ping_have_non_bw=MagickTrue;
7275                           break;
7276                        }
7277                      s++;
7278                    }
7279                  }
7280
7281                if (ping_have_non_bw == MagickFalse)
7282                  {
7283                    s=q;
7284                    for (x=0; x < (ssize_t) image->columns; x++)
7285                    {
7286                      if (s->red != 0 && s->red != QuantumRange)
7287                        {
7288                          ping_have_non_bw=MagickTrue;
7289                        }
7290                      s++;
7291                    }
7292                  }
7293              }
7294            } 
7295        } 
7296
7297      if (image_colors < 257)
7298        {
7299          PixelPacket
7300            colormap[260];
7301              
7302          /*
7303           * Initialize image colormap.
7304           */
7305
7306          if (logging != MagickFalse)
7307             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7308                   "      Sort the new colormap");
7309
7310         /* Sort palette, transparent first */;
7311
7312          n = 0;
7313
7314          for (i=0; i<number_transparent; i++)
7315             colormap[n++] = transparent[i];
7316
7317          for (i=0; i<number_semitransparent; i++)
7318             colormap[n++] = semitransparent[i];
7319
7320          for (i=0; i<number_opaque; i++)
7321             colormap[n++] = opaque[i];
7322
7323          
7324          /* image_colors < 257; search the colormap instead of the pixels
7325           * to get ping_have_color and ping_have_non_bw
7326           */
7327          for (i=0; i<n; i++)
7328          {
7329            if (ping_have_color == MagickFalse)
7330              {
7331                 if (colormap[i].red != colormap[i].green ||
7332                     colormap[i].red != colormap[i].blue)
7333                   {
7334                      ping_have_color=MagickTrue;
7335                      ping_have_non_bw=MagickTrue;
7336                      break;
7337                   }
7338               }
7339
7340            if (ping_have_non_bw == MagickFalse)
7341              {
7342                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
7343                    ping_have_non_bw=MagickTrue;
7344              }
7345           }
7346
7347         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7348             (number_transparent == 0 && number_semitransparent == 0)) &&
7349             (((mng_info->write_png_colortype-1) ==
7350             PNG_COLOR_TYPE_PALETTE) ||
7351             (mng_info->write_png_colortype == 0)))
7352           {
7353             if (logging != MagickFalse)
7354               {
7355                 if (n !=  (ssize_t) image_colors)
7356                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7357                    "   image_colors (%d) and n (%d)  don't match",
7358                    image_colors, n);
7359
7360                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7361                    "      AcquireImageColormap");
7362               }
7363
7364             image->colors = image_colors;
7365
7366             if (AcquireImageColormap(image,image_colors) ==
7367                 MagickFalse)
7368                ThrowWriterException(ResourceLimitError,
7369                   "MemoryAllocationFailed");
7370
7371             for (i=0; i< (ssize_t) image_colors; i++)
7372                image->colormap[i] = colormap[i];
7373
7374             if (logging != MagickFalse)
7375               {
7376                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7377                       "      image->colors=%d (%d)",
7378                       (int) image->colors, image_colors);
7379  
7380                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7381                       "      Update the pixel indexes");
7382               }
7383
7384             /* Sync the pixel indices with the new colormap */
7385
7386             for (y=0; y < (ssize_t) image->rows; y++)
7387             {
7388               q=GetAuthenticPixels(image,0,y,image->columns,1,
7389                   exception);
7390
7391               if (q == (PixelPacket *) NULL)
7392                 break;
7393
7394               indexes=GetAuthenticIndexQueue(image);
7395  
7396               for (x=0; x < (ssize_t) image->columns; x++)
7397               {
7398                 for (i=0; i< (ssize_t) image_colors; i++)
7399                 {
7400                   if ((image->matte == MagickFalse ||
7401                       image->colormap[i].opacity == q->opacity) &&
7402                       (IsColorEqual(&image->colormap[i],
7403                          (PixelPacket *) q)))
7404                   {
7405                     indexes[x]=(IndexPacket) i;
7406                     break;
7407                   }
7408                 }
7409                 q++;
7410               }
7411
7412               if (SyncAuthenticPixels(image,exception) == MagickFalse)
7413                  break;
7414             }
7415           }
7416        }
7417
7418      if (logging != MagickFalse)
7419        {
7420          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7421             "      image->colors=%d", (int) image->colors);
7422
7423          if (image->colormap != NULL)
7424            {
7425              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7426                  "       i     (red,green,blue,opacity)");
7427
7428              for (i=0; i < (ssize_t) image->colors; i++)
7429              {
7430                if (i < 300 || i >= image->colors - 10)
7431                  {
7432                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7433                        "       %d     (%d,%d,%d,%d)",
7434                         (int) i,
7435                         (int) image->colormap[i].red,
7436                         (int) image->colormap[i].green,
7437                         (int) image->colormap[i].blue,
7438                         (int) image->colormap[i].opacity);
7439                  }
7440              }
7441            }
7442
7443            if (number_transparent < 257)
7444              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7445                    "      number_transparent     = %d",
7446                    number_transparent);
7447            else
7448
7449              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7450                    "      number_transparent     > 256");
7451
7452            if (number_opaque < 257)
7453              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7454                    "      number_opaque          = %d",
7455                    number_opaque);
7456
7457            else
7458              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7459                    "      number_opaque          > 256");
7460
7461            if (number_semitransparent < 257)
7462              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7463                    "      number_semitransparent = %d",
7464                    number_semitransparent);
7465
7466            else
7467              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7468                    "      number_semitransparent > 256");
7469
7470            if (ping_have_non_bw == MagickFalse)
7471               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7472                     "      All pixels and the background are black or white");
7473
7474            else if (ping_have_color == MagickFalse)
7475               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7476                     "      All pixels and the background are gray");
7477
7478            else
7479               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7480                     "      At least one pixel or the background is non-gray");
7481
7482            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7483                "    Exit BUILD_PALETTE:");
7484        }
7485
7486    if (mng_info->write_png8 == MagickFalse)
7487       break;
7488
7489    /* Make any reductions necessary for the PNG8 format */
7490     if (image_colors <= 256 &&
7491         image_colors != 0 && image->colormap != NULL &&
7492         number_semitransparent == 0 &&
7493         number_transparent <= 1)
7494       break;
7495
7496     /* PNG8 can't have semitransparent colors so we threshold the
7497      * opacity to 0 or OpaqueOpacity
7498      */
7499     if (number_semitransparent != 0)
7500       {
7501         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7502             "    Thresholding the alpha channel to binary");
7503
7504         for (y=0; y < (ssize_t) image->rows; y++)
7505         {
7506           r=GetAuthenticPixels(image,0,y,image->columns,1,
7507               exception);
7508
7509           if (r == (PixelPacket *) NULL)
7510             break;
7511
7512           for (x=0; x < (ssize_t) image->columns; x++)
7513           {
7514               r->opacity = r->opacity > TransparentOpacity/2 ?
7515                    TransparentOpacity : OpaqueOpacity;
7516               r++;
7517           }
7518   
7519           if (SyncAuthenticPixels(image,exception) == MagickFalse)
7520              break;
7521
7522           if (image_colors != 0 && image_colors <= 256 &&
7523              image->colormap != NULL)
7524             for (i=0; i<image_colors; i++)
7525                 image->colormap[i].opacity =
7526                     image->colormap[i].opacity > TransparentOpacity/2 ?
7527                     TransparentOpacity : OpaqueOpacity;
7528         }
7529       continue;
7530     }
7531
7532     /* PNG8 can't have more than 256 colors so we quantize the pixels and
7533      * background color to the 4-4-4, 3-3-3 or 3-3-2 palette.  If the image is
7534      * mostly gray, the 4-4-4 palette should end up with 256 colors or less.
7535      */
7536     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7537       {
7538         if (logging != MagickFalse)
7539            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7540                "    Quantizing the background color to 4-4-4");
7541
7542         tried_444 = MagickTrue;
7543
7544         image->background_color.red=
7545             ((((((size_t)
7546             image->background_color.red) >> PNGK) & 0xf0)     )  |
7547             (((((size_t)
7548             image->background_color.red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7549         image->background_color.green=
7550             ((((((size_t)
7551             image->background_color.green) >> PNGK) & 0xf0)     )  |
7552             (((((size_t)
7553             image->background_color.green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7554         image->background_color.blue=
7555             ((((((size_t)
7556             image->background_color.blue) >> PNGK) & 0xf0)     )  |
7557             (((((size_t)
7558             image->background_color.blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7559
7560         if (logging != MagickFalse)
7561           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7562               "    Quantizing the pixel colors to 4-4-4");
7563
7564         if (image->colormap == NULL)
7565         {
7566           for (y=0; y < (ssize_t) image->rows; y++)
7567           {
7568             r=GetAuthenticPixels(image,0,y,image->columns,1,
7569                 exception);
7570
7571             if (r == (PixelPacket *) NULL)
7572               break;
7573
7574             for (x=0; x < (ssize_t) image->columns; x++)
7575             {
7576               if (r->opacity == TransparentOpacity)
7577                 {
7578                   r->red = image->background_color.red;
7579                   r->green = image->background_color.green;
7580                   r->blue = image->background_color.blue;
7581                 }
7582               else
7583                 {
7584                   r->red=
7585                        ((((((size_t) r->red) >> PNGK) & 0xf0)    )  |
7586                        (((((size_t) r->red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7587                   r->green=
7588                        ((((((size_t) r->green) >> PNGK) & 0xf0)    )  |
7589                        (((((size_t) r->green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7590                   r->blue=
7591                        ((((((size_t) r->blue) >> PNGK) & 0xf0)    )  |
7592                        (((((size_t) r->blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7593                 }
7594               r++;
7595             }
7596     
7597             if (SyncAuthenticPixels(image,exception) == MagickFalse)
7598                break;
7599           }
7600         }
7601
7602         else /* Should not reach this; colormap already exists and
7603                 must be <= 256 */
7604         {
7605           if (logging != MagickFalse)
7606               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7607               "    Quantizing the colormap to 4-4-4");
7608           for (i=0; i<image_colors; i++)
7609           {
7610               image->colormap[i].red=
7611                   ((((((size_t)
7612                   image->colormap[i].red) >> PNGK) & 0xf0)     )  |
7613                   (((((size_t)
7614                   image->colormap[i].red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7615               image->colormap[i].green=
7616                   ((((((size_t)
7617                   image->colormap[i].green) >> PNGK) & 0xf0)     )  |
7618                   (((((size_t)
7619                   image->colormap[i].green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7620               image->colormap[i].blue=
7621                   ((((((size_t)
7622                   image->colormap[i].blue) >> PNGK) & 0xf0)     )  |
7623                   (((((size_t)
7624                   image->colormap[i].blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7625           }
7626         }
7627         continue;
7628       }
7629
7630     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7631       {
7632         if (logging != MagickFalse)
7633            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7634                "    Quantizing the background color to 3-3-3");
7635
7636         tried_333 = MagickTrue;
7637
7638         image->background_color.red=
7639             ((((((size_t)
7640             image->background_color.red) >> PNGK) & 0xe0)     )  |
7641             (((((size_t)
7642             image->background_color.red) >> PNGK) & 0xe0) >> 3)  |
7643             (((((size_t)
7644             image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7645         image->background_color.green=
7646             ((((((size_t)
7647             image->background_color.green) >> PNGK) & 0xe0)     )  |
7648             (((((size_t)
7649             image->background_color.green) >> PNGK) & 0xe0) >> 3)  |
7650             (((((size_t)
7651             image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7652         image->background_color.blue=
7653             ((((((size_t)
7654             image->background_color.blue) >> PNGK) & 0xe0)     )  |
7655             (((((size_t)
7656             image->background_color.blue) >> PNGK) & 0xe0) >> 3)  |
7657             (((((size_t)
7658             image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7659
7660         if (logging != MagickFalse)
7661           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7662               "    Quantizing the pixel colors to 3-3-3");
7663
7664         if (image->colormap == NULL)
7665         {
7666           for (y=0; y < (ssize_t) image->rows; y++)
7667           {
7668             r=GetAuthenticPixels(image,0,y,image->columns,1,
7669                 exception);
7670
7671             if (r == (PixelPacket *) NULL)
7672               break;
7673
7674             for (x=0; x < (ssize_t) image->columns; x++)
7675             {
7676               if (r->opacity == TransparentOpacity)
7677                 {
7678                   r->red = image->background_color.red;
7679                   r->green = image->background_color.green;
7680                   r->blue = image->background_color.blue;
7681                 }
7682               else
7683                 {
7684                   r->red=
7685                        ((((((size_t) r->red) >> PNGK) & 0xe0)    )  |
7686                        (((((size_t) r->red) >> PNGK) & 0xe0) >> 3)  |
7687                        (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7688                   r->green=
7689                        ((((((size_t) r->green) >> PNGK) & 0xe0)    )  |
7690                        (((((size_t) r->green) >> PNGK) & 0xe0) >> 3)  |
7691                        (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7692                   r->blue=
7693                        ((((((size_t) r->blue) >> PNGK) & 0xe0)    )  |
7694                        (((((size_t) r->blue) >> PNGK) & 0xe0) >> 3)  |
7695                        (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7696                 }
7697               r++;
7698             }
7699     
7700             if (SyncAuthenticPixels(image,exception) == MagickFalse)
7701                break;
7702           }
7703         }
7704
7705         else /* Should not reach this; colormap already exists and
7706                 must be <= 256 */
7707         {
7708           if (logging != MagickFalse)
7709               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7710               "    Quantizing the colormap to 3-3-3");
7711           for (i=0; i<image_colors; i++)
7712           {
7713               image->colormap[i].red=
7714                   ((((((size_t)
7715                   image->colormap[i].red) >> PNGK) & 0xe0)     )  |
7716                   (((((size_t)
7717                   image->colormap[i].red) >> PNGK) & 0xe0) >> 3)  |
7718                   (((((size_t)
7719                   image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7720               image->colormap[i].green=
7721                   ((((((size_t)
7722                   image->colormap[i].green) >> PNGK) & 0xe0)     )  |
7723                   (((((size_t)
7724                   image->colormap[i].green) >> PNGK) & 0xe0) >> 3)  |
7725                   (((((size_t)
7726                   image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7727               image->colormap[i].blue=
7728                   ((((((size_t)
7729                   image->colormap[i].blue) >> PNGK) & 0xe0)     )  |
7730                   (((((size_t)
7731                   image->colormap[i].blue) >> PNGK) & 0xe0) >> 3)  |
7732                   (((((size_t)
7733                   image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7734           }
7735         }
7736         continue;
7737       }
7738
7739     if (image_colors == 0 || image_colors > 256)
7740       {
7741         if (logging != MagickFalse)
7742            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7743                "    Quantizing the background color to 3-3-2");
7744
7745         image->background_color.red=
7746             ((((((size_t)
7747             image->background_color.red) >> PNGK) & 0xe0)     )  |
7748             (((((size_t)
7749             image->background_color.red) >> PNGK) & 0xe0) >> 3)  |
7750             (((((size_t)
7751             image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7752         image->background_color.green=
7753             ((((((size_t)
7754             image->background_color.green) >> PNGK) & 0xe0)     )  |
7755             (((((size_t)
7756             image->background_color.green) >> PNGK) & 0xe0) >> 3)  |
7757             (((((size_t)
7758             image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7759         image->background_color.blue=
7760             ((((((size_t)
7761             image->background_color.blue) >> PNGK) & 0xc0)     )  |
7762             (((((size_t)
7763             image->background_color.blue) >> PNGK) & 0xc0) >> 2)  |
7764             (((((size_t)
7765             image->background_color.blue) >> PNGK) & 0xc0) >> 4)  |
7766             (((((size_t)
7767             image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7768
7769         if (logging != MagickFalse)
7770           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7771               "    Quantizing the pixel colors to 3-3-2");
7772
7773         if (image->colormap == NULL)
7774         {
7775           for (y=0; y < (ssize_t) image->rows; y++)
7776           {
7777             r=GetAuthenticPixels(image,0,y,image->columns,1,
7778                 exception);
7779
7780             if (r == (PixelPacket *) NULL)
7781               break;
7782
7783             for (x=0; x < (ssize_t) image->columns; x++)
7784             {
7785               if (r->opacity == TransparentOpacity)
7786                 {
7787                   r->red = image->background_color.red;
7788                   r->green = image->background_color.green;
7789                   r->blue = image->background_color.blue;
7790                 }
7791               else
7792                 {
7793                   r->red=
7794                        ((((((size_t) r->red) >> PNGK) & 0xe0)    )  |
7795                        (((((size_t) r->red) >> PNGK) & 0xe0) >> 3)  |
7796                        (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7797                   r->green=
7798                        ((((((size_t) r->green) >> PNGK) & 0xe0)    )  |
7799                        (((((size_t) r->green) >> PNGK) & 0xe0) >> 3)  |
7800                        (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7801                   r->blue=
7802                        ((((((size_t) r->blue) >> PNGK) & 0xc0)    )  |
7803                        (((((size_t) r->blue) >> PNGK) & 0xc0) >> 2)  |
7804                        (((((size_t) r->blue) >> PNGK) & 0xc0) >> 4)  |
7805                        (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7806                 }
7807               r++;
7808             }
7809     
7810             if (SyncAuthenticPixels(image,exception) == MagickFalse)
7811                break;
7812           }
7813         }
7814
7815         else /* Should not reach this; colormap already exists and
7816                 must be <= 256 */
7817         {
7818           if (logging != MagickFalse)
7819               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7820               "    Quantizing the colormap to 3-3-2");
7821           for (i=0; i<image_colors; i++)
7822           {
7823               image->colormap[i].red=
7824                   ((((((size_t)
7825                   image->colormap[i].red) >> PNGK) & 0xe0)     )  |
7826                   (((((size_t)
7827                   image->colormap[i].red) >> PNGK) & 0xe0) >> 3)  |
7828                   (((((size_t)
7829                   image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7830               image->colormap[i].green=
7831                   ((((((size_t)
7832                   image->colormap[i].green) >> PNGK) & 0xe0)     )  |
7833                   (((((size_t)
7834                   image->colormap[i].green) >> PNGK) & 0xe0) >> 3)  |
7835                   (((((size_t)
7836                   image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7837               image->colormap[i].blue=
7838                   ((((((size_t)
7839                   image->colormap[i].blue) >> PNGK) & 0xc0)     )  |
7840                   (((((size_t)
7841                   image->colormap[i].blue) >> PNGK) & 0xc0) >> 2)  |
7842                   (((((size_t)
7843                   image->colormap[i].blue) >> PNGK) & 0xc0) >> 4)  |
7844                   (((((size_t)
7845                   image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7846           }
7847       }
7848       continue;
7849     }
7850     break;
7851   }
7852   /* END OF BUILD_PALETTE */
7853
7854   /* If we are excluding the tRNS chunk and there is transparency,
7855    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
7856    * PNG.
7857    */
7858   if (mng_info->ping_exclude_tRNS != MagickFalse &&
7859      (number_transparent != 0 || number_semitransparent != 0))
7860     {
7861       int colortype=mng_info->write_png_colortype;
7862
7863       if (ping_have_color == MagickFalse)
7864         mng_info->write_png_colortype = 5;
7865
7866       else
7867         mng_info->write_png_colortype = 7;
7868
7869       if (colortype != 0 &&
7870          mng_info->write_png_colortype != (ssize_t) colortype)
7871         ping_need_colortype_warning=MagickTrue;
7872
7873     }
7874
7875   /* See if cheap transparency is possible.  It is only possible
7876    * when there is a single transparent color, no semitransparent
7877    * color, and no opaque color that has the same RGB components
7878    * as the transparent color.  We only need this information if
7879    * we are writing a PNG with colortype 0 or 2, and we have not
7880    * excluded the tRNS chunk.
7881    */
7882   if (number_transparent == 1 &&
7883       mng_info->write_png_colortype < 4)
7884     {
7885        ping_have_cheap_transparency = MagickTrue;
7886
7887        if (number_semitransparent != 0)
7888          ping_have_cheap_transparency = MagickFalse;
7889
7890        else if (image_colors == 0 || image_colors > 256 ||
7891            image->colormap == NULL)
7892          {
7893            ExceptionInfo
7894              *exception;
7895
7896            register const PixelPacket
7897              *q;
7898
7899            exception=(&image->exception);
7900
7901            for (y=0; y < (ssize_t) image->rows; y++)
7902            {
7903              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
7904
7905              if (q == (PixelPacket *) NULL)
7906                break;
7907
7908              for (x=0; x < (ssize_t) image->columns; x++)
7909              {
7910                  if (q->opacity != TransparentOpacity &&
7911                      (unsigned short) q->red == ping_trans_color.red &&
7912                      (unsigned short) q->green == ping_trans_color.green &&
7913                      (unsigned short) q->blue == ping_trans_color.blue)
7914                    {
7915                      ping_have_cheap_transparency = MagickFalse;
7916                      break;
7917                    }
7918
7919                  q++;
7920              }
7921     
7922              if (ping_have_cheap_transparency == MagickFalse)
7923                 break;
7924            }
7925          }
7926        else
7927          {
7928             /* Assuming that image->colormap[0] is the one transparent color
7929              * and that all others are opaque.
7930              */
7931             if (image_colors > 1)
7932               for (i=1; i<image_colors; i++)
7933                 if (image->colormap[i].red == image->colormap[0].red &&
7934                     image->colormap[i].green == image->colormap[0].green &&
7935                     image->colormap[i].blue == image->colormap[0].blue)
7936                   {
7937                      ping_have_cheap_transparency = MagickFalse;
7938                      break;
7939                   }
7940          }
7941        
7942        if (logging != MagickFalse)
7943          {
7944            if (ping_have_cheap_transparency == MagickFalse)
7945              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7946                  "   Cheap transparency is not possible.");
7947
7948            else
7949              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7950                  "   Cheap transparency is possible.");
7951          }
7952      }
7953   else
7954     ping_have_cheap_transparency = MagickFalse;
7955
7956   image_depth=image->depth;
7957
7958   quantum_info = (QuantumInfo *) NULL;
7959   number_colors=0;
7960   image_colors=(int) image->colors;
7961   image_matte=image->matte;
7962
7963   mng_info->IsPalette=image->storage_class == PseudoClass &&
7964     image_colors <= 256 && image->colormap != NULL;
7965
7966   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
7967      (image->colors == 0 || image->colormap == NULL))
7968     {
7969       image_info=DestroyImageInfo(image_info);
7970       image=DestroyImage(image);
7971       (void) ThrowMagickException(&IMimage->exception,
7972           GetMagickModule(),CoderError,
7973           "Cannot write PNG8 or color-type 3; colormap is NULL",
7974           "`%s'",IMimage->filename);
7975 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7976       UnlockSemaphoreInfo(ping_semaphore);
7977 #endif
7978       return(MagickFalse);
7979     }
7980
7981   /*
7982     Allocate the PNG structures
7983   */
7984 #ifdef PNG_USER_MEM_SUPPORTED
7985   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
7986     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
7987     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
7988
7989 #else
7990   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
7991     MagickPNGErrorHandler,MagickPNGWarningHandler);
7992
7993 #endif
7994   if (ping == (png_struct *) NULL)
7995     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7996
7997   ping_info=png_create_info_struct(ping);
7998
7999   if (ping_info == (png_info *) NULL)
8000     {
8001       png_destroy_write_struct(&ping,(png_info **) NULL);
8002       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8003     }
8004
8005   png_set_write_fn(ping,image,png_put_data,png_flush_data);
8006   ping_pixels=(unsigned char *) NULL;
8007
8008   if (setjmp(png_jmpbuf(ping)))
8009     {
8010       /*
8011         PNG write failed.
8012       */
8013 #ifdef PNG_DEBUG
8014      if (image_info->verbose)
8015         (void) printf("PNG write has failed.\n");
8016 #endif
8017       png_destroy_write_struct(&ping,&ping_info);
8018 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8019       UnlockSemaphoreInfo(ping_semaphore);
8020 #endif
8021       if (ping_have_blob != MagickFalse)
8022           (void) CloseBlob(image);
8023       image_info=DestroyImageInfo(image_info);
8024       image=DestroyImage(image);
8025       return(MagickFalse);
8026     }
8027   /*
8028     Prepare PNG for writing.
8029   */
8030 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8031   if (mng_info->write_mng)
8032      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8033
8034 #else
8035 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8036   if (mng_info->write_mng)
8037      png_permit_empty_plte(ping,MagickTrue);
8038
8039 # endif
8040 #endif
8041
8042   x=0;
8043
8044   ping_width=(png_uint_32) image->columns;
8045   ping_height=(png_uint_32) image->rows;
8046
8047   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8048      image_depth=8;
8049
8050   if (mng_info->write_png_depth != 0)
8051      image_depth=mng_info->write_png_depth;
8052
8053   /* Adjust requested depth to next higher valid depth if necessary */
8054   if (image_depth > 8)
8055      image_depth=16;
8056
8057   if ((image_depth > 4) && (image_depth < 8))
8058      image_depth=8;
8059
8060   if (image_depth == 3)
8061      image_depth=4;
8062
8063   if (logging != MagickFalse)
8064     {
8065      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8066         "    width=%.20g",(double) ping_width);
8067      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8068         "    height=%.20g",(double) ping_height);
8069      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8070         "    image_matte=%.20g",(double) image->matte);
8071      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8072         "    image->depth=%.20g",(double) image->depth);
8073      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8074         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
8075     }
8076
8077   save_image_depth=image_depth;
8078   ping_bit_depth=(png_byte) save_image_depth;
8079
8080
8081 #if defined(PNG_pHYs_SUPPORTED)
8082   if (ping_exclude_pHYs == MagickFalse)
8083   {
8084   if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8085       (!mng_info->write_mng || !mng_info->equal_physs))
8086     {
8087       if (logging != MagickFalse)
8088         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8089             "    Setting up pHYs chunk");
8090
8091       if (image->units == PixelsPerInchResolution)
8092         {
8093           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8094           ping_pHYs_x_resolution=
8095              (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8096           ping_pHYs_y_resolution=
8097              (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8098         }
8099
8100       else if (image->units == PixelsPerCentimeterResolution)
8101         {
8102           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8103           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8104           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
8105         }
8106
8107       else
8108         {
8109           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8110           ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8111           ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
8112         }
8113
8114       if (logging != MagickFalse)
8115         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8116           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8117           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8118           (int) ping_pHYs_unit_type);
8119        ping_have_pHYs = MagickTrue;
8120     }
8121   }
8122 #endif
8123
8124   if (ping_exclude_bKGD == MagickFalse)
8125   {
8126   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
8127     {
8128        unsigned int
8129          mask;
8130
8131        mask=0xffff;
8132        if (ping_bit_depth == 8)
8133           mask=0x00ff;
8134
8135        if (ping_bit_depth == 4)
8136           mask=0x000f;
8137
8138        if (ping_bit_depth == 2)
8139           mask=0x0003;
8140
8141        if (ping_bit_depth == 1)
8142           mask=0x0001;
8143
8144        ping_background.red=(png_uint_16)
8145          (ScaleQuantumToShort(image->background_color.red) & mask);
8146
8147        ping_background.green=(png_uint_16)
8148          (ScaleQuantumToShort(image->background_color.green) & mask);
8149
8150        ping_background.blue=(png_uint_16)
8151          (ScaleQuantumToShort(image->background_color.blue) & mask);
8152     }
8153
8154   if (logging != MagickFalse)
8155     {
8156       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8157           "    Setting up bKGD chunk (1)");
8158
8159       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8160           "    ping_bit_depth=%d",ping_bit_depth);
8161     }
8162
8163   ping_have_bKGD = MagickTrue;
8164   }
8165
8166   /*
8167     Select the color type.
8168   */
8169   matte=image_matte;
8170   old_bit_depth=0;
8171
8172   if (mng_info->IsPalette && mng_info->write_png8)
8173     {
8174
8175       /* To do: make this a function cause it's used twice, except
8176          for reducing the sample depth from 8. */
8177
8178       number_colors=image_colors;
8179
8180       ping_have_tRNS=MagickFalse;
8181
8182       /*
8183         Set image palette.
8184       */
8185       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8186
8187       if (logging != MagickFalse)
8188         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8189             "  Setting up PLTE chunk with %d colors (%d)",
8190             number_colors, image_colors);
8191
8192       for (i=0; i < (ssize_t) number_colors; i++)
8193       {
8194         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8195         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8196         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8197         if (logging != MagickFalse)
8198           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8199 #if MAGICKCORE_QUANTUM_DEPTH == 8
8200             "    %3ld (%3d,%3d,%3d)",
8201 #else
8202             "    %5ld (%5d,%5d,%5d)",
8203 #endif
8204             (long) i,palette[i].red,palette[i].green,palette[i].blue);
8205
8206       }
8207
8208       ping_have_PLTE=MagickTrue;
8209       image_depth=ping_bit_depth;
8210       ping_num_trans=0;
8211
8212       if (matte != MagickFalse)
8213       {
8214           /*
8215             Identify which colormap entry is transparent.
8216           */
8217           assert(number_colors <= 256);
8218           assert(image->colormap != NULL);
8219
8220           for (i=0; i < (ssize_t) number_transparent; i++)
8221              ping_trans_alpha[i]=0;
8222
8223
8224           ping_num_trans=(unsigned short) (number_transparent +
8225              number_semitransparent);
8226
8227           if (ping_num_trans == 0)
8228              ping_have_tRNS=MagickFalse;
8229
8230           else
8231              ping_have_tRNS=MagickTrue;
8232       }
8233
8234       if (ping_exclude_bKGD == MagickFalse)
8235       {
8236        /*
8237         * Identify which colormap entry is the background color.
8238         */
8239
8240         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8241           if (IsPNGColorEqual(ping_background,image->colormap[i]))
8242             break;
8243
8244         ping_background.index=(png_byte) i;
8245       }
8246     } /* end of write_png8 */
8247
8248   else if (mng_info->write_png24)
8249     {
8250       image_matte=MagickFalse;
8251       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8252     }
8253
8254   else if (mng_info->write_png32)
8255     {
8256       image_matte=MagickTrue;
8257       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8258     }
8259
8260   else /* mng_info->write_pngNN not specified */
8261     {
8262       image_depth=ping_bit_depth;
8263
8264       if (mng_info->write_png_colortype != 0)
8265         {
8266           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
8267
8268           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8269               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8270             image_matte=MagickTrue;
8271
8272           else
8273             image_matte=MagickFalse;
8274
8275           if (logging != MagickFalse)
8276              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8277              "   PNG colortype %d was specified:",(int) ping_color_type);
8278         }
8279
8280       else /* write_png_colortype not specified */
8281         {
8282           if (logging != MagickFalse)
8283              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8284              "  Selecting PNG colortype:");
8285
8286           ping_color_type=(png_byte) ((matte != MagickFalse)?
8287             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
8288
8289           if (image_info->type == TrueColorType)
8290             {
8291               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8292               image_matte=MagickFalse;
8293             }
8294
8295           if (image_info->type == TrueColorMatteType)
8296             {
8297               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8298               image_matte=MagickTrue;
8299             }
8300
8301           if (image_info->type == PaletteType ||
8302               image_info->type == PaletteMatteType)
8303             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8304
8305           if (mng_info->write_png_colortype == 0 &&
8306              (image_info->type == UndefinedType ||
8307              image_info->type == OptimizeType))
8308             {
8309               if (ping_have_color == MagickFalse)
8310                 {
8311                   if (image_matte == MagickFalse)
8312                     {
8313                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8314                       image_matte=MagickFalse;
8315                     }
8316
8317                   else
8318                     {
8319                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8320                       image_matte=MagickTrue;
8321                     }
8322                 }
8323               else
8324                 {
8325                   if (image_matte == MagickFalse)
8326                     {
8327                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8328                       image_matte=MagickFalse;
8329                     }
8330
8331                   else
8332                     {
8333                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8334                       image_matte=MagickTrue;
8335                     }
8336                  }
8337             }
8338
8339         }
8340
8341       if (logging != MagickFalse)
8342          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8343          "    Selected PNG colortype=%d",ping_color_type);
8344
8345       if (ping_bit_depth < 8)
8346         {
8347           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8348               ping_color_type == PNG_COLOR_TYPE_RGB ||
8349               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8350             ping_bit_depth=8;
8351         }
8352
8353       old_bit_depth=ping_bit_depth;
8354
8355       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8356         {
8357           if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8358              ping_bit_depth=1;
8359         }
8360
8361       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
8362         {
8363            size_t one = 1;
8364            ping_bit_depth=1;
8365
8366            if (image->colors == 0)
8367            {
8368               /* DO SOMETHING */
8369               (void) ThrowMagickException(&image->exception,
8370                  GetMagickModule(),CoderError,
8371                 "image has 0 colors", "`%s'","");
8372            }
8373
8374            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
8375              ping_bit_depth <<= 1;
8376         }
8377
8378       if (logging != MagickFalse)
8379          {
8380            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8381             "    Number of colors: %.20g",(double) image_colors);
8382
8383            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8384             "    Tentative PNG bit depth: %d",ping_bit_depth);
8385          }
8386
8387       if (ping_bit_depth < (int) mng_info->write_png_depth)
8388          ping_bit_depth = mng_info->write_png_depth;
8389     }
8390
8391   image_depth=ping_bit_depth;
8392
8393   if (logging != MagickFalse)
8394     {
8395       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8396         "    Tentative PNG color type: %.20g",(double) ping_color_type);
8397
8398       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8399         "    image_info->type: %.20g",(double) image_info->type);
8400
8401       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8402         "    image_depth: %.20g",(double) image_depth);
8403
8404       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8405
8406         "    image->depth: %.20g",(double) image->depth);
8407
8408       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8409         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
8410     }
8411
8412   if (matte != MagickFalse)
8413     {
8414       if (mng_info->IsPalette)
8415         {
8416           if (mng_info->write_png_colortype == 0)
8417             {
8418               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8419
8420               if (ping_have_color != MagickFalse)
8421                  ping_color_type=PNG_COLOR_TYPE_RGBA;
8422             }
8423
8424           /*
8425            * Determine if there is any transparent color.
8426           */
8427           if (number_transparent + number_semitransparent == 0)
8428             {
8429               /*
8430                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
8431               */
8432
8433               image_matte=MagickFalse;
8434
8435               if (mng_info->write_png_colortype == 0)
8436                 ping_color_type&=0x03;
8437             }
8438
8439           else
8440             {
8441               unsigned int
8442                 mask; 
8443
8444               mask=0xffff;
8445
8446               if (ping_bit_depth == 8)
8447                  mask=0x00ff;
8448
8449               if (ping_bit_depth == 4)
8450                  mask=0x000f;
8451
8452               if (ping_bit_depth == 2)
8453                  mask=0x0003;
8454
8455               if (ping_bit_depth == 1)
8456                  mask=0x0001;
8457
8458               ping_trans_color.red=(png_uint_16)
8459                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8460
8461               ping_trans_color.green=(png_uint_16)
8462                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8463
8464               ping_trans_color.blue=(png_uint_16)
8465                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8466
8467               ping_trans_color.gray=(png_uint_16)
8468                 (ScaleQuantumToShort(PixelIntensityToQuantum(
8469                    image->colormap)) & mask);
8470
8471               ping_trans_color.index=(png_byte) 0;
8472
8473               ping_have_tRNS=MagickTrue;
8474             }
8475
8476           if (ping_have_tRNS != MagickFalse)
8477             {
8478               /*
8479                * Determine if there is one and only one transparent color
8480                * and if so if it is fully transparent.
8481                */
8482               if (ping_have_cheap_transparency == MagickFalse)
8483                 ping_have_tRNS=MagickFalse;
8484             }
8485
8486           if (ping_have_tRNS != MagickFalse)
8487             {
8488               if (mng_info->write_png_colortype == 0)
8489                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
8490
8491               if (image_depth == 8)
8492                 {
8493                   ping_trans_color.red&=0xff;
8494                   ping_trans_color.green&=0xff;
8495                   ping_trans_color.blue&=0xff;
8496                   ping_trans_color.gray&=0xff;
8497                 }
8498             }
8499         }
8500       else
8501         {
8502           if (image_depth == 8)
8503             {
8504               ping_trans_color.red&=0xff;
8505               ping_trans_color.green&=0xff;
8506               ping_trans_color.blue&=0xff;
8507               ping_trans_color.gray&=0xff;
8508             }
8509         }
8510     }
8511
8512     matte=image_matte;
8513
8514     if (ping_have_tRNS != MagickFalse)
8515       image_matte=MagickFalse;
8516
8517     if ((mng_info->IsPalette) &&
8518         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
8519         ping_have_color == MagickFalse &&
8520         (image_matte == MagickFalse || image_depth >= 8))
8521       {
8522         size_t one=1;
8523
8524         if (image_matte != MagickFalse)
8525           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8526
8527         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
8528           {
8529             ping_color_type=PNG_COLOR_TYPE_GRAY;
8530
8531             if (save_image_depth == 16 && image_depth == 8)
8532               {
8533                 if (logging != MagickFalse)
8534                   {
8535                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8536                         "  Scaling ping_trans_color (0)");
8537                   }
8538                     ping_trans_color.gray*=0x0101;
8539               }
8540           }
8541
8542         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8543           image_depth=MAGICKCORE_QUANTUM_DEPTH;
8544
8545         if (image_colors == 0 || image_colors-1 > MaxColormapSize)
8546           image_colors=(int) (one << image_depth);
8547
8548         if (image_depth > 8)
8549           ping_bit_depth=16;
8550
8551         else
8552           {
8553             ping_bit_depth=8;
8554             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8555               {
8556                 if(!mng_info->write_png_depth)
8557                   {
8558                     ping_bit_depth=1;
8559
8560                     while ((int) (one << ping_bit_depth)
8561                         < (ssize_t) image_colors)
8562                       ping_bit_depth <<= 1;
8563                   }
8564               }
8565
8566             else if (ping_color_type ==
8567                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
8568                 mng_info->IsPalette)
8569               {
8570               /* Check if grayscale is reducible */
8571
8572                 int
8573                   depth_4_ok=MagickTrue,
8574                   depth_2_ok=MagickTrue,
8575                   depth_1_ok=MagickTrue;
8576
8577                 for (i=0; i < (ssize_t) image_colors; i++)
8578                 {
8579                    unsigned char
8580                      intensity;
8581
8582                    intensity=ScaleQuantumToChar(image->colormap[i].red);
8583
8584                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8585                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8586                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8587                      depth_2_ok=depth_1_ok=MagickFalse;
8588                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
8589                      depth_1_ok=MagickFalse;
8590                 }
8591
8592                 if (depth_1_ok && mng_info->write_png_depth <= 1)
8593                   ping_bit_depth=1;
8594
8595                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
8596                   ping_bit_depth=2;
8597
8598                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
8599                   ping_bit_depth=4;
8600               }
8601           }
8602
8603           image_depth=ping_bit_depth;
8604       }
8605
8606     else
8607
8608       if (mng_info->IsPalette)
8609       {
8610         number_colors=image_colors;
8611
8612         if (image_depth <= 8)
8613           {
8614             /*
8615               Set image palette.
8616             */
8617             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8618
8619             if (mng_info->have_write_global_plte && matte == MagickFalse)
8620               {
8621                 png_set_PLTE(ping,ping_info,NULL,0);
8622
8623                 if (logging != MagickFalse)
8624                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8625                     "  Setting up empty PLTE chunk");
8626               }
8627
8628             else
8629               {
8630                 for (i=0; i < (ssize_t) number_colors; i++)
8631                 {
8632                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8633                   palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8634                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8635                 }
8636
8637                 if (logging != MagickFalse)
8638                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8639                     "  Setting up PLTE chunk with %d colors",
8640                     number_colors);
8641
8642                 ping_have_PLTE=MagickTrue;
8643               }
8644
8645             /* color_type is PNG_COLOR_TYPE_PALETTE */
8646             if (mng_info->write_png_depth == 0)
8647               {
8648                 size_t
8649                   one;
8650
8651                 ping_bit_depth=1;
8652                 one=1;
8653
8654                 while ((one << ping_bit_depth) < number_colors)
8655                   ping_bit_depth <<= 1;
8656               }
8657
8658             ping_num_trans=0;
8659
8660             if (matte != MagickFalse)
8661               {
8662                 /*
8663                  * Set up trans_colors array.
8664                  */
8665                 assert(number_colors <= 256);
8666
8667                 ping_num_trans=(unsigned short) (number_transparent +
8668                   number_semitransparent);
8669
8670                 if (ping_num_trans == 0)
8671                   ping_have_tRNS=MagickFalse;
8672
8673                 else
8674                   {
8675                     if (logging != MagickFalse)
8676                       {
8677                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8678                           "  Scaling ping_trans_color (1)");
8679                       }
8680                     ping_have_tRNS=MagickTrue;
8681
8682                     for (i=0; i < ping_num_trans; i++)
8683                     {
8684                        ping_trans_alpha[i]= (png_byte) (255-
8685                           ScaleQuantumToChar(image->colormap[i].opacity));
8686                     }
8687                   }
8688               }
8689           }
8690       }
8691
8692     else
8693       {
8694
8695         if (image_depth < 8)
8696           image_depth=8;
8697
8698         if ((save_image_depth == 16) && (image_depth == 8))
8699           {
8700             if (logging != MagickFalse)
8701               {
8702                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8703                   "    Scaling ping_trans_color from (%d,%d,%d)",
8704                   (int) ping_trans_color.red,
8705                   (int) ping_trans_color.green,
8706                   (int) ping_trans_color.blue);
8707               }
8708
8709             ping_trans_color.red*=0x0101;
8710             ping_trans_color.green*=0x0101;
8711             ping_trans_color.blue*=0x0101;
8712             ping_trans_color.gray*=0x0101;
8713
8714             if (logging != MagickFalse)
8715               {
8716                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8717                   "    to (%d,%d,%d)",
8718                   (int) ping_trans_color.red,
8719                   (int) ping_trans_color.green,
8720                   (int) ping_trans_color.blue);
8721               }
8722           }
8723       }
8724
8725     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
8726          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
8727
8728     /*
8729       Adjust background and transparency samples in sub-8-bit grayscale files.
8730     */
8731     if (ping_bit_depth < 8 && ping_color_type ==
8732         PNG_COLOR_TYPE_GRAY)
8733       {
8734          png_uint_16
8735            maxval;
8736
8737          size_t
8738            one=1;
8739
8740          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
8741
8742          if (ping_exclude_bKGD == MagickFalse)
8743          {
8744
8745          ping_background.gray=(png_uint_16)
8746            (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8747
8748          if (logging != MagickFalse)
8749            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8750              "  Setting up bKGD chunk (2)");
8751
8752          ping_have_bKGD = MagickTrue;
8753          }
8754
8755          ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8756            ping_trans_color.gray));
8757       }
8758
8759   if (ping_exclude_bKGD == MagickFalse)
8760   {
8761     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8762       {
8763         /*
8764            Identify which colormap entry is the background color.
8765         */
8766
8767         number_colors=image_colors;
8768
8769         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8770           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
8771             break;
8772
8773         ping_background.index=(png_byte) i;
8774
8775         if (logging != MagickFalse)
8776           {
8777             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8778               "  Setting up bKGD chunk with index=%d",(int) i);
8779           }
8780
8781         if (i < (ssize_t) number_colors)
8782           {
8783             ping_have_bKGD = MagickTrue;
8784
8785             if (logging != MagickFalse)
8786               {
8787                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8788                   "     background   =(%d,%d,%d)",
8789                         (int) ping_background.red,
8790                         (int) ping_background.green,
8791                         (int) ping_background.blue);
8792               }
8793           }
8794
8795         else  /* Can't happen */
8796           {
8797             if (logging != MagickFalse)
8798               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8799                   "      No room in PLTE to add bKGD color");
8800             ping_have_bKGD = MagickFalse;
8801           }
8802       }
8803   }
8804
8805   if (logging != MagickFalse)
8806     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8807       "    PNG color type: %d",ping_color_type);
8808   /*
8809     Initialize compression level and filtering.
8810   */
8811   if (logging != MagickFalse)
8812     {
8813       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8814         "  Setting up deflate compression");
8815
8816       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8817         "    Compression buffer size: 32768");
8818     }
8819
8820   png_set_compression_buffer_size(ping,32768L);
8821
8822   if (logging != MagickFalse)
8823     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8824       "    Compression mem level: 9");
8825
8826   png_set_compression_mem_level(ping, 9);
8827
8828   quality=image->quality == UndefinedCompressionQuality ? 75UL :
8829      image->quality;
8830
8831   if (quality > 9)
8832     {
8833       int
8834         level;
8835
8836       level=(int) MagickMin((ssize_t) quality/10,9);
8837
8838       if (logging != MagickFalse)
8839         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8840           "    Compression level: %d",level);
8841
8842       png_set_compression_level(ping,level);
8843     }
8844
8845   else
8846     {
8847       if (logging != MagickFalse)
8848         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8849           "    Compression strategy: Z_HUFFMAN_ONLY");
8850
8851       png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
8852     }
8853
8854   if (logging != MagickFalse)
8855     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8856       "  Setting up filtering");
8857
8858 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
8859   /* This became available in libpng-1.0.9.  Output must be a MNG. */
8860   if (mng_info->write_mng && ((quality % 10) == 7))
8861     {
8862       if (logging != MagickFalse)
8863         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8864           "    Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
8865
8866       ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
8867     }
8868
8869   else
8870     if (logging != MagickFalse)
8871       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8872         "    Filter_type: 0");
8873 #endif
8874
8875   {
8876     int
8877       base_filter;
8878
8879     if ((quality % 10) > 5)
8880       base_filter=PNG_ALL_FILTERS;
8881
8882     else
8883       if ((quality % 10) != 5)
8884         base_filter=(int) quality % 10;
8885
8886       else
8887         if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
8888             ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
8889             (quality < 50))
8890           base_filter=PNG_NO_FILTERS;
8891
8892         else
8893           base_filter=PNG_ALL_FILTERS;
8894
8895     if (logging != MagickFalse)
8896       {
8897         if (base_filter == PNG_ALL_FILTERS)
8898           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8899             "    Base filter method: ADAPTIVE");
8900         else
8901           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8902             "    Base filter method: NONE");
8903       }
8904
8905     png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
8906   }
8907
8908   if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
8909      (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
8910     {
8911       ResetImageProfileIterator(image);
8912       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8913       {
8914         profile=GetImageProfile(image,name);
8915
8916         if (profile != (StringInfo *) NULL)
8917           {
8918 #ifdef PNG_WRITE_iCCP_SUPPORTED
8919             if ((LocaleCompare(name,"ICC") == 0) ||
8920                 (LocaleCompare(name,"ICM") == 0))
8921              {
8922
8923                if (ping_exclude_iCCP == MagickFalse)
8924                  {
8925                        png_set_iCCP(ping,ping_info,(const png_charp) name,0,
8926 #if (PNG_LIBPNG_VER < 10500)
8927                          (png_charp) GetStringInfoDatum(profile),
8928 #else
8929                          (png_const_bytep) GetStringInfoDatum(profile),
8930 #endif
8931                          (png_uint_32) GetStringInfoLength(profile));
8932                  }
8933              }
8934
8935             else
8936 #endif
8937               if (ping_exclude_zCCP == MagickFalse)
8938                 {
8939                   Magick_png_write_raw_profile(image_info,ping,ping_info,
8940                     (unsigned char *) name,(unsigned char *) name,
8941                     GetStringInfoDatum(profile),
8942                     (png_uint_32) GetStringInfoLength(profile));
8943                 }
8944           }
8945
8946           if (logging != MagickFalse)
8947             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8948               "  Setting up text chunk with %s profile",name);
8949
8950         name=GetNextImageProfile(image);
8951       }
8952   }
8953
8954 #if defined(PNG_WRITE_sRGB_SUPPORTED)
8955   if ((mng_info->have_write_global_srgb == 0) &&
8956       ((image->rendering_intent != UndefinedIntent) ||
8957       (image->colorspace == sRGBColorspace)))
8958     {
8959       if (ping_exclude_sRGB == MagickFalse)
8960         {
8961           /*
8962             Note image rendering intent.
8963           */
8964           if (logging != MagickFalse)
8965             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8966                 "  Setting up sRGB chunk");
8967
8968           (void) png_set_sRGB(ping,ping_info,(
8969             Magick_RenderingIntent_to_PNG_RenderingIntent(
8970               image->rendering_intent)));
8971
8972           if (ping_exclude_gAMA == MagickFalse)
8973             png_set_gAMA(ping,ping_info,0.45455);
8974         }
8975     }
8976
8977   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
8978 #endif
8979     {
8980       if (ping_exclude_gAMA == MagickFalse &&
8981           (ping_exclude_sRGB == MagickFalse ||
8982           (image->gamma < .45 || image->gamma > .46)))
8983       {
8984       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
8985         {
8986           /*
8987             Note image gamma.
8988             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8989           */
8990           if (logging != MagickFalse)
8991             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8992               "  Setting up gAMA chunk");
8993
8994           png_set_gAMA(ping,ping_info,image->gamma);
8995         }
8996       }
8997
8998       if (ping_exclude_cHRM == MagickFalse)
8999         {
9000           if ((mng_info->have_write_global_chrm == 0) &&
9001               (image->chromaticity.red_primary.x != 0.0))
9002             {
9003               /*
9004                 Note image chromaticity.
9005                 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9006               */
9007                PrimaryInfo
9008                  bp,
9009                  gp,
9010                  rp,
9011                  wp;
9012
9013                wp=image->chromaticity.white_point;
9014                rp=image->chromaticity.red_primary;
9015                gp=image->chromaticity.green_primary;
9016                bp=image->chromaticity.blue_primary;
9017
9018                if (logging != MagickFalse)
9019                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9020                    "  Setting up cHRM chunk");
9021
9022                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9023                    bp.x,bp.y);
9024            }
9025         }
9026     }
9027
9028   ping_interlace_method=image_info->interlace != NoInterlace;
9029
9030   if (mng_info->write_mng)
9031     png_set_sig_bytes(ping,8);
9032
9033   /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9034
9035   if (mng_info->write_png_colortype != 0)
9036     {
9037      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
9038        if (ping_have_color != MagickFalse)
9039          {
9040            ping_color_type = PNG_COLOR_TYPE_RGB;
9041
9042            if (ping_bit_depth < 8)
9043              ping_bit_depth=8;
9044          }
9045
9046      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
9047        if (ping_have_color != MagickFalse)
9048          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
9049     }
9050
9051   if (ping_need_colortype_warning != MagickFalse ||
9052      ((mng_info->write_png_depth &&
9053      (int) mng_info->write_png_depth != ping_bit_depth) ||
9054      (mng_info->write_png_colortype &&
9055      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
9056       mng_info->write_png_colortype != 7 &&
9057       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
9058     {
9059       if (logging != MagickFalse)
9060         {
9061           if (ping_need_colortype_warning != MagickFalse)
9062             {
9063               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9064                  "  Image has transparency but tRNS chunk was excluded");
9065             }
9066
9067           if (mng_info->write_png_depth)
9068             {
9069               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9070                   "  Defined PNG:bit-depth=%u, Computed depth=%u",
9071                   mng_info->write_png_depth,
9072                   ping_bit_depth);
9073             }
9074
9075           if (mng_info->write_png_colortype)
9076             {
9077               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9078                   "  Defined PNG:color-type=%u, Computed color type=%u",
9079                   mng_info->write_png_colortype-1,
9080                   ping_color_type);
9081             }
9082         }
9083
9084       png_warning(ping,
9085         "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9086     }
9087
9088   if (image_matte != MagickFalse && image->matte == MagickFalse)
9089     {
9090       /* Add an opaque matte channel */
9091       image->matte = MagickTrue;
9092       (void) SetImageOpacity(image,0);
9093
9094       if (logging != MagickFalse)
9095         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9096           "  Added an opaque matte channel");
9097     }
9098
9099   if (number_transparent != 0 || number_semitransparent != 0)
9100     {
9101       if (ping_color_type < 4)
9102         {
9103            ping_have_tRNS=MagickTrue;
9104            if (logging != MagickFalse)
9105              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9106                "  Setting ping_have_tRNS=MagickTrue.");
9107         }
9108     }
9109
9110   if (logging != MagickFalse)
9111     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9112       "  Writing PNG header chunks");
9113
9114   png_set_IHDR(ping,ping_info,ping_width,ping_height,
9115                ping_bit_depth,ping_color_type,
9116                ping_interlace_method,ping_compression_method,
9117                ping_filter_method);
9118
9119   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9120     {
9121       png_set_PLTE(ping,ping_info,palette,number_colors);
9122
9123       if (logging != MagickFalse)
9124         {
9125           for (i=0; i< (ssize_t) number_colors; i++)
9126           {
9127             if (i < ping_num_trans)
9128               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9129                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9130                       (int) i,
9131                       (int) palette[i].red,
9132                       (int) palette[i].green,
9133                       (int) palette[i].blue,
9134                       (int) i,
9135                       (int) ping_trans_alpha[i]);
9136              else
9137               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9138                 "     PLTE[%d] = (%d,%d,%d)",
9139                       (int) i,
9140                       (int) palette[i].red,
9141                       (int) palette[i].green,
9142                       (int) palette[i].blue);
9143            }
9144          }
9145     }
9146
9147   if (ping_exclude_bKGD == MagickFalse)
9148     {
9149       if (ping_have_bKGD != MagickFalse)
9150           png_set_bKGD(ping,ping_info,&ping_background);
9151     }
9152
9153   if (ping_exclude_pHYs == MagickFalse)
9154     {
9155       if (ping_have_pHYs != MagickFalse)
9156         {
9157           png_set_pHYs(ping,ping_info,
9158              ping_pHYs_x_resolution,
9159              ping_pHYs_y_resolution,
9160              ping_pHYs_unit_type);
9161
9162           if (logging)
9163             {
9164               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9165                    "    Setting up pHYs chunk");
9166               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9167                    "      x_resolution=%lu",
9168                    (unsigned long) ping_pHYs_x_resolution);
9169               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9170                    "      y_resolution=%lu",
9171                    (unsigned long) ping_pHYs_y_resolution);
9172               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9173                    "      unit_type=%lu",
9174                    (unsigned long) ping_pHYs_unit_type);
9175             }
9176         }
9177     }
9178
9179 #if defined(PNG_oFFs_SUPPORTED)
9180   if (ping_exclude_oFFs == MagickFalse)
9181     {
9182       if (image->page.x || image->page.y)
9183         {
9184            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9185               (png_int_32) image->page.y, 0);
9186
9187            if (logging != MagickFalse)
9188              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9189                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
9190                  (int) image->page.x, (int) image->page.y);
9191         }
9192     }
9193 #endif
9194
9195   if (mng_info->need_blob != MagickFalse)
9196   {
9197     if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9198        MagickFalse)
9199        png_error(ping,"WriteBlob Failed");
9200
9201      ping_have_blob=MagickTrue;
9202   }
9203
9204   png_write_info_before_PLTE(ping, ping_info);
9205
9206   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
9207     {
9208       if (logging != MagickFalse)
9209         {
9210           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9211               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9212         }
9213
9214       if (ping_color_type == 3)
9215          (void) png_set_tRNS(ping, ping_info,
9216                 ping_trans_alpha,
9217                 ping_num_trans,
9218                 NULL);
9219
9220       else
9221         {
9222            (void) png_set_tRNS(ping, ping_info,
9223                   NULL,
9224                   0,
9225                   &ping_trans_color);
9226
9227            if (logging != MagickFalse)
9228              {
9229                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9230                  "     tRNS color   =(%d,%d,%d)",
9231                        (int) ping_trans_color.red,
9232                        (int) ping_trans_color.green,
9233                        (int) ping_trans_color.blue);
9234              }
9235          }
9236     }
9237
9238   /* write any png-chunk-b profiles */
9239   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
9240
9241   png_write_info(ping,ping_info);
9242
9243   /* write any PNG-chunk-m profiles */
9244   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
9245
9246   if (ping_exclude_vpAg == MagickFalse)
9247     {
9248       if ((image->page.width != 0 && image->page.width != image->columns) ||
9249           (image->page.height != 0 && image->page.height != image->rows))
9250         {
9251           unsigned char
9252             chunk[14];
9253
9254           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
9255           PNGType(chunk,mng_vpAg);
9256           LogPNGChunk(logging,mng_vpAg,9L);
9257           PNGLong(chunk+4,(png_uint_32) image->page.width);
9258           PNGLong(chunk+8,(png_uint_32) image->page.height);
9259           chunk[12]=0;   /* unit = pixels */
9260           (void) WriteBlob(image,13,chunk);
9261           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9262         }
9263     }
9264
9265 #if (PNG_LIBPNG_VER == 10206)
9266     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
9267 #define PNG_HAVE_IDAT               0x04
9268     ping->mode |= PNG_HAVE_IDAT;
9269 #undef PNG_HAVE_IDAT
9270 #endif
9271
9272   png_set_packing(ping);
9273   /*
9274     Allocate memory.
9275   */
9276   rowbytes=image->columns;
9277   if (image_depth > 8)
9278     rowbytes*=2;
9279   switch (ping_color_type)
9280     {
9281       case PNG_COLOR_TYPE_RGB:
9282         rowbytes*=3;
9283         break;
9284
9285       case PNG_COLOR_TYPE_GRAY_ALPHA:
9286         rowbytes*=2;
9287         break;
9288
9289       case PNG_COLOR_TYPE_RGBA:
9290         rowbytes*=4;
9291         break;
9292
9293       default:
9294         break;
9295     }
9296
9297   if (logging != MagickFalse)
9298     {
9299       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9300         "  Writing PNG image data");
9301
9302       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9303         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
9304     }
9305   ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9306     sizeof(*ping_pixels));
9307
9308   if (ping_pixels == (unsigned char *) NULL)
9309     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9310
9311   /*
9312     Initialize image scanlines.
9313   */
9314   if (setjmp(png_jmpbuf(ping)))
9315     {
9316       /*
9317         PNG write failed.
9318       */
9319 #ifdef PNG_DEBUG
9320      if (image_info->verbose)
9321         (void) printf("PNG write has failed.\n");
9322 #endif
9323       png_destroy_write_struct(&ping,&ping_info);
9324       if (quantum_info != (QuantumInfo *) NULL)
9325         quantum_info=DestroyQuantumInfo(quantum_info);
9326       if (ping_pixels != (unsigned char *) NULL)
9327         ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9328 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9329       UnlockSemaphoreInfo(ping_semaphore);
9330 #endif
9331       if (ping_have_blob != MagickFalse)
9332           (void) CloseBlob(image);
9333       image_info=DestroyImageInfo(image_info);
9334       image=DestroyImage(image);
9335       return(MagickFalse);
9336     }
9337   quantum_info=AcquireQuantumInfo(image_info,image);
9338   if (quantum_info == (QuantumInfo *) NULL)
9339     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9340   quantum_info->format=UndefinedQuantumFormat;
9341   quantum_info->depth=image_depth;
9342   num_passes=png_set_interlace_handling(ping);
9343
9344   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9345        !mng_info->write_png32) &&
9346        (mng_info->IsPalette ||
9347        (image_info->type == BilevelType)) &&
9348        image_matte == MagickFalse &&
9349        ping_have_non_bw == MagickFalse)
9350     {
9351       /* Palette, Bilevel, or Opaque Monochrome */
9352       register const PixelPacket
9353         *p;
9354
9355       quantum_info->depth=8;
9356       for (pass=0; pass < num_passes; pass++)
9357       {
9358         /*
9359           Convert PseudoClass image to a PNG monochrome image.
9360         */
9361         for (y=0; y < (ssize_t) image->rows; y++)
9362         {
9363           if (logging != MagickFalse && y == 0)
9364              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9365                  "    Writing row of pixels (0)");
9366
9367           p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9368
9369           if (p == (const PixelPacket *) NULL)
9370             break;
9371
9372           if (mng_info->IsPalette)
9373             {
9374               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9375                 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9376               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9377                   mng_info->write_png_depth &&
9378                   mng_info->write_png_depth != old_bit_depth)
9379                 {
9380                   /* Undo pixel scaling */
9381                   for (i=0; i < (ssize_t) image->columns; i++)
9382                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
9383                      >> (8-old_bit_depth));
9384                 }
9385             }
9386
9387           else
9388             {
9389               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9390                 quantum_info,RedQuantum,ping_pixels,&image->exception);
9391             }
9392
9393           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
9394             for (i=0; i < (ssize_t) image->columns; i++)
9395                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
9396                       255 : 0);
9397
9398           if (logging != MagickFalse && y == 0)
9399             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9400                 "    Writing row of pixels (1)");
9401
9402           png_write_row(ping,ping_pixels);
9403         }
9404         if (image->previous == (Image *) NULL)
9405           {
9406             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9407             if (status == MagickFalse)
9408               break;
9409           }
9410       }
9411     }
9412
9413   else   /* Not Palette, Bilevel, or Opaque Monochrome */
9414     {
9415       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9416          !mng_info->write_png32) &&
9417          (image_matte != MagickFalse ||
9418          (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
9419          (mng_info->IsPalette) && ping_have_color == MagickFalse)
9420         {
9421           register const PixelPacket
9422             *p;
9423
9424           for (pass=0; pass < num_passes; pass++)
9425           {
9426
9427           for (y=0; y < (ssize_t) image->rows; y++)
9428           {
9429             p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9430
9431             if (p == (const PixelPacket *) NULL)
9432               break;
9433
9434             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9435               {
9436                 if (mng_info->IsPalette)
9437                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9438                     quantum_info,GrayQuantum,ping_pixels,&image->exception);
9439
9440                 else
9441                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9442                     quantum_info,RedQuantum,ping_pixels,&image->exception);
9443
9444                 if (logging != MagickFalse && y == 0)
9445                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9446                        "    Writing GRAY PNG pixels (2)");
9447               }
9448
9449             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9450               {
9451                 if (logging != MagickFalse && y == 0)
9452                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9453                          "    Writing GRAY_ALPHA PNG pixels (2)");
9454
9455                 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9456                   quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
9457               }
9458
9459             if (logging != MagickFalse && y == 0)
9460               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9461                   "    Writing row of pixels (2)");
9462
9463             png_write_row(ping,ping_pixels);
9464           }
9465
9466           if (image->previous == (Image *) NULL)
9467             {
9468               status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9469               if (status == MagickFalse)
9470                 break;
9471             }
9472           }
9473         }
9474
9475       else
9476         {
9477           register const PixelPacket
9478             *p;
9479
9480           for (pass=0; pass < num_passes; pass++)
9481           {
9482             if ((image_depth > 8) || (mng_info->write_png24 ||
9483                 mng_info->write_png32 ||
9484                 (!mng_info->write_png8 && !mng_info->IsPalette)))
9485             {
9486               for (y=0; y < (ssize_t) image->rows; y++)
9487               {
9488                 p=GetVirtualPixels(image,0,y,image->columns,1,
9489                    &image->exception);
9490
9491                 if (p == (const PixelPacket *) NULL)
9492                   break;
9493
9494                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9495                   {
9496                     if (image->storage_class == DirectClass)
9497                       (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9498                         quantum_info,RedQuantum,ping_pixels,&image->exception);
9499
9500                     else
9501                       (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9502                         quantum_info,GrayQuantum,ping_pixels,&image->exception);
9503                   }
9504
9505                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9506                   {
9507                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9508                       quantum_info,GrayAlphaQuantum,ping_pixels,
9509                       &image->exception);
9510
9511                     if (logging != MagickFalse && y == 0)
9512                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9513                            "    Writing GRAY_ALPHA PNG pixels (3)");
9514                   }
9515
9516                 else if (image_matte != MagickFalse)
9517                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9518                     quantum_info,RGBAQuantum,ping_pixels,&image->exception);
9519
9520                 else
9521                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9522                     quantum_info,RGBQuantum,ping_pixels,&image->exception);
9523
9524                 if (logging != MagickFalse && y == 0)
9525                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9526                       "    Writing row of pixels (3)");
9527
9528                 png_write_row(ping,ping_pixels);
9529               }
9530             }
9531
9532           else
9533             /* not ((image_depth > 8) || (mng_info->write_png24 ||
9534                 mng_info->write_png32 ||
9535                 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9536             {
9537               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9538                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9539                 {
9540                   if (logging != MagickFalse)
9541                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9542                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
9543
9544                   quantum_info->depth=8;
9545                   image_depth=8;
9546                 }
9547
9548               for (y=0; y < (ssize_t) image->rows; y++)
9549               {
9550                 if (logging != MagickFalse && y == 0)
9551                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9552                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
9553
9554                 p=GetVirtualPixels(image,0,y,image->columns,1,
9555                    &image->exception);
9556
9557                 if (p == (const PixelPacket *) NULL)
9558                   break;
9559
9560                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9561                   {
9562                     quantum_info->depth=image->depth;
9563
9564                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9565                        quantum_info,GrayQuantum,ping_pixels,&image->exception);
9566                   }
9567
9568                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9569                   {
9570                     if (logging != MagickFalse && y == 0)
9571                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9572                            "  Writing GRAY_ALPHA PNG pixels (4)");
9573
9574                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9575                          quantum_info,GrayAlphaQuantum,ping_pixels,
9576                          &image->exception);
9577                   }
9578
9579                 else
9580                   {
9581                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9582                       quantum_info,IndexQuantum,ping_pixels,&image->exception);
9583
9584                     if (logging != MagickFalse && y <= 2)
9585                     {
9586                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9587                           "  Writing row of non-gray pixels (4)");
9588
9589                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9590                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
9591                           (int)ping_pixels[0],(int)ping_pixels[1]);
9592                     }
9593                   }
9594                 png_write_row(ping,ping_pixels);
9595               }
9596             }
9597
9598             if (image->previous == (Image *) NULL)
9599               {
9600                 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9601                 if (status == MagickFalse)
9602                   break;
9603               }
9604           }
9605         }
9606     }
9607
9608   if (quantum_info != (QuantumInfo *) NULL)
9609     quantum_info=DestroyQuantumInfo(quantum_info);
9610
9611   if (logging != MagickFalse)
9612     {
9613       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9614         "  Wrote PNG image data");
9615
9616       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9617         "    Width: %.20g",(double) ping_width);
9618
9619       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9620         "    Height: %.20g",(double) ping_height);
9621
9622       if (mng_info->write_png_depth)
9623         {
9624           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9625             "    Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9626         }
9627
9628       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9629         "    PNG bit-depth written: %d",ping_bit_depth);
9630
9631       if (mng_info->write_png_colortype)
9632         {
9633           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9634             "    Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9635         }
9636
9637       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9638         "    PNG color-type written: %d",ping_color_type);
9639
9640       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9641         "    PNG Interlace method: %d",ping_interlace_method);
9642     }
9643   /*
9644     Generate text chunks.
9645   */
9646   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
9647   {
9648     ResetImagePropertyIterator(image);
9649     property=GetNextImageProperty(image);
9650     while (property != (const char *) NULL)
9651     {
9652       png_textp
9653         text;
9654
9655       value=GetImageProperty(image,property);
9656       if (ping_exclude_pHYs != MagickFalse       ||
9657           LocaleCompare(property,"density") != 0 ||
9658           LocaleCompare(property,"units") != 0)
9659         {
9660         if (value != (const char *) NULL)
9661           {
9662             text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9663             text[0].key=(char *) property;
9664             text[0].text=(char *) value;
9665             text[0].text_length=strlen(value);
9666
9667             if (ping_exclude_tEXt != MagickFalse)
9668                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9669
9670             else if (ping_exclude_zTXt != MagickFalse)
9671                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9672
9673             else
9674             {
9675                text[0].compression=image_info->compression == NoCompression ||
9676                  (image_info->compression == UndefinedCompression &&
9677                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9678                  PNG_TEXT_COMPRESSION_zTXt ;
9679             }
9680
9681             if (logging != MagickFalse)
9682               {
9683                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9684                   "  Setting up text chunk");
9685
9686                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9687                   "    keyword: %s",text[0].key);
9688               }
9689
9690             png_set_text(ping,ping_info,text,1);
9691             png_free(ping,text);
9692           }
9693         }
9694       property=GetNextImageProperty(image);
9695     }
9696   }
9697
9698   /* write any PNG-chunk-e profiles */
9699   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
9700
9701   if (logging != MagickFalse)
9702     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9703       "  Writing PNG end info");
9704
9705   png_write_end(ping,ping_info);
9706
9707   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9708     {
9709       if (mng_info->page.x || mng_info->page.y ||
9710           (ping_width != mng_info->page.width) ||
9711           (ping_height != mng_info->page.height))
9712         {
9713           unsigned char
9714             chunk[32];
9715
9716           /*
9717             Write FRAM 4 with clipping boundaries followed by FRAM 1.
9718           */
9719           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
9720           PNGType(chunk,mng_FRAM);
9721           LogPNGChunk(logging,mng_FRAM,27L);
9722           chunk[4]=4;
9723           chunk[5]=0;  /* frame name separator (no name) */
9724           chunk[6]=1;  /* flag for changing delay, for next frame only */
9725           chunk[7]=0;  /* flag for changing frame timeout */
9726           chunk[8]=1;  /* flag for changing frame clipping for next frame */
9727           chunk[9]=0;  /* flag for changing frame sync_id */
9728           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9729           chunk[14]=0; /* clipping boundaries delta type */
9730           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9731           PNGLong(chunk+19,
9732              (png_uint_32) (mng_info->page.x + ping_width));
9733           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9734           PNGLong(chunk+27,
9735              (png_uint_32) (mng_info->page.y + ping_height));
9736           (void) WriteBlob(image,31,chunk);
9737           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9738           mng_info->old_framing_mode=4;
9739           mng_info->framing_mode=1;
9740         }
9741
9742       else
9743         mng_info->framing_mode=3;
9744     }
9745   if (mng_info->write_mng && !mng_info->need_fram &&
9746       ((int) image->dispose == 3))
9747      (void) ThrowMagickException(&image->exception,GetMagickModule(),
9748        CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
9749        "`%s'",image->filename);
9750
9751   /*
9752     Free PNG resources.
9753   */
9754
9755   png_destroy_write_struct(&ping,&ping_info);
9756
9757   ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9758
9759 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9760   UnlockSemaphoreInfo(ping_semaphore);
9761 #endif
9762
9763   if (ping_have_blob != MagickFalse)
9764      (void) CloseBlob(image);
9765
9766   image_info=DestroyImageInfo(image_info);
9767   image=DestroyImage(image);
9768
9769   /* Store bit depth actually written */
9770   s[0]=(char) ping_bit_depth;
9771   s[1]='\0';
9772
9773   (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9774
9775   if (logging != MagickFalse)
9776     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9777       "  exit WriteOnePNGImage()");
9778
9779   return(MagickTrue);
9780 /*  End write one PNG image */
9781 }
9782
9783 /*
9784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9785 %                                                                             %
9786 %                                                                             %
9787 %                                                                             %
9788 %   W r i t e P N G I m a g e                                                 %
9789 %                                                                             %
9790 %                                                                             %
9791 %                                                                             %
9792 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9793 %
9794 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
9795 %  Multiple-image Network Graphics (MNG) image file.
9796 %
9797 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
9798 %
9799 %  The format of the WritePNGImage method is:
9800 %
9801 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9802 %
9803 %  A description of each parameter follows:
9804 %
9805 %    o image_info: the image info.
9806 %
9807 %    o image:  The image.
9808 %
9809 %  Returns MagickTrue on success, MagickFalse on failure.
9810 %
9811 %  Communicating with the PNG encoder:
9812 %
9813 %  While the datastream written is always in PNG format and normally would
9814 %  be given the "png" file extension, this method also writes the following
9815 %  pseudo-formats which are subsets of PNG:
9816 %
9817 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
9818 %               a depth greater than 8, the depth is reduced. If transparency
9819 %               is present, the tRNS chunk must only have values 0 and 255
9820 %               (i.e., transparency is binary: fully opaque or fully
9821 %               transparent).  If other values are present they will be
9822 %               50%-thresholded to binary transparency.  If more than 256
9823 %               colors are present, they will be quantized to the 3-3-2
9824 %               palette.  If you want better quantization or dithering of
9825 %               the colors or alpha, you need to do it before calling the
9826 %               PNG encoder. The pixels contain 8-bit indices even if
9827 %               they could be represented with 1, 2, or 4 bits.  Grayscale
9828 %               images will be written as indexed PNG files even though the
9829 %               PNG grayscale type might be slightly more efficient.  Please
9830 %               note that writing to the PNG8 format may result in loss
9831 %               of color and alpha data.
9832 %
9833 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
9834 %               chunk can be present to convey binary transparency by naming
9835 %               one of the colors as transparent.  The only loss incurred
9836 %               is reduction of sample depth to 8.  If the image has more
9837 %               than one transparent color, has semitransparent pixels, or
9838 %               has an opaque pixel with the same RGB components as the
9839 %               transparent color, an image is not written.
9840 %
9841 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
9842 %               transparency is permitted, i.e., the alpha sample for
9843 %               each pixel can have any value from 0 to 255. The alpha
9844 %               channel is present even if the image is fully opaque.
9845 %               The only loss in data is the reduction of the sample depth
9846 %               to 8.
9847 %
9848 %    o -define: For more precise control of the PNG output, you can use the
9849 %               Image options "png:bit-depth" and "png:color-type".  These
9850 %               can be set from the commandline with "-define" and also
9851 %               from the application programming interfaces.  The options
9852 %               are case-independent and are converted to lowercase before
9853 %               being passed to this encoder.
9854 %
9855 %               png:color-type can be 0, 2, 3, 4, or 6.
9856 %
9857 %               When png:color-type is 0 (Grayscale), png:bit-depth can
9858 %               be 1, 2, 4, 8, or 16.
9859 %
9860 %               When png:color-type is 2 (RGB), png:bit-depth can
9861 %               be 8 or 16.
9862 %
9863 %               When png:color-type is 3 (Indexed), png:bit-depth can
9864 %               be 1, 2, 4, or 8.  This refers to the number of bits
9865 %               used to store the index.  The color samples always have
9866 %               bit-depth 8 in indexed PNG files.
9867 %
9868 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
9869 %               png:bit-depth can be 8 or 16.
9870 %
9871 %  If the image cannot be written without loss with the requested bit-depth
9872 %  and color-type, a PNG file will not be written, and the encoder will
9873 %  return MagickFalse.
9874 %
9875 %  Since image encoders should not be responsible for the "heavy lifting",
9876 %  the user should make sure that ImageMagick has already reduced the
9877 %  image depth and number of colors and limit transparency to binary
9878 %  transparency prior to attempting to write the image with depth, color,
9879 %   or transparency limitations.
9880 %
9881 %  TODO: Enforce the previous paragraph.
9882 %
9883 %  Note that another definition, "png:bit-depth-written" exists, but it
9884 %  is not intended for external use.  It is only used internally by the
9885 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
9886 %
9887 %  It is possible to request that the PNG encoder write previously-formatted
9888 %  ancillary chunks in the output PNG file, using the "-profile" commandline
9889 %  option as shown below or by setting the profile via a programming
9890 %  interface:
9891 %
9892 %     -profile PNG-chunk-x:<file>
9893 %
9894 %  where x is a location flag and <file> is a file containing the chunk
9895 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
9896 %  This encoder will compute the chunk length and CRC, so those must not
9897 %  be included in the file.
9898 %
9899 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
9900 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
9901 %  of the same type, then add a short unique string after the "x" to prevent
9902 %  subsequent profiles from overwriting the preceding ones, e.g.,
9903 %
9904 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
9905 %
9906 %  As of version 6.6.6 the following optimizations are always done:
9907 %
9908 %   o  32-bit depth is reduced to 16.
9909 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
9910 %      high byte and low byte are identical.
9911 %   o  Palette is sorted to remove unused entries and to put a
9912 %      transparent color first, if BUILD_PNG_PALETTE is defined.
9913 %   o  Opaque matte channel is removed when writing an indexed PNG.
9914 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
9915 %      this can be done without loss and a larger bit depth N was not
9916 %      requested via the "-define PNG:bit-depth=N" option.
9917 %   o  If matte channel is present but only one transparent color is
9918 %      present, RGB+tRNS is written instead of RGBA
9919 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
9920 %      was requested when converting an opaque image).
9921 %
9922 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9923 */
9924 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
9925   Image *image)
9926 {
9927   MagickBooleanType
9928     excluding,
9929     logging,
9930     have_mng_structure,
9931     status;
9932
9933   MngInfo
9934     *mng_info;
9935
9936   const char
9937     *value;
9938
9939   int
9940     i,
9941     source;
9942
9943   /*
9944     Open image file.
9945   */
9946   assert(image_info != (const ImageInfo *) NULL);
9947   assert(image_info->signature == MagickSignature);
9948   assert(image != (Image *) NULL);
9949   assert(image->signature == MagickSignature);
9950   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
9951   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
9952   /*
9953     Allocate a MngInfo structure.
9954   */
9955   have_mng_structure=MagickFalse;
9956   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
9957
9958   if (mng_info == (MngInfo *) NULL)
9959     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9960
9961   /*
9962     Initialize members of the MngInfo structure.
9963   */
9964   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
9965   mng_info->image=image;
9966   mng_info->equal_backgrounds=MagickTrue;
9967   have_mng_structure=MagickTrue;
9968
9969   /* See if user has requested a specific PNG subformat */
9970
9971   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
9972   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
9973   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
9974
9975   if (mng_info->write_png8)
9976     {
9977       mng_info->write_png_colortype = /* 3 */ 4;
9978       mng_info->write_png_depth = 8;
9979       image->depth = 8;
9980     }
9981
9982   if (mng_info->write_png24)
9983     {
9984       mng_info->write_png_colortype = /* 2 */ 3;
9985       mng_info->write_png_depth = 8;
9986       image->depth = 8;
9987
9988       if (image->matte == MagickTrue)
9989         (void) SetImageType(image,TrueColorMatteType);
9990
9991       else
9992         (void) SetImageType(image,TrueColorType);
9993
9994       (void) SyncImage(image);
9995     }
9996
9997   if (mng_info->write_png32)
9998     {
9999       mng_info->write_png_colortype = /* 6 */  7;
10000       mng_info->write_png_depth = 8;
10001       image->depth = 8;
10002
10003       if (image->matte == MagickTrue)
10004         (void) SetImageType(image,TrueColorMatteType);
10005
10006       else
10007         (void) SetImageType(image,TrueColorType);
10008
10009       (void) SyncImage(image);
10010     }
10011
10012   value=GetImageOption(image_info,"png:bit-depth");
10013
10014   if (value != (char *) NULL)
10015     {
10016       if (LocaleCompare(value,"1") == 0)
10017         mng_info->write_png_depth = 1;
10018
10019       else if (LocaleCompare(value,"2") == 0)
10020         mng_info->write_png_depth = 2;
10021
10022       else if (LocaleCompare(value,"4") == 0)
10023         mng_info->write_png_depth = 4;
10024
10025       else if (LocaleCompare(value,"8") == 0)
10026         mng_info->write_png_depth = 8;
10027
10028       else if (LocaleCompare(value,"16") == 0)
10029         mng_info->write_png_depth = 16;
10030
10031       else
10032         (void) ThrowMagickException(&image->exception,
10033              GetMagickModule(),CoderWarning,
10034              "ignoring invalid defined png:bit-depth",
10035              "=%s",value);
10036
10037       if (logging != MagickFalse)
10038         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10039           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
10040     }
10041
10042   value=GetImageOption(image_info,"png:color-type");
10043
10044   if (value != (char *) NULL)
10045     {
10046       /* We must store colortype+1 because 0 is a valid colortype */
10047       if (LocaleCompare(value,"0") == 0)
10048         mng_info->write_png_colortype = 1;
10049
10050       else if (LocaleCompare(value,"2") == 0)
10051         mng_info->write_png_colortype = 3;
10052
10053       else if (LocaleCompare(value,"3") == 0)
10054         mng_info->write_png_colortype = 4;
10055
10056       else if (LocaleCompare(value,"4") == 0)
10057         mng_info->write_png_colortype = 5;
10058
10059       else if (LocaleCompare(value,"6") == 0)
10060         mng_info->write_png_colortype = 7;
10061
10062       else
10063         (void) ThrowMagickException(&image->exception,
10064              GetMagickModule(),CoderWarning,
10065              "ignoring invalid defined png:color-type",
10066              "=%s",value);
10067
10068       if (logging != MagickFalse)
10069         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10070           "  png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
10071     }
10072
10073   /* Check for chunks to be excluded:
10074    *
10075    * The default is to not exclude any known chunks except for any
10076    * listed in the "unused_chunks" array, above.
10077    *
10078    * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10079    * define (in the image properties or in the image artifacts)
10080    * or via a mng_info member.  For convenience, in addition
10081    * to or instead of a comma-separated list of chunks, the
10082    * "exclude-chunk" string can be simply "all" or "none".
10083    *
10084    * The exclude-chunk define takes priority over the mng_info.
10085    *
10086    * A "PNG:include-chunk" define takes  priority over both the
10087    * mng_info and the "PNG:exclude-chunk" define.  Like the
10088    * "exclude-chunk" string, it can define "all" or "none" as
10089    * well as a comma-separated list.  Chunks that are unknown to
10090    * ImageMagick are always excluded, regardless of their "copy-safe"
10091    * status according to the PNG specification, and even if they
10092    * appear in the "include-chunk" list.
10093    *
10094    * Finally, all chunks listed in the "unused_chunks" array are
10095    * automatically excluded, regardless of the other instructions
10096    * or lack thereof.
10097    *
10098    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10099    * will not be written and the gAMA chunk will only be written if it
10100    * is not between .45 and .46, or approximately (1.0/2.2).
10101    *
10102    * If you exclude tRNS and the image has transparency, the colortype
10103    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10104    *
10105    * The -strip option causes StripImage() to set the png:include-chunk
10106    * artifact to "none,gama".
10107    */
10108
10109   mng_info->ping_exclude_bKGD=MagickFalse;
10110   mng_info->ping_exclude_cHRM=MagickFalse;
10111   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10112   mng_info->ping_exclude_gAMA=MagickFalse;
10113   mng_info->ping_exclude_cHRM=MagickFalse;
10114   mng_info->ping_exclude_iCCP=MagickFalse;
10115   /* mng_info->ping_exclude_iTXt=MagickFalse; */
10116   mng_info->ping_exclude_oFFs=MagickFalse;
10117   mng_info->ping_exclude_pHYs=MagickFalse;
10118   mng_info->ping_exclude_sRGB=MagickFalse;
10119   mng_info->ping_exclude_tEXt=MagickFalse;
10120   mng_info->ping_exclude_tRNS=MagickFalse;
10121   mng_info->ping_exclude_vpAg=MagickFalse;
10122   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10123   mng_info->ping_exclude_zTXt=MagickFalse;
10124
10125   excluding=MagickFalse;
10126
10127   for (source=0; source<1; source++)
10128   {
10129     if (source==0)
10130       {
10131        value=GetImageArtifact(image,"png:exclude-chunk");
10132
10133        if (value == NULL)
10134          value=GetImageArtifact(image,"png:exclude-chunks");
10135       }
10136     else
10137       {
10138        value=GetImageOption(image_info,"png:exclude-chunk");
10139
10140        if (value == NULL)
10141          value=GetImageOption(image_info,"png:exclude-chunks");
10142       }
10143
10144     if (value != NULL)
10145     {
10146
10147     size_t
10148       last;
10149
10150     excluding=MagickTrue;
10151
10152     if (logging != MagickFalse)
10153       {
10154         if (source == 0)
10155            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10156               "  png:exclude-chunk=%s found in image artifacts.\n", value);
10157         else
10158            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10159               "  png:exclude-chunk=%s found in image properties.\n", value);
10160       }
10161
10162     last=strlen(value);
10163
10164     for (i=0; i<(int) last; i+=5)
10165     {
10166
10167       if (LocaleNCompare(value+i,"all",3) == 0)
10168       {
10169         mng_info->ping_exclude_bKGD=MagickTrue;
10170         mng_info->ping_exclude_cHRM=MagickTrue;
10171         mng_info->ping_exclude_EXIF=MagickTrue;
10172         mng_info->ping_exclude_gAMA=MagickTrue;
10173         mng_info->ping_exclude_iCCP=MagickTrue;
10174         /* mng_info->ping_exclude_iTXt=MagickTrue; */
10175         mng_info->ping_exclude_oFFs=MagickTrue;
10176         mng_info->ping_exclude_pHYs=MagickTrue;
10177         mng_info->ping_exclude_sRGB=MagickTrue;
10178         mng_info->ping_exclude_tEXt=MagickTrue;
10179         mng_info->ping_exclude_tRNS=MagickTrue;
10180         mng_info->ping_exclude_vpAg=MagickTrue;
10181         mng_info->ping_exclude_zCCP=MagickTrue;
10182         mng_info->ping_exclude_zTXt=MagickTrue;
10183         i--;
10184       }
10185
10186       if (LocaleNCompare(value+i,"none",4) == 0)
10187       {
10188         mng_info->ping_exclude_bKGD=MagickFalse;
10189         mng_info->ping_exclude_cHRM=MagickFalse;
10190         mng_info->ping_exclude_EXIF=MagickFalse;
10191         mng_info->ping_exclude_gAMA=MagickFalse;
10192         mng_info->ping_exclude_iCCP=MagickFalse;
10193         /* mng_info->ping_exclude_iTXt=MagickFalse; */
10194         mng_info->ping_exclude_oFFs=MagickFalse;
10195         mng_info->ping_exclude_pHYs=MagickFalse;
10196         mng_info->ping_exclude_sRGB=MagickFalse;
10197         mng_info->ping_exclude_tEXt=MagickFalse;
10198         mng_info->ping_exclude_tRNS=MagickFalse;
10199         mng_info->ping_exclude_vpAg=MagickFalse;
10200         mng_info->ping_exclude_zCCP=MagickFalse;
10201         mng_info->ping_exclude_zTXt=MagickFalse;
10202       }
10203
10204       if (LocaleNCompare(value+i,"bkgd",4) == 0)
10205         mng_info->ping_exclude_bKGD=MagickTrue;
10206
10207       if (LocaleNCompare(value+i,"chrm",4) == 0)
10208         mng_info->ping_exclude_cHRM=MagickTrue;
10209
10210       if (LocaleNCompare(value+i,"exif",4) == 0)
10211         mng_info->ping_exclude_EXIF=MagickTrue;
10212
10213       if (LocaleNCompare(value+i,"gama",4) == 0)
10214         mng_info->ping_exclude_gAMA=MagickTrue;
10215
10216       if (LocaleNCompare(value+i,"iccp",4) == 0)
10217         mng_info->ping_exclude_iCCP=MagickTrue;
10218
10219     /*
10220       if (LocaleNCompare(value+i,"itxt",4) == 0)
10221         mng_info->ping_exclude_iTXt=MagickTrue;
10222      */
10223
10224       if (LocaleNCompare(value+i,"gama",4) == 0)
10225         mng_info->ping_exclude_gAMA=MagickTrue;
10226
10227       if (LocaleNCompare(value+i,"offs",4) == 0)
10228         mng_info->ping_exclude_oFFs=MagickTrue;
10229
10230       if (LocaleNCompare(value+i,"phys",4) == 0)
10231         mng_info->ping_exclude_pHYs=MagickTrue;
10232
10233       if (LocaleNCompare(value+i,"srgb",4) == 0)
10234         mng_info->ping_exclude_sRGB=MagickTrue;
10235
10236       if (LocaleNCompare(value+i,"text",4) == 0)
10237         mng_info->ping_exclude_tEXt=MagickTrue;
10238
10239       if (LocaleNCompare(value+i,"trns",4) == 0)
10240         mng_info->ping_exclude_tRNS=MagickTrue;
10241
10242       if (LocaleNCompare(value+i,"vpag",4) == 0)
10243         mng_info->ping_exclude_vpAg=MagickTrue;
10244
10245       if (LocaleNCompare(value+i,"zccp",4) == 0)
10246         mng_info->ping_exclude_zCCP=MagickTrue;
10247
10248       if (LocaleNCompare(value+i,"ztxt",4) == 0)
10249         mng_info->ping_exclude_zTXt=MagickTrue;
10250
10251       }
10252     }
10253   }
10254
10255   for (source=0; source<1; source++)
10256   {
10257     if (source==0)
10258       {
10259        value=GetImageArtifact(image,"png:include-chunk");
10260
10261        if (value == NULL)
10262          value=GetImageArtifact(image,"png:include-chunks");
10263       }
10264     else
10265       {
10266        value=GetImageOption(image_info,"png:include-chunk");
10267
10268        if (value == NULL)
10269          value=GetImageOption(image_info,"png:include-chunks");
10270       }
10271
10272     if (value != NULL)
10273     {
10274     size_t
10275       last;
10276
10277     excluding=MagickTrue;
10278
10279     if (logging != MagickFalse)
10280       {
10281         if (source == 0)
10282            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10283               "  png:include-chunk=%s found in image artifacts.\n", value);
10284         else
10285            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10286               "  png:include-chunk=%s found in image properties.\n", value);
10287       }
10288
10289     last=strlen(value);
10290
10291     for (i=0; i<(int) last; i+=5)
10292       {
10293       if (LocaleNCompare(value+i,"all",3) == 0)
10294         {
10295           mng_info->ping_exclude_bKGD=MagickFalse;
10296           mng_info->ping_exclude_cHRM=MagickFalse;
10297           mng_info->ping_exclude_EXIF=MagickFalse;
10298           mng_info->ping_exclude_gAMA=MagickFalse;
10299           mng_info->ping_exclude_iCCP=MagickFalse;
10300           /* mng_info->ping_exclude_iTXt=MagickFalse; */
10301           mng_info->ping_exclude_oFFs=MagickFalse;
10302           mng_info->ping_exclude_pHYs=MagickFalse;
10303           mng_info->ping_exclude_sRGB=MagickFalse;
10304           mng_info->ping_exclude_tEXt=MagickFalse;
10305           mng_info->ping_exclude_tRNS=MagickFalse;
10306           mng_info->ping_exclude_vpAg=MagickFalse;
10307           mng_info->ping_exclude_zCCP=MagickFalse;
10308           mng_info->ping_exclude_zTXt=MagickFalse;
10309           i--;
10310         }
10311
10312       if (LocaleNCompare(value+i,"none",4) == 0)
10313         {
10314           mng_info->ping_exclude_bKGD=MagickTrue;
10315           mng_info->ping_exclude_cHRM=MagickTrue;
10316           mng_info->ping_exclude_EXIF=MagickTrue;
10317           mng_info->ping_exclude_gAMA=MagickTrue;
10318           mng_info->ping_exclude_iCCP=MagickTrue;
10319           /* mng_info->ping_exclude_iTXt=MagickTrue; */
10320           mng_info->ping_exclude_oFFs=MagickTrue;
10321           mng_info->ping_exclude_pHYs=MagickTrue;
10322           mng_info->ping_exclude_sRGB=MagickTrue;
10323           mng_info->ping_exclude_tEXt=MagickTrue;
10324           mng_info->ping_exclude_tRNS=MagickTrue;
10325           mng_info->ping_exclude_vpAg=MagickTrue;
10326           mng_info->ping_exclude_zCCP=MagickTrue;
10327           mng_info->ping_exclude_zTXt=MagickTrue;
10328         }
10329
10330       if (LocaleNCompare(value+i,"bkgd",4) == 0)
10331         mng_info->ping_exclude_bKGD=MagickFalse;
10332
10333       if (LocaleNCompare(value+i,"chrm",4) == 0)
10334         mng_info->ping_exclude_cHRM=MagickFalse;
10335
10336       if (LocaleNCompare(value+i,"exif",4) == 0)
10337         mng_info->ping_exclude_EXIF=MagickFalse;
10338
10339       if (LocaleNCompare(value+i,"gama",4) == 0)
10340         mng_info->ping_exclude_gAMA=MagickFalse;
10341
10342       if (LocaleNCompare(value+i,"iccp",4) == 0)
10343         mng_info->ping_exclude_iCCP=MagickFalse;
10344
10345     /*
10346       if (LocaleNCompare(value+i,"itxt",4) == 0)
10347         mng_info->ping_exclude_iTXt=MagickFalse;
10348      */
10349
10350       if (LocaleNCompare(value+i,"gama",4) == 0)
10351         mng_info->ping_exclude_gAMA=MagickFalse;
10352
10353       if (LocaleNCompare(value+i,"offs",4) == 0)
10354         mng_info->ping_exclude_oFFs=MagickFalse;
10355
10356       if (LocaleNCompare(value+i,"phys",4) == 0)
10357         mng_info->ping_exclude_pHYs=MagickFalse;
10358
10359       if (LocaleNCompare(value+i,"srgb",4) == 0)
10360         mng_info->ping_exclude_sRGB=MagickFalse;
10361
10362       if (LocaleNCompare(value+i,"text",4) == 0)
10363         mng_info->ping_exclude_tEXt=MagickFalse;
10364
10365       if (LocaleNCompare(value+i,"trns",4) == 0)
10366         mng_info->ping_exclude_tRNS=MagickFalse;
10367
10368       if (LocaleNCompare(value+i,"vpag",4) == 0)
10369         mng_info->ping_exclude_vpAg=MagickFalse;
10370
10371       if (LocaleNCompare(value+i,"zccp",4) == 0)
10372         mng_info->ping_exclude_zCCP=MagickFalse;
10373
10374       if (LocaleNCompare(value+i,"ztxt",4) == 0)
10375         mng_info->ping_exclude_zTXt=MagickFalse;
10376
10377       }
10378     }
10379   }
10380
10381   if (excluding != MagickFalse && logging != MagickFalse)
10382   {
10383     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10384       "  Chunks to be excluded from the output PNG:");
10385     if (mng_info->ping_exclude_bKGD != MagickFalse)
10386       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10387           "    bKGD");
10388     if (mng_info->ping_exclude_cHRM != MagickFalse)
10389       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10390           "    cHRM");
10391     if (mng_info->ping_exclude_EXIF != MagickFalse)
10392       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10393           "    EXIF");
10394     if (mng_info->ping_exclude_gAMA != MagickFalse)
10395       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10396           "    gAMA");
10397     if (mng_info->ping_exclude_iCCP != MagickFalse)
10398       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10399           "    iCCP");
10400 /*
10401     if (mng_info->ping_exclude_iTXt != MagickFalse)
10402       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10403           "    iTXt");
10404 */
10405     if (mng_info->ping_exclude_oFFs != MagickFalse)
10406       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10407           "    oFFs");
10408     if (mng_info->ping_exclude_pHYs != MagickFalse)
10409       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10410           "    pHYs");
10411     if (mng_info->ping_exclude_sRGB != MagickFalse)
10412       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10413           "    sRGB");
10414     if (mng_info->ping_exclude_tEXt != MagickFalse)
10415       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10416           "    tEXt");
10417     if (mng_info->ping_exclude_tRNS != MagickFalse)
10418       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10419           "    tRNS");
10420     if (mng_info->ping_exclude_vpAg != MagickFalse)
10421       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10422           "    vpAg");
10423     if (mng_info->ping_exclude_zCCP != MagickFalse)
10424       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10425           "    zCCP");
10426     if (mng_info->ping_exclude_zTXt != MagickFalse)
10427       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10428           "    zTXt");
10429   }
10430
10431   mng_info->need_blob = MagickTrue;
10432
10433   status=WriteOnePNGImage(mng_info,image_info,image);
10434
10435   MngInfoFreeStruct(mng_info,&have_mng_structure);
10436
10437   if (logging != MagickFalse)
10438     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
10439
10440   return(status);
10441 }
10442
10443 #if defined(JNG_SUPPORTED)
10444
10445 /* Write one JNG image */
10446 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10447    const ImageInfo *image_info,Image *image)
10448 {
10449   Image
10450     *jpeg_image;
10451
10452   ImageInfo
10453     *jpeg_image_info;
10454
10455   MagickBooleanType
10456     logging,
10457     status;
10458
10459   size_t
10460     length;
10461
10462   unsigned char
10463     *blob,
10464     chunk[80],
10465     *p;
10466
10467   unsigned int
10468     jng_alpha_compression_method,
10469     jng_alpha_sample_depth,
10470     jng_color_type,
10471     transparent;
10472
10473   size_t
10474     jng_quality;
10475
10476   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
10477     "  Enter WriteOneJNGImage()");
10478
10479   blob=(unsigned char *) NULL;
10480   jpeg_image=(Image *) NULL;
10481   jpeg_image_info=(ImageInfo *) NULL;
10482
10483   status=MagickTrue;
10484   transparent=image_info->type==GrayscaleMatteType ||
10485      image_info->type==TrueColorMatteType;
10486   jng_color_type=10;
10487   jng_alpha_sample_depth=0;
10488   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10489   jng_alpha_compression_method=0;
10490
10491   if (image->matte != MagickFalse)
10492     {
10493       /* if any pixels are transparent */
10494       transparent=MagickTrue;
10495       if (image_info->compression==JPEGCompression)
10496         jng_alpha_compression_method=8;
10497     }
10498
10499   if (transparent)
10500     {
10501       jng_color_type=14;
10502
10503       /* Create JPEG blob, image, and image_info */
10504       if (logging != MagickFalse)
10505         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10506           "  Creating jpeg_image_info for opacity.");
10507
10508       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10509
10510       if (jpeg_image_info == (ImageInfo *) NULL)
10511         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10512
10513       if (logging != MagickFalse)
10514         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10515           "  Creating jpeg_image.");
10516
10517       jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10518
10519       if (jpeg_image == (Image *) NULL)
10520         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10521
10522       (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10523       status=SeparateImageChannel(jpeg_image,OpacityChannel);
10524       status=NegateImage(jpeg_image,MagickFalse);
10525       jpeg_image->matte=MagickFalse;
10526
10527       if (jng_quality >= 1000)
10528         jpeg_image_info->quality=jng_quality/1000;
10529
10530       else
10531         jpeg_image_info->quality=jng_quality;
10532
10533       jpeg_image_info->type=GrayscaleType;
10534       (void) SetImageType(jpeg_image,GrayscaleType);
10535       (void) AcquireUniqueFilename(jpeg_image->filename);
10536       (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10537         "%s",jpeg_image->filename);
10538     }
10539
10540   /* To do: check bit depth of PNG alpha channel */
10541
10542   /* Check if image is grayscale. */
10543   if (image_info->type != TrueColorMatteType && image_info->type !=
10544     TrueColorType && ImageIsGray(image))
10545     jng_color_type-=2;
10546
10547   if (transparent)
10548     {
10549       if (jng_alpha_compression_method==0)
10550         {
10551           const char
10552             *value;
10553
10554           /* Encode opacity as a grayscale PNG blob */
10555           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10556             &image->exception);
10557           if (logging != MagickFalse)
10558             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10559               "  Creating PNG blob.");
10560           length=0;
10561
10562           (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10563           (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10564           jpeg_image_info->interlace=NoInterlace;
10565
10566           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10567             &image->exception);
10568
10569           /* Retrieve sample depth used */
10570           value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10571           if (value != (char *) NULL)
10572             jng_alpha_sample_depth= (unsigned int) value[0];
10573         }
10574       else
10575         {
10576           /* Encode opacity as a grayscale JPEG blob */
10577
10578           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10579             &image->exception);
10580
10581           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10582           (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10583           jpeg_image_info->interlace=NoInterlace;
10584           if (logging != MagickFalse)
10585             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10586               "  Creating blob.");
10587           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10588            &image->exception);
10589           jng_alpha_sample_depth=8;
10590
10591           if (logging != MagickFalse)
10592             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10593               "  Successfully read jpeg_image into a blob, length=%.20g.",
10594               (double) length);
10595
10596         }
10597       /* Destroy JPEG image and image_info */
10598       jpeg_image=DestroyImage(jpeg_image);
10599       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10600       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10601     }
10602
10603   /* Write JHDR chunk */
10604   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
10605   PNGType(chunk,mng_JHDR);
10606   LogPNGChunk(logging,mng_JHDR,16L);
10607   PNGLong(chunk+4,(png_uint_32) image->columns);
10608   PNGLong(chunk+8,(png_uint_32) image->rows);
10609   chunk[12]=jng_color_type;
10610   chunk[13]=8;  /* sample depth */
10611   chunk[14]=8; /*jng_image_compression_method */
10612   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10613   chunk[16]=jng_alpha_sample_depth;
10614   chunk[17]=jng_alpha_compression_method;
10615   chunk[18]=0; /*jng_alpha_filter_method */
10616   chunk[19]=0; /*jng_alpha_interlace_method */
10617   (void) WriteBlob(image,20,chunk);
10618   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10619   if (logging != MagickFalse)
10620     {
10621       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10622         "    JNG width:%15lu",(unsigned long) image->columns);
10623
10624       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10625         "    JNG height:%14lu",(unsigned long) image->rows);
10626
10627       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10628         "    JNG color type:%10d",jng_color_type);
10629
10630       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10631         "    JNG sample depth:%8d",8);
10632
10633       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10634         "    JNG compression:%9d",8);
10635
10636       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10637         "    JNG interlace:%11d",0);
10638
10639       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10640         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
10641
10642       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10643         "    JNG alpha compression:%3d",jng_alpha_compression_method);
10644
10645       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10646         "    JNG alpha filter:%8d",0);
10647
10648       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10649         "    JNG alpha interlace:%5d",0);
10650     }
10651
10652   /* Write any JNG-chunk-b profiles */
10653   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
10654
10655   /*
10656      Write leading ancillary chunks
10657   */
10658
10659   if (transparent)
10660   {
10661     /*
10662       Write JNG bKGD chunk
10663     */
10664
10665     unsigned char
10666       blue,
10667       green,
10668       red;
10669
10670     ssize_t
10671       num_bytes;
10672
10673     if (jng_color_type == 8 || jng_color_type == 12)
10674       num_bytes=6L;
10675     else
10676       num_bytes=10L;
10677     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
10678     PNGType(chunk,mng_bKGD);
10679     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
10680     red=ScaleQuantumToChar(image->background_color.red);
10681     green=ScaleQuantumToChar(image->background_color.green);
10682     blue=ScaleQuantumToChar(image->background_color.blue);
10683     *(chunk+4)=0;
10684     *(chunk+5)=red;
10685     *(chunk+6)=0;
10686     *(chunk+7)=green;
10687     *(chunk+8)=0;
10688     *(chunk+9)=blue;
10689     (void) WriteBlob(image,(size_t) num_bytes,chunk);
10690     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10691   }
10692
10693   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10694     {
10695       /*
10696         Write JNG sRGB chunk
10697       */
10698       (void) WriteBlobMSBULong(image,1L);
10699       PNGType(chunk,mng_sRGB);
10700       LogPNGChunk(logging,mng_sRGB,1L);
10701
10702       if (image->rendering_intent != UndefinedIntent)
10703         chunk[4]=(unsigned char)
10704           Magick_RenderingIntent_to_PNG_RenderingIntent(
10705           (image->rendering_intent));
10706
10707       else
10708         chunk[4]=(unsigned char)
10709           Magick_RenderingIntent_to_PNG_RenderingIntent(
10710           (PerceptualIntent));
10711
10712       (void) WriteBlob(image,5,chunk);
10713       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10714     }
10715   else
10716     {
10717       if (image->gamma != 0.0)
10718         {
10719           /*
10720              Write JNG gAMA chunk
10721           */
10722           (void) WriteBlobMSBULong(image,4L);
10723           PNGType(chunk,mng_gAMA);
10724           LogPNGChunk(logging,mng_gAMA,4L);
10725           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10726           (void) WriteBlob(image,8,chunk);
10727           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10728         }
10729
10730       if ((mng_info->equal_chrms == MagickFalse) &&
10731           (image->chromaticity.red_primary.x != 0.0))
10732         {
10733           PrimaryInfo
10734             primary;
10735
10736           /*
10737              Write JNG cHRM chunk
10738           */
10739           (void) WriteBlobMSBULong(image,32L);
10740           PNGType(chunk,mng_cHRM);
10741           LogPNGChunk(logging,mng_cHRM,32L);
10742           primary=image->chromaticity.white_point;
10743           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10744           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
10745           primary=image->chromaticity.red_primary;
10746           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10747           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
10748           primary=image->chromaticity.green_primary;
10749           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10750           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
10751           primary=image->chromaticity.blue_primary;
10752           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10753           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
10754           (void) WriteBlob(image,36,chunk);
10755           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10756         }
10757     }
10758
10759   if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10760     {
10761       /*
10762          Write JNG pHYs chunk
10763       */
10764       (void) WriteBlobMSBULong(image,9L);
10765       PNGType(chunk,mng_pHYs);
10766       LogPNGChunk(logging,mng_pHYs,9L);
10767       if (image->units == PixelsPerInchResolution)
10768         {
10769           PNGLong(chunk+4,(png_uint_32)
10770             (image->x_resolution*100.0/2.54+0.5));
10771
10772           PNGLong(chunk+8,(png_uint_32)
10773             (image->y_resolution*100.0/2.54+0.5));
10774
10775           chunk[12]=1;
10776         }
10777
10778       else
10779         {
10780           if (image->units == PixelsPerCentimeterResolution)
10781             {
10782               PNGLong(chunk+4,(png_uint_32)
10783                 (image->x_resolution*100.0+0.5));
10784
10785               PNGLong(chunk+8,(png_uint_32)
10786                 (image->y_resolution*100.0+0.5));
10787
10788               chunk[12]=1;
10789             }
10790
10791           else
10792             {
10793               PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10794               PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
10795               chunk[12]=0;
10796             }
10797         }
10798       (void) WriteBlob(image,13,chunk);
10799       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10800     }
10801
10802   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10803     {
10804       /*
10805          Write JNG oFFs chunk
10806       */
10807       (void) WriteBlobMSBULong(image,9L);
10808       PNGType(chunk,mng_oFFs);
10809       LogPNGChunk(logging,mng_oFFs,9L);
10810       PNGsLong(chunk+4,(ssize_t) (image->page.x));
10811       PNGsLong(chunk+8,(ssize_t) (image->page.y));
10812       chunk[12]=0;
10813       (void) WriteBlob(image,13,chunk);
10814       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10815     }
10816   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10817     {
10818        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
10819        PNGType(chunk,mng_vpAg);
10820        LogPNGChunk(logging,mng_vpAg,9L);
10821        PNGLong(chunk+4,(png_uint_32) image->page.width);
10822        PNGLong(chunk+8,(png_uint_32) image->page.height);
10823        chunk[12]=0;   /* unit = pixels */
10824        (void) WriteBlob(image,13,chunk);
10825        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10826     }
10827
10828
10829   if (transparent)
10830     {
10831       if (jng_alpha_compression_method==0)
10832         {
10833           register ssize_t
10834             i;
10835
10836           ssize_t
10837             len;
10838
10839           /* Write IDAT chunk header */
10840           if (logging != MagickFalse)
10841             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10842               "  Write IDAT chunks from blob, length=%.20g.",(double)
10843               length);
10844
10845           /* Copy IDAT chunks */
10846           len=0;
10847           p=blob+8;
10848           for (i=8; i<(ssize_t) length; i+=len+12)
10849           {
10850             len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
10851             p+=4;
10852
10853             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
10854               {
10855                 /* Found an IDAT chunk. */
10856                 (void) WriteBlobMSBULong(image,(size_t) len);
10857                 LogPNGChunk(logging,mng_IDAT,(size_t) len);
10858                 (void) WriteBlob(image,(size_t) len+4,p);
10859                 (void) WriteBlobMSBULong(image,
10860                     crc32(0,p,(uInt) len+4));
10861               }
10862
10863             else
10864               {
10865                 if (logging != MagickFalse)
10866                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10867                     "    Skipping %c%c%c%c chunk, length=%.20g.",
10868                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
10869               }
10870             p+=(8+len);
10871           }
10872         }
10873       else
10874         {
10875           /* Write JDAA chunk header */
10876           if (logging != MagickFalse)
10877             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10878               "  Write JDAA chunk, length=%.20g.",(double) length);
10879           (void) WriteBlobMSBULong(image,(size_t) length);
10880           PNGType(chunk,mng_JDAA);
10881           LogPNGChunk(logging,mng_JDAA,length);
10882           /* Write JDAT chunk(s) data */
10883           (void) WriteBlob(image,4,chunk);
10884           (void) WriteBlob(image,length,blob);
10885           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
10886              (uInt) length));
10887         }
10888       blob=(unsigned char *) RelinquishMagickMemory(blob);
10889     }
10890
10891   /* Encode image as a JPEG blob */
10892   if (logging != MagickFalse)
10893     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10894       "  Creating jpeg_image_info.");
10895   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10896   if (jpeg_image_info == (ImageInfo *) NULL)
10897     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10898
10899   if (logging != MagickFalse)
10900     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10901       "  Creating jpeg_image.");
10902
10903   jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10904   if (jpeg_image == (Image *) NULL)
10905     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10906   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10907
10908   (void) AcquireUniqueFilename(jpeg_image->filename);
10909   (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
10910     jpeg_image->filename);
10911
10912   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10913     &image->exception);
10914
10915   if (logging != MagickFalse)
10916     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10917       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
10918       (double) jpeg_image->rows);
10919
10920   if (jng_color_type == 8 || jng_color_type == 12)
10921     jpeg_image_info->type=GrayscaleType;
10922
10923   jpeg_image_info->quality=jng_quality % 1000;
10924   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10925   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10926
10927   if (logging != MagickFalse)
10928     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10929       "  Creating blob.");
10930
10931   blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
10932
10933   if (logging != MagickFalse)
10934     {
10935       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10936         "  Successfully read jpeg_image into a blob, length=%.20g.",
10937         (double) length);
10938
10939       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10940         "  Write JDAT chunk, length=%.20g.",(double) length);
10941     }
10942
10943   /* Write JDAT chunk(s) */
10944   (void) WriteBlobMSBULong(image,(size_t) length);
10945   PNGType(chunk,mng_JDAT);
10946   LogPNGChunk(logging,mng_JDAT,length);
10947   (void) WriteBlob(image,4,chunk);
10948   (void) WriteBlob(image,length,blob);
10949   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
10950
10951   jpeg_image=DestroyImage(jpeg_image);
10952   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10953   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10954   blob=(unsigned char *) RelinquishMagickMemory(blob);
10955
10956   /* Write any JNG-chunk-e profiles */
10957   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
10958
10959   /* Write IEND chunk */
10960   (void) WriteBlobMSBULong(image,0L);
10961   PNGType(chunk,mng_IEND);
10962   LogPNGChunk(logging,mng_IEND,0);
10963   (void) WriteBlob(image,4,chunk);
10964   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
10965
10966   if (logging != MagickFalse)
10967     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10968       "  exit WriteOneJNGImage()");
10969
10970   return(status);
10971 }
10972
10973
10974 /*
10975 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10976 %                                                                             %
10977 %                                                                             %
10978 %                                                                             %
10979 %   W r i t e J N G I m a g e                                                 %
10980 %                                                                             %
10981 %                                                                             %
10982 %                                                                             %
10983 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10984 %
10985 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
10986 %
10987 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
10988 %
10989 %  The format of the WriteJNGImage method is:
10990 %
10991 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10992 %
10993 %  A description of each parameter follows:
10994 %
10995 %    o image_info: the image info.
10996 %
10997 %    o image:  The image.
10998 %
10999 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11000 */
11001 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11002 {
11003   MagickBooleanType
11004     have_mng_structure,
11005     logging,
11006     status;
11007
11008   MngInfo
11009     *mng_info;
11010
11011   /*
11012     Open image file.
11013   */
11014   assert(image_info != (const ImageInfo *) NULL);
11015   assert(image_info->signature == MagickSignature);
11016   assert(image != (Image *) NULL);
11017   assert(image->signature == MagickSignature);
11018   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11019   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
11020   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11021   if (status == MagickFalse)
11022     return(status);
11023
11024   /*
11025     Allocate a MngInfo structure.
11026   */
11027   have_mng_structure=MagickFalse;
11028   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11029   if (mng_info == (MngInfo *) NULL)
11030     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11031   /*
11032     Initialize members of the MngInfo structure.
11033   */
11034   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11035   mng_info->image=image;
11036   have_mng_structure=MagickTrue;
11037
11038   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11039
11040   status=WriteOneJNGImage(mng_info,image_info,image);
11041   (void) CloseBlob(image);
11042
11043   (void) CatchImageException(image);
11044   MngInfoFreeStruct(mng_info,&have_mng_structure);
11045   if (logging != MagickFalse)
11046     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11047   return(status);
11048 }
11049 #endif
11050
11051
11052
11053 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11054 {
11055   const char
11056     *option;
11057
11058   Image
11059     *next_image;
11060
11061   MagickBooleanType
11062     have_mng_structure,
11063     status;
11064
11065   volatile MagickBooleanType
11066     logging;
11067
11068   MngInfo
11069     *mng_info;
11070
11071   int
11072     image_count,
11073     need_iterations,
11074     need_matte;
11075
11076   volatile int
11077 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11078     defined(PNG_MNG_FEATURES_SUPPORTED)
11079     need_local_plte,
11080 #endif
11081     all_images_are_gray,
11082     need_defi,
11083     use_global_plte;
11084
11085   register ssize_t
11086     i;
11087
11088   unsigned char
11089     chunk[800];
11090
11091   volatile unsigned int
11092     write_jng,
11093     write_mng;
11094
11095   volatile size_t
11096     scene;
11097
11098   size_t
11099     final_delay=0,
11100     initial_delay;
11101
11102 #if (PNG_LIBPNG_VER < 10200)
11103     if (image_info->verbose)
11104       printf("Your PNG library (libpng-%s) is rather old.\n",
11105          PNG_LIBPNG_VER_STRING);
11106 #endif
11107
11108   /*
11109     Open image file.
11110   */
11111   assert(image_info != (const ImageInfo *) NULL);
11112   assert(image_info->signature == MagickSignature);
11113   assert(image != (Image *) NULL);
11114   assert(image->signature == MagickSignature);
11115   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11116   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
11117   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11118   if (status == MagickFalse)
11119     return(status);
11120
11121   /*
11122     Allocate a MngInfo structure.
11123   */
11124   have_mng_structure=MagickFalse;
11125   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11126   if (mng_info == (MngInfo *) NULL)
11127     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11128   /*
11129     Initialize members of the MngInfo structure.
11130   */
11131   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11132   mng_info->image=image;
11133   have_mng_structure=MagickTrue;
11134   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11135
11136   /*
11137    * See if user has requested a specific PNG subformat to be used
11138    * for all of the PNGs in the MNG being written, e.g.,
11139    *
11140    *    convert *.png png8:animation.mng
11141    *
11142    * To do: check -define png:bit_depth and png:color_type as well,
11143    * or perhaps use mng:bit_depth and mng:color_type instead for
11144    * global settings.
11145    */
11146
11147   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11148   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11149   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11150
11151   write_jng=MagickFalse;
11152   if (image_info->compression == JPEGCompression)
11153     write_jng=MagickTrue;
11154
11155   mng_info->adjoin=image_info->adjoin &&
11156     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11157
11158   if (logging != MagickFalse)
11159     {
11160       /* Log some info about the input */
11161       Image
11162         *p;
11163
11164       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11165         "  Checking input image(s)");
11166
11167       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11168         "    Image_info depth: %.20g",(double) image_info->depth);
11169
11170       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11171         "    Type: %d",image_info->type);
11172
11173       scene=0;
11174       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11175       {
11176         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11177           "    Scene: %.20g",(double) scene++);
11178
11179         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11180           "      Image depth: %.20g",(double) p->depth);
11181
11182         if (p->matte)
11183           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11184             "      Matte: True");
11185
11186         else
11187           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11188             "      Matte: False");
11189
11190         if (p->storage_class == PseudoClass)
11191           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11192             "      Storage class: PseudoClass");
11193
11194         else
11195           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11196             "      Storage class: DirectClass");
11197
11198         if (p->colors)
11199           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11200             "      Number of colors: %.20g",(double) p->colors);
11201
11202         else
11203           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11204             "      Number of colors: unspecified");
11205
11206         if (mng_info->adjoin == MagickFalse)
11207           break;
11208       }
11209     }
11210
11211   use_global_plte=MagickFalse;
11212   all_images_are_gray=MagickFalse;
11213 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11214   need_local_plte=MagickTrue;
11215 #endif
11216   need_defi=MagickFalse;
11217   need_matte=MagickFalse;
11218   mng_info->framing_mode=1;
11219   mng_info->old_framing_mode=1;
11220
11221   if (write_mng)
11222       if (image_info->page != (char *) NULL)
11223         {
11224           /*
11225             Determine image bounding box.
11226           */
11227           SetGeometry(image,&mng_info->page);
11228           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11229             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11230         }
11231   if (write_mng)
11232     {
11233       unsigned int
11234         need_geom;
11235
11236       unsigned short
11237         red,
11238         green,
11239         blue;
11240
11241       mng_info->page=image->page;
11242       need_geom=MagickTrue;
11243       if (mng_info->page.width || mng_info->page.height)
11244          need_geom=MagickFalse;
11245       /*
11246         Check all the scenes.
11247       */
11248       initial_delay=image->delay;
11249       need_iterations=MagickFalse;
11250       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11251       mng_info->equal_physs=MagickTrue,
11252       mng_info->equal_gammas=MagickTrue;
11253       mng_info->equal_srgbs=MagickTrue;
11254       mng_info->equal_backgrounds=MagickTrue;
11255       image_count=0;
11256 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11257     defined(PNG_MNG_FEATURES_SUPPORTED)
11258       all_images_are_gray=MagickTrue;
11259       mng_info->equal_palettes=MagickFalse;
11260       need_local_plte=MagickFalse;
11261 #endif
11262       for (next_image=image; next_image != (Image *) NULL; )
11263       {
11264         if (need_geom)
11265           {
11266             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11267               mng_info->page.width=next_image->columns+next_image->page.x;
11268
11269             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11270               mng_info->page.height=next_image->rows+next_image->page.y;
11271           }
11272
11273         if (next_image->page.x || next_image->page.y)
11274           need_defi=MagickTrue;
11275
11276         if (next_image->matte)
11277           need_matte=MagickTrue;
11278
11279         if ((int) next_image->dispose >= BackgroundDispose)
11280           if (next_image->matte || next_image->page.x || next_image->page.y ||
11281               ((next_image->columns < mng_info->page.width) &&
11282                (next_image->rows < mng_info->page.height)))
11283             mng_info->need_fram=MagickTrue;
11284
11285         if (next_image->iterations)
11286           need_iterations=MagickTrue;
11287
11288         final_delay=next_image->delay;
11289
11290         if (final_delay != initial_delay || final_delay > 1UL*
11291            next_image->ticks_per_second)
11292           mng_info->need_fram=1;
11293
11294 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11295     defined(PNG_MNG_FEATURES_SUPPORTED)
11296         /*
11297           check for global palette possibility.
11298         */
11299         if (image->matte != MagickFalse)
11300            need_local_plte=MagickTrue;
11301
11302         if (need_local_plte == 0)
11303           {
11304             if (ImageIsGray(image) == MagickFalse)
11305               all_images_are_gray=MagickFalse;
11306             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11307             if (use_global_plte == 0)
11308               use_global_plte=mng_info->equal_palettes;
11309             need_local_plte=!mng_info->equal_palettes;
11310           }
11311 #endif
11312         if (GetNextImageInList(next_image) != (Image *) NULL)
11313           {
11314             if (next_image->background_color.red !=
11315                 next_image->next->background_color.red ||
11316                 next_image->background_color.green !=
11317                 next_image->next->background_color.green ||
11318                 next_image->background_color.blue !=
11319                 next_image->next->background_color.blue)
11320               mng_info->equal_backgrounds=MagickFalse;
11321
11322             if (next_image->gamma != next_image->next->gamma)
11323               mng_info->equal_gammas=MagickFalse;
11324
11325             if (next_image->rendering_intent !=
11326                 next_image->next->rendering_intent)
11327               mng_info->equal_srgbs=MagickFalse;
11328
11329             if ((next_image->units != next_image->next->units) ||
11330                 (next_image->x_resolution != next_image->next->x_resolution) ||
11331                 (next_image->y_resolution != next_image->next->y_resolution))
11332               mng_info->equal_physs=MagickFalse;
11333
11334             if (mng_info->equal_chrms)
11335               {
11336                 if (next_image->chromaticity.red_primary.x !=
11337                     next_image->next->chromaticity.red_primary.x ||
11338                     next_image->chromaticity.red_primary.y !=
11339                     next_image->next->chromaticity.red_primary.y ||
11340                     next_image->chromaticity.green_primary.x !=
11341                     next_image->next->chromaticity.green_primary.x ||
11342                     next_image->chromaticity.green_primary.y !=
11343                     next_image->next->chromaticity.green_primary.y ||
11344                     next_image->chromaticity.blue_primary.x !=
11345                     next_image->next->chromaticity.blue_primary.x ||
11346                     next_image->chromaticity.blue_primary.y !=
11347                     next_image->next->chromaticity.blue_primary.y ||
11348                     next_image->chromaticity.white_point.x !=
11349                     next_image->next->chromaticity.white_point.x ||
11350                     next_image->chromaticity.white_point.y !=
11351                     next_image->next->chromaticity.white_point.y)
11352                   mng_info->equal_chrms=MagickFalse;
11353               }
11354           }
11355         image_count++;
11356         next_image=GetNextImageInList(next_image);
11357       }
11358       if (image_count < 2)
11359         {
11360           mng_info->equal_backgrounds=MagickFalse;
11361           mng_info->equal_chrms=MagickFalse;
11362           mng_info->equal_gammas=MagickFalse;
11363           mng_info->equal_srgbs=MagickFalse;
11364           mng_info->equal_physs=MagickFalse;
11365           use_global_plte=MagickFalse;
11366 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11367           need_local_plte=MagickTrue;
11368 #endif
11369           need_iterations=MagickFalse;
11370         }
11371
11372      if (mng_info->need_fram == MagickFalse)
11373        {
11374          /*
11375            Only certain framing rates 100/n are exactly representable without
11376            the FRAM chunk but we'll allow some slop in VLC files
11377          */
11378          if (final_delay == 0)
11379            {
11380              if (need_iterations != MagickFalse)
11381                {
11382                  /*
11383                    It's probably a GIF with loop; don't run it *too* fast.
11384                  */
11385                  if (mng_info->adjoin)
11386                    {
11387                      final_delay=10;
11388                      (void) ThrowMagickException(&image->exception,
11389                         GetMagickModule(),CoderWarning,
11390                        "input has zero delay between all frames; assuming",
11391                        " 10 cs `%s'","");
11392                    }
11393                }
11394              else
11395                mng_info->ticks_per_second=0;
11396            }
11397          if (final_delay != 0)
11398            mng_info->ticks_per_second=(png_uint_32)
11399               (image->ticks_per_second/final_delay);
11400          if (final_delay > 50)
11401            mng_info->ticks_per_second=2;
11402
11403          if (final_delay > 75)
11404            mng_info->ticks_per_second=1;
11405
11406          if (final_delay > 125)
11407            mng_info->need_fram=MagickTrue;
11408
11409          if (need_defi && final_delay > 2 && (final_delay != 4) &&
11410             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11411             (final_delay != 25) && (final_delay != 50) && (final_delay !=
11412                1UL*image->ticks_per_second))
11413            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
11414        }
11415
11416      if (mng_info->need_fram != MagickFalse)
11417         mng_info->ticks_per_second=1UL*image->ticks_per_second;
11418      /*
11419         If pseudocolor, we should also check to see if all the
11420         palettes are identical and write a global PLTE if they are.
11421         ../glennrp Feb 99.
11422      */
11423      /*
11424         Write the MNG version 1.0 signature and MHDR chunk.
11425      */
11426      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11427      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
11428      PNGType(chunk,mng_MHDR);
11429      LogPNGChunk(logging,mng_MHDR,28L);
11430      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11431      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
11432      PNGLong(chunk+12,mng_info->ticks_per_second);
11433      PNGLong(chunk+16,0L);  /* layer count=unknown */
11434      PNGLong(chunk+20,0L);  /* frame count=unknown */
11435      PNGLong(chunk+24,0L);  /* play time=unknown   */
11436      if (write_jng)
11437        {
11438          if (need_matte)
11439            {
11440              if (need_defi || mng_info->need_fram || use_global_plte)
11441                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
11442
11443              else
11444                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
11445            }
11446
11447          else
11448            {
11449              if (need_defi || mng_info->need_fram || use_global_plte)
11450                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
11451
11452              else
11453                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
11454            }
11455        }
11456
11457      else
11458        {
11459          if (need_matte)
11460            {
11461              if (need_defi || mng_info->need_fram || use_global_plte)
11462                PNGLong(chunk+28,11L);    /* simplicity=LC */
11463
11464              else
11465                PNGLong(chunk+28,9L);    /* simplicity=VLC */
11466            }
11467
11468          else
11469            {
11470              if (need_defi || mng_info->need_fram || use_global_plte)
11471                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
11472
11473              else
11474                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
11475            }
11476        }
11477      (void) WriteBlob(image,32,chunk);
11478      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11479      option=GetImageOption(image_info,"mng:need-cacheoff");
11480      if (option != (const char *) NULL)
11481        {
11482          size_t
11483            length;
11484
11485          /*
11486            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11487          */
11488          PNGType(chunk,mng_nEED);
11489          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
11490          (void) WriteBlobMSBULong(image,(size_t) length);
11491          LogPNGChunk(logging,mng_nEED,(size_t) length);
11492          length+=4;
11493          (void) WriteBlob(image,length,chunk);
11494          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11495        }
11496      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11497          (GetNextImageInList(image) != (Image *) NULL) &&
11498          (image->iterations != 1))
11499        {
11500          /*
11501            Write MNG TERM chunk
11502          */
11503          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
11504          PNGType(chunk,mng_TERM);
11505          LogPNGChunk(logging,mng_TERM,10L);
11506          chunk[4]=3;  /* repeat animation */
11507          chunk[5]=0;  /* show last frame when done */
11508          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11509             final_delay/MagickMax(image->ticks_per_second,1)));
11510
11511          if (image->iterations == 0)
11512            PNGLong(chunk+10,PNG_UINT_31_MAX);
11513
11514          else
11515            PNGLong(chunk+10,(png_uint_32) image->iterations);
11516
11517          if (logging != MagickFalse)
11518            {
11519              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11520                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11521               final_delay/MagickMax(image->ticks_per_second,1)));
11522
11523              if (image->iterations == 0)
11524                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11525                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
11526
11527              else
11528                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11529                  "     Image iterations: %.20g",(double) image->iterations);
11530            }
11531          (void) WriteBlob(image,14,chunk);
11532          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11533        }
11534      /*
11535        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11536      */
11537      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11538           mng_info->equal_srgbs)
11539        {
11540          /*
11541            Write MNG sRGB chunk
11542          */
11543          (void) WriteBlobMSBULong(image,1L);
11544          PNGType(chunk,mng_sRGB);
11545          LogPNGChunk(logging,mng_sRGB,1L);
11546
11547          if (image->rendering_intent != UndefinedIntent)
11548            chunk[4]=(unsigned char)
11549              Magick_RenderingIntent_to_PNG_RenderingIntent(
11550              (image->rendering_intent));
11551
11552          else
11553            chunk[4]=(unsigned char)
11554              Magick_RenderingIntent_to_PNG_RenderingIntent(
11555                (PerceptualIntent));
11556
11557          (void) WriteBlob(image,5,chunk);
11558          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11559          mng_info->have_write_global_srgb=MagickTrue;
11560        }
11561
11562      else
11563        {
11564          if (image->gamma && mng_info->equal_gammas)
11565            {
11566              /*
11567                 Write MNG gAMA chunk
11568              */
11569              (void) WriteBlobMSBULong(image,4L);
11570              PNGType(chunk,mng_gAMA);
11571              LogPNGChunk(logging,mng_gAMA,4L);
11572              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11573              (void) WriteBlob(image,8,chunk);
11574              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11575              mng_info->have_write_global_gama=MagickTrue;
11576            }
11577          if (mng_info->equal_chrms)
11578            {
11579              PrimaryInfo
11580                primary;
11581
11582              /*
11583                 Write MNG cHRM chunk
11584              */
11585              (void) WriteBlobMSBULong(image,32L);
11586              PNGType(chunk,mng_cHRM);
11587              LogPNGChunk(logging,mng_cHRM,32L);
11588              primary=image->chromaticity.white_point;
11589              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11590              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11591              primary=image->chromaticity.red_primary;
11592              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11593              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11594              primary=image->chromaticity.green_primary;
11595              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11596              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11597              primary=image->chromaticity.blue_primary;
11598              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11599              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11600              (void) WriteBlob(image,36,chunk);
11601              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11602              mng_info->have_write_global_chrm=MagickTrue;
11603            }
11604        }
11605      if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11606        {
11607          /*
11608             Write MNG pHYs chunk
11609          */
11610          (void) WriteBlobMSBULong(image,9L);
11611          PNGType(chunk,mng_pHYs);
11612          LogPNGChunk(logging,mng_pHYs,9L);
11613
11614          if (image->units == PixelsPerInchResolution)
11615            {
11616              PNGLong(chunk+4,(png_uint_32)
11617                (image->x_resolution*100.0/2.54+0.5));
11618
11619              PNGLong(chunk+8,(png_uint_32)
11620                (image->y_resolution*100.0/2.54+0.5));
11621
11622              chunk[12]=1;
11623            }
11624
11625          else
11626            {
11627              if (image->units == PixelsPerCentimeterResolution)
11628                {
11629                  PNGLong(chunk+4,(png_uint_32)
11630                    (image->x_resolution*100.0+0.5));
11631
11632                  PNGLong(chunk+8,(png_uint_32)
11633                    (image->y_resolution*100.0+0.5));
11634
11635                  chunk[12]=1;
11636                }
11637
11638              else
11639                {
11640                  PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11641                  PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11642                  chunk[12]=0;
11643                }
11644            }
11645          (void) WriteBlob(image,13,chunk);
11646          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11647        }
11648      /*
11649        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11650        or does not cover the entire frame.
11651      */
11652      if (write_mng && (image->matte || image->page.x > 0 ||
11653          image->page.y > 0 || (image->page.width &&
11654          (image->page.width+image->page.x < mng_info->page.width))
11655          || (image->page.height && (image->page.height+image->page.y
11656          < mng_info->page.height))))
11657        {
11658          (void) WriteBlobMSBULong(image,6L);
11659          PNGType(chunk,mng_BACK);
11660          LogPNGChunk(logging,mng_BACK,6L);
11661          red=ScaleQuantumToShort(image->background_color.red);
11662          green=ScaleQuantumToShort(image->background_color.green);
11663          blue=ScaleQuantumToShort(image->background_color.blue);
11664          PNGShort(chunk+4,red);
11665          PNGShort(chunk+6,green);
11666          PNGShort(chunk+8,blue);
11667          (void) WriteBlob(image,10,chunk);
11668          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11669          if (mng_info->equal_backgrounds)
11670            {
11671              (void) WriteBlobMSBULong(image,6L);
11672              PNGType(chunk,mng_bKGD);
11673              LogPNGChunk(logging,mng_bKGD,6L);
11674              (void) WriteBlob(image,10,chunk);
11675              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11676            }
11677        }
11678
11679 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11680      if ((need_local_plte == MagickFalse) &&
11681          (image->storage_class == PseudoClass) &&
11682          (all_images_are_gray == MagickFalse))
11683        {
11684          size_t
11685            data_length;
11686
11687          /*
11688            Write MNG PLTE chunk
11689          */
11690          data_length=3*image->colors;
11691          (void) WriteBlobMSBULong(image,data_length);
11692          PNGType(chunk,mng_PLTE);
11693          LogPNGChunk(logging,mng_PLTE,data_length);
11694
11695          for (i=0; i < (ssize_t) image->colors; i++)
11696          {
11697            chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11698            chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11699            chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11700          }
11701
11702          (void) WriteBlob(image,data_length+4,chunk);
11703          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11704          mng_info->have_write_global_plte=MagickTrue;
11705        }
11706 #endif
11707     }
11708   scene=0;
11709   mng_info->delay=0;
11710 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11711     defined(PNG_MNG_FEATURES_SUPPORTED)
11712   mng_info->equal_palettes=MagickFalse;
11713 #endif
11714   do
11715   {
11716     if (mng_info->adjoin)
11717     {
11718 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11719     defined(PNG_MNG_FEATURES_SUPPORTED)
11720     /*
11721       If we aren't using a global palette for the entire MNG, check to
11722       see if we can use one for two or more consecutive images.
11723     */
11724     if (need_local_plte && use_global_plte && !all_images_are_gray)
11725       {
11726         if (mng_info->IsPalette)
11727           {
11728             /*
11729               When equal_palettes is true, this image has the same palette
11730               as the previous PseudoClass image
11731             */
11732             mng_info->have_write_global_plte=mng_info->equal_palettes;
11733             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11734             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11735               {
11736                 /*
11737                   Write MNG PLTE chunk
11738                 */
11739                 size_t
11740                   data_length;
11741
11742                 data_length=3*image->colors;
11743                 (void) WriteBlobMSBULong(image,data_length);
11744                 PNGType(chunk,mng_PLTE);
11745                 LogPNGChunk(logging,mng_PLTE,data_length);
11746
11747                 for (i=0; i < (ssize_t) image->colors; i++)
11748                 {
11749                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11750                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11751                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11752                 }
11753
11754                 (void) WriteBlob(image,data_length+4,chunk);
11755                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11756                    (uInt) (data_length+4)));
11757                 mng_info->have_write_global_plte=MagickTrue;
11758               }
11759           }
11760         else
11761           mng_info->have_write_global_plte=MagickFalse;
11762       }
11763 #endif
11764     if (need_defi)
11765       {
11766         ssize_t
11767           previous_x,
11768           previous_y;
11769
11770         if (scene)
11771           {
11772             previous_x=mng_info->page.x;
11773             previous_y=mng_info->page.y;
11774           }
11775         else
11776           {
11777             previous_x=0;
11778             previous_y=0;
11779           }
11780         mng_info->page=image->page;
11781         if ((mng_info->page.x !=  previous_x) ||
11782             (mng_info->page.y != previous_y))
11783           {
11784              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
11785              PNGType(chunk,mng_DEFI);
11786              LogPNGChunk(logging,mng_DEFI,12L);
11787              chunk[4]=0; /* object 0 MSB */
11788              chunk[5]=0; /* object 0 LSB */
11789              chunk[6]=0; /* visible  */
11790              chunk[7]=0; /* abstract */
11791              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11792              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11793              (void) WriteBlob(image,16,chunk);
11794              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11795           }
11796       }
11797     }
11798
11799    mng_info->write_mng=write_mng;
11800
11801    if ((int) image->dispose >= 3)
11802      mng_info->framing_mode=3;
11803
11804    if (mng_info->need_fram && mng_info->adjoin &&
11805        ((image->delay != mng_info->delay) ||
11806         (mng_info->framing_mode != mng_info->old_framing_mode)))
11807      {
11808        if (image->delay == mng_info->delay)
11809          {
11810            /*
11811              Write a MNG FRAM chunk with the new framing mode.
11812            */
11813            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
11814            PNGType(chunk,mng_FRAM);
11815            LogPNGChunk(logging,mng_FRAM,1L);
11816            chunk[4]=(unsigned char) mng_info->framing_mode;
11817            (void) WriteBlob(image,5,chunk);
11818            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11819          }
11820        else
11821          {
11822            /*
11823              Write a MNG FRAM chunk with the delay.
11824            */
11825            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
11826            PNGType(chunk,mng_FRAM);
11827            LogPNGChunk(logging,mng_FRAM,10L);
11828            chunk[4]=(unsigned char) mng_info->framing_mode;
11829            chunk[5]=0;  /* frame name separator (no name) */
11830            chunk[6]=2;  /* flag for changing default delay */
11831            chunk[7]=0;  /* flag for changing frame timeout */
11832            chunk[8]=0;  /* flag for changing frame clipping */
11833            chunk[9]=0;  /* flag for changing frame sync_id */
11834            PNGLong(chunk+10,(png_uint_32)
11835              ((mng_info->ticks_per_second*
11836              image->delay)/MagickMax(image->ticks_per_second,1)));
11837            (void) WriteBlob(image,14,chunk);
11838            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11839            mng_info->delay=(png_uint_32) image->delay;
11840          }
11841        mng_info->old_framing_mode=mng_info->framing_mode;
11842      }
11843
11844 #if defined(JNG_SUPPORTED)
11845    if (image_info->compression == JPEGCompression)
11846      {
11847        ImageInfo
11848          *write_info;
11849
11850        if (logging != MagickFalse)
11851          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11852            "  Writing JNG object.");
11853        /* To do: specify the desired alpha compression method. */
11854        write_info=CloneImageInfo(image_info);
11855        write_info->compression=UndefinedCompression;
11856        status=WriteOneJNGImage(mng_info,write_info,image);
11857        write_info=DestroyImageInfo(write_info);
11858      }
11859    else
11860 #endif
11861      {
11862        if (logging != MagickFalse)
11863          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11864            "  Writing PNG object.");
11865
11866        mng_info->need_blob = MagickFalse;
11867
11868        /* We don't want any ancillary chunks written */
11869        mng_info->ping_exclude_bKGD=MagickTrue;
11870        mng_info->ping_exclude_cHRM=MagickTrue;
11871        mng_info->ping_exclude_EXIF=MagickTrue;
11872        mng_info->ping_exclude_gAMA=MagickTrue;
11873        mng_info->ping_exclude_cHRM=MagickTrue;
11874        mng_info->ping_exclude_iCCP=MagickTrue;
11875        /* mng_info->ping_exclude_iTXt=MagickTrue; */
11876        mng_info->ping_exclude_oFFs=MagickTrue;
11877        mng_info->ping_exclude_pHYs=MagickTrue;
11878        mng_info->ping_exclude_sRGB=MagickTrue;
11879        mng_info->ping_exclude_tEXt=MagickTrue;
11880        mng_info->ping_exclude_tRNS=MagickTrue;
11881        mng_info->ping_exclude_vpAg=MagickTrue;
11882        mng_info->ping_exclude_zCCP=MagickTrue;
11883        mng_info->ping_exclude_zTXt=MagickTrue;
11884
11885        status=WriteOnePNGImage(mng_info,image_info,image);
11886      }
11887
11888     if (status == MagickFalse)
11889       {
11890         MngInfoFreeStruct(mng_info,&have_mng_structure);
11891         (void) CloseBlob(image);
11892         return(MagickFalse);
11893       }
11894     (void) CatchImageException(image);
11895     if (GetNextImageInList(image) == (Image *) NULL)
11896       break;
11897     image=SyncNextImageInList(image);
11898     status=SetImageProgress(image,SaveImagesTag,scene++,
11899       GetImageListLength(image));
11900
11901     if (status == MagickFalse)
11902       break;
11903
11904   } while (mng_info->adjoin);
11905
11906   if (write_mng)
11907     {
11908       while (GetPreviousImageInList(image) != (Image *) NULL)
11909         image=GetPreviousImageInList(image);
11910       /*
11911         Write the MEND chunk.
11912       */
11913       (void) WriteBlobMSBULong(image,0x00000000L);
11914       PNGType(chunk,mng_MEND);
11915       LogPNGChunk(logging,mng_MEND,0L);
11916       (void) WriteBlob(image,4,chunk);
11917       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11918     }
11919   /*
11920     Relinquish resources.
11921   */
11922   (void) CloseBlob(image);
11923   MngInfoFreeStruct(mng_info,&have_mng_structure);
11924
11925   if (logging != MagickFalse)
11926     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
11927
11928   return(MagickTrue);
11929 }
11930 #else /* PNG_LIBPNG_VER > 10011 */
11931
11932 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
11933 {
11934   image=image;
11935   printf("Your PNG library is too old: You have libpng-%s\n",
11936      PNG_LIBPNG_VER_STRING);
11937
11938   ThrowBinaryException(CoderError,"PNG library is too old",
11939      image_info->filename);
11940 }
11941
11942 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11943 {
11944   return(WritePNGImage(image_info,image));
11945 }
11946 #endif /* PNG_LIBPNG_VER > 10011 */
11947 #endif