]> granicus.if.org Git - imagemagick/blob - coders/png.c
Re-enabled writing grayscale PNG with bit depth 2 or 4.
[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     Relinquish resources.
2999   */
3000   png_destroy_read_struct(&ping,&ping_info,&end_info);
3001
3002   ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
3003 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3004   UnlockSemaphoreInfo(ping_semaphore);
3005 #endif
3006
3007   if (logging != MagickFalse)
3008     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3009       "  exit ReadOnePNGImage()");
3010
3011   return(image);
3012
3013 /* end of reading one PNG image */
3014 }
3015
3016 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3017 {
3018   Image
3019     *image,
3020     *previous;
3021
3022   MagickBooleanType
3023     have_mng_structure,
3024     logging,
3025     status;
3026
3027   MngInfo
3028     *mng_info;
3029
3030   char
3031     magic_number[MaxTextExtent];
3032
3033   ssize_t
3034     count;
3035
3036   /*
3037     Open image file.
3038   */
3039   assert(image_info != (const ImageInfo *) NULL);
3040   assert(image_info->signature == MagickSignature);
3041
3042   if (image_info->debug != MagickFalse)
3043     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3044       image_info->filename);
3045
3046   assert(exception != (ExceptionInfo *) NULL);
3047   assert(exception->signature == MagickSignature);
3048   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3049   image=AcquireImage(image_info);
3050   mng_info=(MngInfo *) NULL;
3051   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3052
3053   if (status == MagickFalse)
3054     ThrowReaderException(FileOpenError,"UnableToOpenFile");
3055
3056   /*
3057     Verify PNG signature.
3058   */
3059   count=ReadBlob(image,8,(unsigned char *) magic_number);
3060
3061   if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3062     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3063
3064   /*
3065     Allocate a MngInfo structure.
3066   */
3067   have_mng_structure=MagickFalse;
3068   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3069
3070   if (mng_info == (MngInfo *) NULL)
3071     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3072
3073   /*
3074     Initialize members of the MngInfo structure.
3075   */
3076   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3077   mng_info->image=image;
3078   have_mng_structure=MagickTrue;
3079
3080   previous=image;
3081   image=ReadOnePNGImage(mng_info,image_info,exception);
3082   MngInfoFreeStruct(mng_info,&have_mng_structure);
3083
3084   if (image == (Image *) NULL)
3085     {
3086       if (previous != (Image *) NULL)
3087         {
3088           if (previous->signature != MagickSignature)
3089             ThrowReaderException(CorruptImageError,"CorruptImage");
3090
3091           (void) CloseBlob(previous);
3092           (void) DestroyImageList(previous);
3093         }
3094
3095       if (logging != MagickFalse)
3096         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3097           "exit ReadPNGImage() with error");
3098
3099       return((Image *) NULL);
3100     }
3101
3102   (void) CloseBlob(image);
3103
3104   if ((image->columns == 0) || (image->rows == 0))
3105     {
3106       if (logging != MagickFalse)
3107         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3108           "exit ReadPNGImage() with error.");
3109
3110       ThrowReaderException(CorruptImageError,"CorruptImage");
3111     }
3112
3113   if (LocaleCompare(image_info->magick,"PNG8") == 0)
3114     {
3115       (void) SetImageType(image,PaletteType);
3116
3117       if (image->matte != MagickFalse)
3118         {
3119           /* To do: Reduce to binary transparency */
3120         }
3121     }
3122
3123   if (LocaleCompare(image_info->magick,"PNG24") == 0)
3124     {
3125       (void) SetImageType(image,TrueColorType);
3126       image->matte=MagickFalse;
3127     }
3128
3129   if (LocaleCompare(image_info->magick,"PNG32") == 0)
3130     (void) SetImageType(image,TrueColorMatteType);
3131
3132   if (logging != MagickFalse)
3133     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3134         "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3135             (double) image->page.width,(double) image->page.height,
3136             (double) image->page.x,(double) image->page.y);
3137
3138   if (logging != MagickFalse)
3139     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3140
3141   return(image);
3142 }
3143
3144
3145
3146 #if defined(JNG_SUPPORTED)
3147 /*
3148 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3149 %                                                                             %
3150 %                                                                             %
3151 %                                                                             %
3152 %   R e a d O n e J N G I m a g e                                             %
3153 %                                                                             %
3154 %                                                                             %
3155 %                                                                             %
3156 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3157 %
3158 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3159 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
3160 %  necessary for the new Image structure and returns a pointer to the new
3161 %  image.
3162 %
3163 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
3164 %
3165 %  The format of the ReadOneJNGImage method is:
3166 %
3167 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3168 %         ExceptionInfo *exception)
3169 %
3170 %  A description of each parameter follows:
3171 %
3172 %    o mng_info: Specifies a pointer to a MngInfo structure.
3173 %
3174 %    o image_info: the image info.
3175 %
3176 %    o exception: return any errors or warnings in this structure.
3177 %
3178 */
3179 static Image *ReadOneJNGImage(MngInfo *mng_info,
3180     const ImageInfo *image_info, ExceptionInfo *exception)
3181 {
3182   Image
3183     *alpha_image,
3184     *color_image,
3185     *image,
3186     *jng_image;
3187
3188   ImageInfo
3189     *alpha_image_info,
3190     *color_image_info;
3191
3192   MagickBooleanType
3193     logging;
3194
3195   ssize_t
3196     y;
3197
3198   MagickBooleanType
3199     status;
3200
3201   png_uint_32
3202     jng_height,
3203     jng_width;
3204
3205   png_byte
3206     jng_color_type,
3207     jng_image_sample_depth,
3208     jng_image_compression_method,
3209     jng_image_interlace_method,
3210     jng_alpha_sample_depth,
3211     jng_alpha_compression_method,
3212     jng_alpha_filter_method,
3213     jng_alpha_interlace_method;
3214
3215   register const PixelPacket
3216     *s;
3217
3218   register ssize_t
3219     i,
3220     x;
3221
3222   register PixelPacket
3223     *q;
3224
3225   register unsigned char
3226     *p;
3227
3228   unsigned int
3229     read_JSEP,
3230     reading_idat,
3231     skip_to_iend;
3232
3233   size_t
3234     length;
3235
3236   jng_alpha_compression_method=0;
3237   jng_alpha_sample_depth=8;
3238   jng_color_type=0;
3239   jng_height=0;
3240   jng_width=0;
3241   alpha_image=(Image *) NULL;
3242   color_image=(Image *) NULL;
3243   alpha_image_info=(ImageInfo *) NULL;
3244   color_image_info=(ImageInfo *) NULL;
3245
3246   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3247     "  Enter ReadOneJNGImage()");
3248
3249   image=mng_info->image;
3250
3251   if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3252     {
3253       /*
3254         Allocate next image structure.
3255       */
3256       if (logging != MagickFalse)
3257         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3258            "  AcquireNextImage()");
3259
3260       AcquireNextImage(image_info,image);
3261
3262       if (GetNextImageInList(image) == (Image *) NULL)
3263         return((Image *) NULL);
3264
3265       image=SyncNextImageInList(image);
3266     }
3267   mng_info->image=image;
3268
3269   /*
3270     Signature bytes have already been read.
3271   */
3272
3273   read_JSEP=MagickFalse;
3274   reading_idat=MagickFalse;
3275   skip_to_iend=MagickFalse;
3276   for (;;)
3277   {
3278     char
3279       type[MaxTextExtent];
3280
3281     unsigned char
3282       *chunk;
3283
3284     unsigned int
3285       count;
3286
3287     /*
3288       Read a new JNG chunk.
3289     */
3290     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3291       2*GetBlobSize(image));
3292
3293     if (status == MagickFalse)
3294       break;
3295
3296     type[0]='\0';
3297     (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3298     length=ReadBlobMSBLong(image);
3299     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3300
3301     if (logging != MagickFalse)
3302       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3303         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
3304         type[0],type[1],type[2],type[3],(double) length);
3305
3306     if (length > PNG_UINT_31_MAX || count == 0)
3307       ThrowReaderException(CorruptImageError,"CorruptImage");
3308
3309     p=NULL;
3310     chunk=(unsigned char *) NULL;
3311
3312     if (length)
3313       {
3314         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3315
3316         if (chunk == (unsigned char *) NULL)
3317           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3318
3319         for (i=0; i < (ssize_t) length; i++)
3320           chunk[i]=(unsigned char) ReadBlobByte(image);
3321
3322         p=chunk;
3323       }
3324
3325     (void) ReadBlobMSBLong(image);  /* read crc word */
3326
3327     if (skip_to_iend)
3328       {
3329         if (length)
3330           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3331
3332         continue;
3333       }
3334
3335     if (memcmp(type,mng_JHDR,4) == 0)
3336       {
3337         if (length == 16)
3338           {
3339             jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3340                 (p[2] << 8) | p[3]);
3341             jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3342                 (p[6] << 8) | p[7]);
3343             jng_color_type=p[8];
3344             jng_image_sample_depth=p[9];
3345             jng_image_compression_method=p[10];
3346             jng_image_interlace_method=p[11];
3347
3348             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3349               NoInterlace;
3350
3351             jng_alpha_sample_depth=p[12];
3352             jng_alpha_compression_method=p[13];
3353             jng_alpha_filter_method=p[14];
3354             jng_alpha_interlace_method=p[15];
3355
3356             if (logging != MagickFalse)
3357               {
3358                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3359                   "    jng_width:      %16lu",(unsigned long) jng_width);
3360
3361                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3362                   "    jng_width:      %16lu",(unsigned long) jng_height);
3363
3364                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3365                   "    jng_color_type: %16d",jng_color_type);
3366
3367                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3368                   "    jng_image_sample_depth:      %3d",
3369                   jng_image_sample_depth);
3370
3371                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3372                   "    jng_image_compression_method:%3d",
3373                   jng_image_compression_method);
3374
3375                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3376                   "    jng_image_interlace_method:  %3d",
3377                   jng_image_interlace_method);
3378
3379                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3380                   "    jng_alpha_sample_depth:      %3d",
3381                   jng_alpha_sample_depth);
3382
3383                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3384                   "    jng_alpha_compression_method:%3d",
3385                   jng_alpha_compression_method);
3386
3387                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3388                   "    jng_alpha_filter_method:     %3d",
3389                   jng_alpha_filter_method);
3390
3391                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3392                   "    jng_alpha_interlace_method:  %3d",
3393                   jng_alpha_interlace_method);
3394               }
3395           }
3396
3397         if (length)
3398           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3399
3400         continue;
3401       }
3402
3403
3404     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3405         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3406          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3407       {
3408         /*
3409            o create color_image
3410            o open color_blob, attached to color_image
3411            o if (color type has alpha)
3412                open alpha_blob, attached to alpha_image
3413         */
3414
3415         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
3416
3417         if (color_image_info == (ImageInfo *) NULL)
3418           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3419
3420         GetImageInfo(color_image_info);
3421         color_image=AcquireImage(color_image_info);
3422
3423         if (color_image == (Image *) NULL)
3424           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3425
3426         if (logging != MagickFalse)
3427           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3428             "    Creating color_blob.");
3429
3430         (void) AcquireUniqueFilename(color_image->filename);
3431         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3432           exception);
3433
3434         if (status == MagickFalse)
3435           return((Image *) NULL);
3436
3437         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3438           {
3439             alpha_image_info=(ImageInfo *)
3440               AcquireMagickMemory(sizeof(ImageInfo));
3441
3442             if (alpha_image_info == (ImageInfo *) NULL)
3443               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3444
3445             GetImageInfo(alpha_image_info);
3446             alpha_image=AcquireImage(alpha_image_info);
3447
3448             if (alpha_image == (Image *) NULL)
3449               {
3450                 alpha_image=DestroyImage(alpha_image);
3451                 ThrowReaderException(ResourceLimitError,
3452                   "MemoryAllocationFailed");
3453               }
3454
3455             if (logging != MagickFalse)
3456               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3457                 "    Creating alpha_blob.");
3458
3459             (void) AcquireUniqueFilename(alpha_image->filename);
3460             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3461               exception);
3462
3463             if (status == MagickFalse)
3464               return((Image *) NULL);
3465
3466             if (jng_alpha_compression_method == 0)
3467               {
3468                 unsigned char
3469                   data[18];
3470
3471                 if (logging != MagickFalse)
3472                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3473                     "    Writing IHDR chunk to alpha_blob.");
3474
3475                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3476                   "\211PNG\r\n\032\n");
3477
3478                 (void) WriteBlobMSBULong(alpha_image,13L);
3479                 PNGType(data,mng_IHDR);
3480                 LogPNGChunk(logging,mng_IHDR,13L);
3481                 PNGLong(data+4,jng_width);
3482                 PNGLong(data+8,jng_height);
3483                 data[12]=jng_alpha_sample_depth;
3484                 data[13]=0; /* color_type gray */
3485                 data[14]=0; /* compression method 0 */
3486                 data[15]=0; /* filter_method 0 */
3487                 data[16]=0; /* interlace_method 0 */
3488                 (void) WriteBlob(alpha_image,17,data);
3489                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3490               }
3491           }
3492         reading_idat=MagickTrue;
3493       }
3494
3495     if (memcmp(type,mng_JDAT,4) == 0)
3496       {
3497         /* Copy chunk to color_image->blob */
3498
3499         if (logging != MagickFalse)
3500           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3501             "    Copying JDAT chunk data to color_blob.");
3502
3503         (void) WriteBlob(color_image,length,chunk);
3504
3505         if (length)
3506           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3507
3508         continue;
3509       }
3510
3511     if (memcmp(type,mng_IDAT,4) == 0)
3512       {
3513         png_byte
3514            data[5];
3515
3516         /* Copy IDAT header and chunk data to alpha_image->blob */
3517
3518         if (image_info->ping == MagickFalse)
3519           {
3520             if (logging != MagickFalse)
3521               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3522                 "    Copying IDAT chunk data to alpha_blob.");
3523
3524             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
3525             PNGType(data,mng_IDAT);
3526             LogPNGChunk(logging,mng_IDAT,length);
3527             (void) WriteBlob(alpha_image,4,data);
3528             (void) WriteBlob(alpha_image,length,chunk);
3529             (void) WriteBlobMSBULong(alpha_image,
3530               crc32(crc32(0,data,4),chunk,(uInt) length));
3531           }
3532
3533         if (length)
3534           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3535
3536         continue;
3537       }
3538
3539     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3540       {
3541         /* Copy chunk data to alpha_image->blob */
3542
3543         if (image_info->ping == MagickFalse)
3544           {
3545             if (logging != MagickFalse)
3546               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3547                 "    Copying JDAA chunk data to alpha_blob.");
3548
3549             (void) WriteBlob(alpha_image,length,chunk);
3550           }
3551
3552         if (length)
3553           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3554
3555         continue;
3556       }
3557
3558     if (memcmp(type,mng_JSEP,4) == 0)
3559       {
3560         read_JSEP=MagickTrue;
3561
3562         if (length)
3563           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3564
3565         continue;
3566       }
3567
3568     if (memcmp(type,mng_bKGD,4) == 0)
3569       {
3570         if (length == 2)
3571           {
3572             image->background_color.red=ScaleCharToQuantum(p[1]);
3573             image->background_color.green=image->background_color.red;
3574             image->background_color.blue=image->background_color.red;
3575           }
3576
3577         if (length == 6)
3578           {
3579             image->background_color.red=ScaleCharToQuantum(p[1]);
3580             image->background_color.green=ScaleCharToQuantum(p[3]);
3581             image->background_color.blue=ScaleCharToQuantum(p[5]);
3582           }
3583
3584         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3585         continue;
3586       }
3587
3588     if (memcmp(type,mng_gAMA,4) == 0)
3589       {
3590         if (length == 4)
3591           image->gamma=((float) mng_get_long(p))*0.00001;
3592
3593         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3594         continue;
3595       }
3596
3597     if (memcmp(type,mng_cHRM,4) == 0)
3598       {
3599         if (length == 32)
3600           {
3601             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3602             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3603             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3604             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3605             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3606             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3607             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3608             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3609           }
3610
3611         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3612         continue;
3613       }
3614
3615     if (memcmp(type,mng_sRGB,4) == 0)
3616       {
3617         if (length == 1)
3618           {
3619             image->rendering_intent=
3620               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
3621             image->gamma=0.45455f;
3622             image->chromaticity.red_primary.x=0.6400f;
3623             image->chromaticity.red_primary.y=0.3300f;
3624             image->chromaticity.green_primary.x=0.3000f;
3625             image->chromaticity.green_primary.y=0.6000f;
3626             image->chromaticity.blue_primary.x=0.1500f;
3627             image->chromaticity.blue_primary.y=0.0600f;
3628             image->chromaticity.white_point.x=0.3127f;
3629             image->chromaticity.white_point.y=0.3290f;
3630           }
3631
3632         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3633         continue;
3634       }
3635
3636     if (memcmp(type,mng_oFFs,4) == 0)
3637       {
3638         if (length > 8)
3639           {
3640             image->page.x=(ssize_t) mng_get_long(p);
3641             image->page.y=(ssize_t) mng_get_long(&p[4]);
3642
3643             if ((int) p[8] != 0)
3644               {
3645                 image->page.x/=10000;
3646                 image->page.y/=10000;
3647               }
3648           }
3649
3650         if (length)
3651           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3652
3653         continue;
3654       }
3655
3656     if (memcmp(type,mng_pHYs,4) == 0)
3657       {
3658         if (length > 8)
3659           {
3660             image->x_resolution=(double) mng_get_long(p);
3661             image->y_resolution=(double) mng_get_long(&p[4]);
3662             if ((int) p[8] == PNG_RESOLUTION_METER)
3663               {
3664                 image->units=PixelsPerCentimeterResolution;
3665                 image->x_resolution=image->x_resolution/100.0f;
3666                 image->y_resolution=image->y_resolution/100.0f;
3667               }
3668           }
3669
3670         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3671         continue;
3672       }
3673
3674 #if 0
3675     if (memcmp(type,mng_iCCP,4) == 0)
3676       {
3677         /* To do: */
3678         if (length)
3679           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3680
3681         continue;
3682       }
3683 #endif
3684
3685     if (length)
3686       chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3687
3688     if (memcmp(type,mng_IEND,4))
3689       continue;
3690
3691     break;
3692   }
3693
3694
3695   /* IEND found */
3696
3697   /*
3698     Finish up reading image data:
3699
3700        o read main image from color_blob.
3701
3702        o close color_blob.
3703
3704        o if (color_type has alpha)
3705             if alpha_encoding is PNG
3706                read secondary image from alpha_blob via ReadPNG
3707             if alpha_encoding is JPEG
3708                read secondary image from alpha_blob via ReadJPEG
3709
3710        o close alpha_blob.
3711
3712        o copy intensity of secondary image into
3713          opacity samples of main image.
3714
3715        o destroy the secondary image.
3716   */
3717
3718   (void) CloseBlob(color_image);
3719
3720   if (logging != MagickFalse)
3721     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3722       "    Reading jng_image from color_blob.");
3723
3724   (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3725     color_image->filename);
3726
3727   color_image_info->ping=MagickFalse;   /* To do: avoid this */
3728   jng_image=ReadImage(color_image_info,exception);
3729
3730   if (jng_image == (Image *) NULL)
3731     return((Image *) NULL);
3732
3733   (void) RelinquishUniqueFileResource(color_image->filename);
3734   color_image=DestroyImage(color_image);
3735   color_image_info=DestroyImageInfo(color_image_info);
3736
3737   if (jng_image == (Image *) NULL)
3738     return((Image *) NULL);
3739
3740   if (logging != MagickFalse)
3741     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3742       "    Copying jng_image pixels to main image.");
3743
3744   image->rows=jng_height;
3745   image->columns=jng_width;
3746   length=image->columns*sizeof(PixelPacket);
3747
3748   for (y=0; y < (ssize_t) image->rows; y++)
3749   {
3750     s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3751     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3752     (void) CopyMagickMemory(q,s,length);
3753
3754     if (SyncAuthenticPixels(image,exception) == MagickFalse)
3755       break;
3756   }
3757
3758   jng_image=DestroyImage(jng_image);
3759
3760   if (image_info->ping == MagickFalse)
3761     {
3762      if (jng_color_type >= 12)
3763        {
3764          if (jng_alpha_compression_method == 0)
3765            {
3766              png_byte
3767                data[5];
3768              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3769              PNGType(data,mng_IEND);
3770              LogPNGChunk(logging,mng_IEND,0L);
3771              (void) WriteBlob(alpha_image,4,data);
3772              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3773            }
3774
3775          (void) CloseBlob(alpha_image);
3776
3777          if (logging != MagickFalse)
3778            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3779              "    Reading opacity from alpha_blob.");
3780
3781          (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3782            "%s",alpha_image->filename);
3783
3784          jng_image=ReadImage(alpha_image_info,exception);
3785
3786          if (jng_image != (Image *) NULL)
3787            for (y=0; y < (ssize_t) image->rows; y++)
3788            {
3789              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3790                 &image->exception);
3791              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3792
3793              if (image->matte != MagickFalse)
3794                for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3795                   q->opacity=(Quantum) QuantumRange-s->red;
3796
3797              else
3798                for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3799                {
3800                   q->opacity=(Quantum) QuantumRange-s->red;
3801                   if (q->opacity != OpaqueOpacity)
3802                     image->matte=MagickTrue;
3803                }
3804
3805              if (SyncAuthenticPixels(image,exception) == MagickFalse)
3806                break;
3807            }
3808          (void) RelinquishUniqueFileResource(alpha_image->filename);
3809          alpha_image=DestroyImage(alpha_image);
3810          alpha_image_info=DestroyImageInfo(alpha_image_info);
3811          if (jng_image != (Image *) NULL)
3812            jng_image=DestroyImage(jng_image);
3813        }
3814     }
3815
3816   /* Read the JNG image.  */
3817
3818   if (mng_info->mng_type == 0)
3819     {
3820       mng_info->mng_width=jng_width;
3821       mng_info->mng_height=jng_height;
3822     }
3823
3824   if (image->page.width == 0 && image->page.height == 0)
3825     {
3826       image->page.width=jng_width;
3827       image->page.height=jng_height;
3828     }
3829
3830   if (image->page.x == 0 && image->page.y == 0)
3831     {
3832       image->page.x=mng_info->x_off[mng_info->object_id];
3833       image->page.y=mng_info->y_off[mng_info->object_id];
3834     }
3835
3836   else
3837     {
3838       image->page.y=mng_info->y_off[mng_info->object_id];
3839     }
3840
3841   mng_info->image_found++;
3842   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3843     2*GetBlobSize(image));
3844
3845   if (logging != MagickFalse)
3846     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3847       "  exit ReadOneJNGImage()");
3848
3849   return(image);
3850 }
3851
3852 /*
3853 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3854 %                                                                             %
3855 %                                                                             %
3856 %                                                                             %
3857 %   R e a d J N G I m a g e                                                   %
3858 %                                                                             %
3859 %                                                                             %
3860 %                                                                             %
3861 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3862 %
3863 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3864 %  (including the 8-byte signature)  and returns it.  It allocates the memory
3865 %  necessary for the new Image structure and returns a pointer to the new
3866 %  image.
3867 %
3868 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
3869 %
3870 %  The format of the ReadJNGImage method is:
3871 %
3872 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3873 %         *exception)
3874 %
3875 %  A description of each parameter follows:
3876 %
3877 %    o image_info: the image info.
3878 %
3879 %    o exception: return any errors or warnings in this structure.
3880 %
3881 */
3882
3883 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3884 {
3885   Image
3886     *image,
3887     *previous;
3888
3889   MagickBooleanType
3890     have_mng_structure,
3891     logging,
3892     status;
3893
3894   MngInfo
3895     *mng_info;
3896
3897   char
3898     magic_number[MaxTextExtent];
3899
3900   size_t
3901     count;
3902
3903   /*
3904     Open image file.
3905   */
3906   assert(image_info != (const ImageInfo *) NULL);
3907   assert(image_info->signature == MagickSignature);
3908   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3909   assert(exception != (ExceptionInfo *) NULL);
3910   assert(exception->signature == MagickSignature);
3911   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
3912   image=AcquireImage(image_info);
3913   mng_info=(MngInfo *) NULL;
3914   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3915
3916   if (status == MagickFalse)
3917     return((Image *) NULL);
3918
3919   if (LocaleCompare(image_info->magick,"JNG") != 0)
3920     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3921
3922   /* Verify JNG signature.  */
3923
3924   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3925
3926   if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3927     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3928
3929   /* Allocate a MngInfo structure.  */
3930
3931   have_mng_structure=MagickFalse;
3932   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
3933
3934   if (mng_info == (MngInfo *) NULL)
3935     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3936
3937   /* Initialize members of the MngInfo structure.  */
3938
3939   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3940   have_mng_structure=MagickTrue;
3941
3942   mng_info->image=image;
3943   previous=image;
3944   image=ReadOneJNGImage(mng_info,image_info,exception);
3945   MngInfoFreeStruct(mng_info,&have_mng_structure);
3946
3947   if (image == (Image *) NULL)
3948     {
3949       if (IsImageObject(previous) != MagickFalse)
3950         {
3951           (void) CloseBlob(previous);
3952           (void) DestroyImageList(previous);
3953         }
3954
3955       if (logging != MagickFalse)
3956         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3957           "exit ReadJNGImage() with error");
3958
3959       return((Image *) NULL);
3960     }
3961   (void) CloseBlob(image);
3962
3963   if (image->columns == 0 || image->rows == 0)
3964     {
3965       if (logging != MagickFalse)
3966         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3967           "exit ReadJNGImage() with error");
3968
3969       ThrowReaderException(CorruptImageError,"CorruptImage");
3970     }
3971
3972   if (logging != MagickFalse)
3973     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3974
3975   return(image);
3976 }
3977 #endif
3978
3979 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3980 {
3981   char
3982     page_geometry[MaxTextExtent];
3983
3984   Image
3985     *image,
3986     *previous;
3987
3988   MagickBooleanType
3989     logging,
3990     have_mng_structure;
3991
3992   volatile int
3993     first_mng_object,
3994     object_id,
3995     term_chunk_found,
3996     skip_to_iend;
3997
3998   volatile ssize_t
3999     image_count=0;
4000
4001   MagickBooleanType
4002     status;
4003
4004   MagickOffsetType
4005     offset;
4006
4007   MngInfo
4008     *mng_info;
4009
4010   MngBox
4011     default_fb,
4012     fb,
4013     previous_fb;
4014
4015 #if defined(MNG_INSERT_LAYERS)
4016   PixelPacket
4017     mng_background_color;
4018 #endif
4019
4020   register unsigned char
4021     *p;
4022
4023   register ssize_t
4024     i;
4025
4026   size_t
4027     count;
4028
4029   ssize_t
4030     loop_level;
4031
4032   volatile short
4033     skipping_loop;
4034
4035 #if defined(MNG_INSERT_LAYERS)
4036   unsigned int
4037     mandatory_back=0;
4038 #endif
4039
4040   volatile unsigned int
4041 #ifdef MNG_OBJECT_BUFFERS
4042     mng_background_object=0,
4043 #endif
4044     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4045
4046   size_t
4047     default_frame_timeout,
4048     frame_timeout,
4049 #if defined(MNG_INSERT_LAYERS)
4050     image_height,
4051     image_width,
4052 #endif
4053     length;
4054
4055   /* These delays are all measured in image ticks_per_second,
4056    * not in MNG ticks_per_second
4057    */
4058   volatile size_t
4059     default_frame_delay,
4060     final_delay,
4061     final_image_delay,
4062     frame_delay,
4063 #if defined(MNG_INSERT_LAYERS)
4064     insert_layers,
4065 #endif
4066     mng_iterations=1,
4067     simplicity=0,
4068     subframe_height=0,
4069     subframe_width=0;
4070
4071   previous_fb.top=0;
4072   previous_fb.bottom=0;
4073   previous_fb.left=0;
4074   previous_fb.right=0;
4075   default_fb.top=0;
4076   default_fb.bottom=0;
4077   default_fb.left=0;
4078   default_fb.right=0;
4079
4080   /* Open image file.  */
4081
4082   assert(image_info != (const ImageInfo *) NULL);
4083   assert(image_info->signature == MagickSignature);
4084   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4085   assert(exception != (ExceptionInfo *) NULL);
4086   assert(exception->signature == MagickSignature);
4087   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4088   image=AcquireImage(image_info);
4089   mng_info=(MngInfo *) NULL;
4090   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4091
4092   if (status == MagickFalse)
4093     return((Image *) NULL);
4094
4095   first_mng_object=MagickFalse;
4096   skipping_loop=(-1);
4097   have_mng_structure=MagickFalse;
4098
4099   /* Allocate a MngInfo structure.  */
4100
4101   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4102
4103   if (mng_info == (MngInfo *) NULL)
4104     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4105
4106   /* Initialize members of the MngInfo structure.  */
4107
4108   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4109   mng_info->image=image;
4110   have_mng_structure=MagickTrue;
4111
4112   if (LocaleCompare(image_info->magick,"MNG") == 0)
4113     {
4114       char
4115         magic_number[MaxTextExtent];
4116
4117       /* Verify MNG signature.  */
4118       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4119       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4120         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4121
4122       /* Initialize some nonzero members of the MngInfo structure.  */
4123       for (i=0; i < MNG_MAX_OBJECTS; i++)
4124       {
4125         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4126         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4127       }
4128       mng_info->exists[0]=MagickTrue;
4129     }
4130
4131   first_mng_object=MagickTrue;
4132   mng_type=0;
4133 #if defined(MNG_INSERT_LAYERS)
4134   insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4135 #endif
4136   default_frame_delay=0;
4137   default_frame_timeout=0;
4138   frame_delay=0;
4139   final_delay=1;
4140   mng_info->ticks_per_second=1UL*image->ticks_per_second;
4141   object_id=0;
4142   skip_to_iend=MagickFalse;
4143   term_chunk_found=MagickFalse;
4144   mng_info->framing_mode=1;
4145 #if defined(MNG_INSERT_LAYERS)
4146   mandatory_back=MagickFalse;
4147 #endif
4148 #if defined(MNG_INSERT_LAYERS)
4149   mng_background_color=image->background_color;
4150 #endif
4151   default_fb=mng_info->frame;
4152   previous_fb=mng_info->frame;
4153   do
4154   {
4155     char
4156       type[MaxTextExtent];
4157
4158     if (LocaleCompare(image_info->magick,"MNG") == 0)
4159       {
4160         unsigned char
4161           *chunk;
4162
4163         /*
4164           Read a new chunk.
4165         */
4166         type[0]='\0';
4167         (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4168         length=ReadBlobMSBLong(image);
4169         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4170
4171         if (logging != MagickFalse)
4172           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4173            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
4174            type[0],type[1],type[2],type[3],(double) length);
4175
4176         if (length > PNG_UINT_31_MAX)
4177           status=MagickFalse;
4178
4179         if (count == 0)
4180           ThrowReaderException(CorruptImageError,"CorruptImage");
4181
4182         p=NULL;
4183         chunk=(unsigned char *) NULL;
4184
4185         if (length)
4186           {
4187             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4188
4189             if (chunk == (unsigned char *) NULL)
4190               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4191
4192             for (i=0; i < (ssize_t) length; i++)
4193               chunk[i]=(unsigned char) ReadBlobByte(image);
4194
4195             p=chunk;
4196           }
4197
4198         (void) ReadBlobMSBLong(image);  /* read crc word */
4199
4200 #if !defined(JNG_SUPPORTED)
4201         if (memcmp(type,mng_JHDR,4) == 0)
4202           {
4203             skip_to_iend=MagickTrue;
4204
4205             if (mng_info->jhdr_warning == 0)
4206               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4207                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4208
4209             mng_info->jhdr_warning++;
4210           }
4211 #endif
4212         if (memcmp(type,mng_DHDR,4) == 0)
4213           {
4214             skip_to_iend=MagickTrue;
4215
4216             if (mng_info->dhdr_warning == 0)
4217               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4218                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4219
4220             mng_info->dhdr_warning++;
4221           }
4222         if (memcmp(type,mng_MEND,4) == 0)
4223           break;
4224
4225         if (skip_to_iend)
4226           {
4227             if (memcmp(type,mng_IEND,4) == 0)
4228               skip_to_iend=MagickFalse;
4229
4230             if (length)
4231               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4232
4233             if (logging != MagickFalse)
4234               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4235                 "  Skip to IEND.");
4236
4237             continue;
4238           }
4239
4240         if (memcmp(type,mng_MHDR,4) == 0)
4241           {
4242             mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4243                 (p[2] << 8) | p[3]);
4244
4245             mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4246                 (p[6] << 8) | p[7]);
4247
4248             if (logging != MagickFalse)
4249               {
4250                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4251                   "  MNG width: %.20g",(double) mng_info->mng_width);
4252                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4253                   "  MNG height: %.20g",(double) mng_info->mng_height);
4254               }
4255
4256             p+=8;
4257             mng_info->ticks_per_second=(size_t) mng_get_long(p);
4258
4259             if (mng_info->ticks_per_second == 0)
4260               default_frame_delay=0;
4261
4262             else
4263               default_frame_delay=1UL*image->ticks_per_second/
4264                 mng_info->ticks_per_second;
4265
4266             frame_delay=default_frame_delay;
4267             simplicity=0;
4268
4269             if (length > 16)
4270               {
4271                 p+=16;
4272                 simplicity=(size_t) mng_get_long(p);
4273               }
4274
4275             mng_type=1;    /* Full MNG */
4276
4277             if ((simplicity != 0) && ((simplicity | 11) == 11))
4278               mng_type=2; /* LC */
4279
4280             if ((simplicity != 0) && ((simplicity | 9) == 9))
4281               mng_type=3; /* VLC */
4282
4283 #if defined(MNG_INSERT_LAYERS)
4284             if (mng_type != 3)
4285               insert_layers=MagickTrue;
4286 #endif
4287             if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4288               {
4289                 /* Allocate next image structure.  */
4290                 AcquireNextImage(image_info,image);
4291
4292                 if (GetNextImageInList(image) == (Image *) NULL)
4293                   return((Image *) NULL);
4294
4295                 image=SyncNextImageInList(image);
4296                 mng_info->image=image;
4297               }
4298
4299             if ((mng_info->mng_width > 65535L) ||
4300                 (mng_info->mng_height > 65535L))
4301               ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4302
4303             (void) FormatMagickString(page_geometry,MaxTextExtent,
4304               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4305               mng_info->mng_height);
4306
4307             mng_info->frame.left=0;
4308             mng_info->frame.right=(ssize_t) mng_info->mng_width;
4309             mng_info->frame.top=0;
4310             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4311             mng_info->clip=default_fb=previous_fb=mng_info->frame;
4312
4313             for (i=0; i < MNG_MAX_OBJECTS; i++)
4314               mng_info->object_clip[i]=mng_info->frame;
4315
4316             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4317             continue;
4318           }
4319
4320         if (memcmp(type,mng_TERM,4) == 0)
4321           {
4322             int
4323               repeat=0;
4324
4325
4326             if (length)
4327               repeat=p[0];
4328
4329             if (repeat == 3)
4330               {
4331                 final_delay=(png_uint_32) mng_get_long(&p[2]);
4332                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4333
4334                 if (mng_iterations == PNG_UINT_31_MAX)
4335                   mng_iterations=0;
4336
4337                 image->iterations=mng_iterations;
4338                 term_chunk_found=MagickTrue;
4339               }
4340
4341             if (logging != MagickFalse)
4342               {
4343                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4344                   "    repeat=%d",repeat);
4345
4346                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4347                   "    final_delay=%.20g",(double) final_delay);
4348
4349                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4350                   "    image->iterations=%.20g",(double) image->iterations);
4351               }
4352
4353             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4354             continue;
4355           }
4356         if (memcmp(type,mng_DEFI,4) == 0)
4357           {
4358             if (mng_type == 3)
4359               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4360                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4361                 image->filename);
4362
4363             object_id=(p[0] << 8) | p[1];
4364
4365             if (mng_type == 2 && object_id != 0)
4366               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4367                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4368                 image->filename);
4369
4370             if (object_id > MNG_MAX_OBJECTS)
4371               {
4372                 /*
4373                   Instead ofsuing a warning we should allocate a larger
4374                   MngInfo structure and continue.
4375                 */
4376                 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4377                   CoderError,"object id too large","`%s'",image->filename);
4378                 object_id=MNG_MAX_OBJECTS;
4379               }
4380
4381             if (mng_info->exists[object_id])
4382               if (mng_info->frozen[object_id])
4383                 {
4384                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4385                   (void) ThrowMagickException(&image->exception,
4386                     GetMagickModule(),CoderError,
4387                     "DEFI cannot redefine a frozen MNG object","`%s'",
4388                     image->filename);
4389                   continue;
4390                 }
4391
4392             mng_info->exists[object_id]=MagickTrue;
4393
4394             if (length > 2)
4395               mng_info->invisible[object_id]=p[2];
4396
4397             /*
4398               Extract object offset info.
4399             */
4400             if (length > 11)
4401               {
4402                 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4403                     (p[5] << 16) | (p[6] << 8) | p[7]);
4404
4405                 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4406                     (p[9] << 16) | (p[10] << 8) | p[11]);
4407
4408                 if (logging != MagickFalse)
4409                   {
4410                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4411                       "  x_off[%d]: %.20g",object_id,(double)
4412                       mng_info->x_off[object_id]);
4413
4414                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4415                       "  y_off[%d]: %.20g",object_id,(double)
4416                       mng_info->y_off[object_id]);
4417                   }
4418               }
4419
4420             /*
4421               Extract object clipping info.
4422             */
4423             if (length > 27)
4424               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4425                 &p[12]);
4426
4427             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4428             continue;
4429           }
4430         if (memcmp(type,mng_bKGD,4) == 0)
4431           {
4432             mng_info->have_global_bkgd=MagickFalse;
4433
4434             if (length > 5)
4435               {
4436                 mng_info->mng_global_bkgd.red=
4437                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4438
4439                 mng_info->mng_global_bkgd.green=
4440                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4441
4442                 mng_info->mng_global_bkgd.blue=
4443                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4444
4445                 mng_info->have_global_bkgd=MagickTrue;
4446               }
4447
4448             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4449             continue;
4450           }
4451         if (memcmp(type,mng_BACK,4) == 0)
4452           {
4453 #if defined(MNG_INSERT_LAYERS)
4454             if (length > 6)
4455               mandatory_back=p[6];
4456
4457             else
4458               mandatory_back=0;
4459
4460             if (mandatory_back && length > 5)
4461               {
4462                 mng_background_color.red=
4463                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4464
4465                 mng_background_color.green=
4466                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4467
4468                 mng_background_color.blue=
4469                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4470
4471                 mng_background_color.opacity=OpaqueOpacity;
4472               }
4473
4474 #ifdef MNG_OBJECT_BUFFERS
4475             if (length > 8)
4476               mng_background_object=(p[7] << 8) | p[8];
4477 #endif
4478 #endif
4479             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4480             continue;
4481           }
4482
4483         if (memcmp(type,mng_PLTE,4) == 0)
4484           {
4485             /* Read global PLTE.  */
4486
4487             if (length && (length < 769))
4488               {
4489                 if (mng_info->global_plte == (png_colorp) NULL)
4490                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4491                     sizeof(*mng_info->global_plte));
4492
4493                 for (i=0; i < (ssize_t) (length/3); i++)
4494                 {
4495                   mng_info->global_plte[i].red=p[3*i];
4496                   mng_info->global_plte[i].green=p[3*i+1];
4497                   mng_info->global_plte[i].blue=p[3*i+2];
4498                 }
4499
4500                 mng_info->global_plte_length=(unsigned int) (length/3);
4501               }
4502 #ifdef MNG_LOOSE
4503             for ( ; i < 256; i++)
4504             {
4505               mng_info->global_plte[i].red=i;
4506               mng_info->global_plte[i].green=i;
4507               mng_info->global_plte[i].blue=i;
4508             }
4509
4510             if (length)
4511               mng_info->global_plte_length=256;
4512 #endif
4513             else
4514               mng_info->global_plte_length=0;
4515
4516             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4517             continue;
4518           }
4519
4520         if (memcmp(type,mng_tRNS,4) == 0)
4521           {
4522             /* read global tRNS */
4523
4524             if (length < 257)
4525               for (i=0; i < (ssize_t) length; i++)
4526                 mng_info->global_trns[i]=p[i];
4527
4528 #ifdef MNG_LOOSE
4529             for ( ; i < 256; i++)
4530               mng_info->global_trns[i]=255;
4531 #endif
4532             mng_info->global_trns_length=(unsigned int) length;
4533             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4534             continue;
4535           }
4536         if (memcmp(type,mng_gAMA,4) == 0)
4537           {
4538             if (length == 4)
4539               {
4540                 ssize_t
4541                   igamma;
4542
4543                 igamma=mng_get_long(p);
4544                 mng_info->global_gamma=((float) igamma)*0.00001;
4545                 mng_info->have_global_gama=MagickTrue;
4546               }
4547
4548             else
4549               mng_info->have_global_gama=MagickFalse;
4550
4551             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4552             continue;
4553           }
4554
4555         if (memcmp(type,mng_cHRM,4) == 0)
4556           {
4557             /* Read global cHRM */
4558
4559             if (length == 32)
4560               {
4561                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4562                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4563                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4564                 mng_info->global_chrm.red_primary.y=0.00001*
4565                   mng_get_long(&p[12]);
4566                 mng_info->global_chrm.green_primary.x=0.00001*
4567                   mng_get_long(&p[16]);
4568                 mng_info->global_chrm.green_primary.y=0.00001*
4569                   mng_get_long(&p[20]);
4570                 mng_info->global_chrm.blue_primary.x=0.00001*
4571                   mng_get_long(&p[24]);
4572                 mng_info->global_chrm.blue_primary.y=0.00001*
4573                   mng_get_long(&p[28]);
4574                 mng_info->have_global_chrm=MagickTrue;
4575               }
4576             else
4577               mng_info->have_global_chrm=MagickFalse;
4578
4579             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4580             continue;
4581           }
4582
4583         if (memcmp(type,mng_sRGB,4) == 0)
4584           {
4585             /*
4586               Read global sRGB.
4587             */
4588             if (length)
4589               {
4590                 mng_info->global_srgb_intent=
4591                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4592                 mng_info->have_global_srgb=MagickTrue;
4593               }
4594             else
4595               mng_info->have_global_srgb=MagickFalse;
4596
4597             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4598             continue;
4599           }
4600
4601         if (memcmp(type,mng_iCCP,4) == 0)
4602           {
4603             /* To do: */
4604
4605             /*
4606               Read global iCCP.
4607             */
4608             if (length)
4609               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4610
4611             continue;
4612           }
4613
4614         if (memcmp(type,mng_FRAM,4) == 0)
4615           {
4616             if (mng_type == 3)
4617               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4618                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4619                 image->filename);
4620
4621             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4622               image->delay=frame_delay;
4623
4624             frame_delay=default_frame_delay;
4625             frame_timeout=default_frame_timeout;
4626             fb=default_fb;
4627
4628             if (length)
4629               if (p[0])
4630                 mng_info->framing_mode=p[0];
4631
4632             if (logging != MagickFalse)
4633               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4634                 "    Framing_mode=%d",mng_info->framing_mode);
4635
4636             if (length > 6)
4637               {
4638                 /* Note the delay and frame clipping boundaries.  */
4639
4640                 p++; /* framing mode */
4641
4642                 while (*p && ((p-chunk) < (ssize_t) length))
4643                   p++;  /* frame name */
4644
4645                 p++;  /* frame name terminator */
4646
4647                 if ((p-chunk) < (ssize_t) (length-4))
4648                   {
4649                     int
4650                       change_delay,
4651                       change_timeout,
4652                       change_clipping;
4653
4654                     change_delay=(*p++);
4655                     change_timeout=(*p++);
4656                     change_clipping=(*p++);
4657                     p++; /* change_sync */
4658
4659                     if (change_delay)
4660                       {
4661                         frame_delay=1UL*image->ticks_per_second*
4662                           mng_get_long(p);
4663
4664                         if (mng_info->ticks_per_second != 0)
4665                           frame_delay/=mng_info->ticks_per_second;
4666
4667                         else
4668                           frame_delay=PNG_UINT_31_MAX;
4669
4670                         if (change_delay == 2)
4671                           default_frame_delay=frame_delay;
4672
4673                         p+=4;
4674
4675                         if (logging != MagickFalse)
4676                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4677                             "    Framing_delay=%.20g",(double) frame_delay);
4678                       }
4679
4680                     if (change_timeout)
4681                       {
4682                         frame_timeout=1UL*image->ticks_per_second*
4683                           mng_get_long(p);
4684
4685                         if (mng_info->ticks_per_second != 0)
4686                           frame_timeout/=mng_info->ticks_per_second;
4687
4688                         else
4689                           frame_timeout=PNG_UINT_31_MAX;
4690
4691                         if (change_delay == 2)
4692                           default_frame_timeout=frame_timeout;
4693
4694                         p+=4;
4695
4696                         if (logging != MagickFalse)
4697                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4698                             "    Framing_timeout=%.20g",(double) frame_timeout);
4699                       }
4700
4701                     if (change_clipping)
4702                       {
4703                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4704                         p+=17;
4705                         previous_fb=fb;
4706
4707                         if (logging != MagickFalse)
4708                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4709                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
4710                             (double) fb.left,(double) fb.right,(double) fb.top,
4711                             (double) fb.bottom);
4712
4713                         if (change_clipping == 2)
4714                           default_fb=fb;
4715                       }
4716                   }
4717               }
4718             mng_info->clip=fb;
4719             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4720
4721             subframe_width=(size_t) (mng_info->clip.right
4722                -mng_info->clip.left);
4723
4724             subframe_height=(size_t) (mng_info->clip.bottom
4725                -mng_info->clip.top);
4726             /*
4727               Insert a background layer behind the frame if framing_mode is 4.
4728             */
4729 #if defined(MNG_INSERT_LAYERS)
4730             if (logging != MagickFalse)
4731               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4732                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
4733                 subframe_width,(double) subframe_height);
4734
4735             if (insert_layers && (mng_info->framing_mode == 4) &&
4736                 (subframe_width) && (subframe_height))
4737               {
4738                 /* Allocate next image structure.  */
4739                 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4740                   {
4741                     AcquireNextImage(image_info,image);
4742
4743                     if (GetNextImageInList(image) == (Image *) NULL)
4744                       {
4745                         image=DestroyImageList(image);
4746                         MngInfoFreeStruct(mng_info,&have_mng_structure);
4747                         return((Image *) NULL);
4748                       }
4749
4750                     image=SyncNextImageInList(image);
4751                   }
4752
4753                 mng_info->image=image;
4754
4755                 if (term_chunk_found)
4756                   {
4757                     image->start_loop=MagickTrue;
4758                     image->iterations=mng_iterations;
4759                     term_chunk_found=MagickFalse;
4760                   }
4761
4762                 else
4763                     image->start_loop=MagickFalse;
4764
4765                 image->columns=subframe_width;
4766                 image->rows=subframe_height;
4767                 image->page.width=subframe_width;
4768                 image->page.height=subframe_height;
4769                 image->page.x=mng_info->clip.left;
4770                 image->page.y=mng_info->clip.top;
4771                 image->background_color=mng_background_color;
4772                 image->matte=MagickFalse;
4773                 image->delay=0;
4774                 (void) SetImageBackgroundColor(image);
4775
4776                 if (logging != MagickFalse)
4777                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4778                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4779                     (double) mng_info->clip.left,(double) mng_info->clip.right,
4780                     (double) mng_info->clip.top,(double) mng_info->clip.bottom);
4781               }
4782 #endif
4783             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4784             continue;
4785           }
4786         if (memcmp(type,mng_CLIP,4) == 0)
4787           {
4788             unsigned int
4789               first_object,
4790               last_object;
4791
4792             /*
4793               Read CLIP.
4794             */
4795             first_object=(p[0] << 8) | p[1];
4796             last_object=(p[2] << 8) | p[3];
4797
4798             for (i=(int) first_object; i <= (int) last_object; i++)
4799             {
4800               if (mng_info->exists[i] && !mng_info->frozen[i])
4801                 {
4802                   MngBox
4803                     box;
4804
4805                   box=mng_info->object_clip[i];
4806                   mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4807                 }
4808             }
4809
4810             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4811             continue;
4812           }
4813         if (memcmp(type,mng_SAVE,4) == 0)
4814           {
4815             for (i=1; i < MNG_MAX_OBJECTS; i++)
4816               if (mng_info->exists[i])
4817                 {
4818                  mng_info->frozen[i]=MagickTrue;
4819 #ifdef MNG_OBJECT_BUFFERS
4820                  if (mng_info->ob[i] != (MngBuffer *) NULL)
4821                     mng_info->ob[i]->frozen=MagickTrue;
4822 #endif
4823                 }
4824
4825             if (length)
4826               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4827
4828             continue;
4829           }
4830
4831         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4832           {
4833             /* Read DISC or SEEK.  */
4834
4835             if ((length == 0) || !memcmp(type,mng_SEEK,4))
4836               {
4837                 for (i=1; i < MNG_MAX_OBJECTS; i++)
4838                   MngInfoDiscardObject(mng_info,i);
4839               }
4840
4841             else
4842               {
4843                 register ssize_t
4844                   j;
4845
4846                 for (j=0; j < (ssize_t) length; j+=2)
4847                 {
4848                   i=p[j] << 8 | p[j+1];
4849                   MngInfoDiscardObject(mng_info,i);
4850                 }
4851               }
4852
4853             if (length)
4854               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4855
4856             continue;
4857           }
4858
4859         if (memcmp(type,mng_MOVE,4) == 0)
4860           {
4861             size_t
4862               first_object,
4863               last_object;
4864
4865             /* read MOVE */
4866
4867             first_object=(p[0] << 8) | p[1];
4868             last_object=(p[2] << 8) | p[3];
4869             for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
4870             {
4871               if (mng_info->exists[i] && !mng_info->frozen[i])
4872                 {
4873                   MngPair
4874                     new_pair;
4875
4876                   MngPair
4877                     old_pair;
4878
4879                   old_pair.a=mng_info->x_off[i];
4880                   old_pair.b=mng_info->y_off[i];
4881                   new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4882                   mng_info->x_off[i]=new_pair.a;
4883                   mng_info->y_off[i]=new_pair.b;
4884                 }
4885             }
4886
4887             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4888             continue;
4889           }
4890
4891         if (memcmp(type,mng_LOOP,4) == 0)
4892           {
4893             ssize_t loop_iters=1;
4894             loop_level=chunk[0];
4895             mng_info->loop_active[loop_level]=1;  /* mark loop active */
4896
4897             /* Record starting point.  */
4898             loop_iters=mng_get_long(&chunk[1]);
4899
4900             if (logging != MagickFalse)
4901               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4902                 "  LOOP level %.20g has %.20g iterations ",(double) loop_level,
4903                 (double) loop_iters);
4904
4905             if (loop_iters == 0)
4906               skipping_loop=loop_level;
4907
4908             else
4909               {
4910                 mng_info->loop_jump[loop_level]=TellBlob(image);
4911                 mng_info->loop_count[loop_level]=loop_iters;
4912               }
4913
4914             mng_info->loop_iteration[loop_level]=0;
4915             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4916             continue;
4917           }
4918
4919         if (memcmp(type,mng_ENDL,4) == 0)
4920           {
4921             loop_level=chunk[0];
4922
4923             if (skipping_loop > 0)
4924               {
4925                 if (skipping_loop == loop_level)
4926                   {
4927                     /*
4928                       Found end of zero-iteration loop.
4929                     */
4930                     skipping_loop=(-1);
4931                     mng_info->loop_active[loop_level]=0;
4932                   }
4933               }
4934
4935             else
4936               {
4937                 if (mng_info->loop_active[loop_level] == 1)
4938                   {
4939                     mng_info->loop_count[loop_level]--;
4940                     mng_info->loop_iteration[loop_level]++;
4941
4942                     if (logging != MagickFalse)
4943                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4944                         "  ENDL: LOOP level %.20g has %.20g remaining iters ",
4945                         (double) loop_level,(double)
4946                         mng_info->loop_count[loop_level]);
4947
4948                     if (mng_info->loop_count[loop_level] != 0)
4949                       {
4950                         offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4951                           SEEK_SET);
4952
4953                         if (offset < 0)
4954                           ThrowReaderException(CorruptImageError,
4955                             "ImproperImageHeader");
4956                       }
4957
4958                     else
4959                       {
4960                         short
4961                           last_level;
4962
4963                         /*
4964                           Finished loop.
4965                         */
4966                         mng_info->loop_active[loop_level]=0;
4967                         last_level=(-1);
4968                         for (i=0; i < loop_level; i++)
4969                           if (mng_info->loop_active[i] == 1)
4970                             last_level=(short) i;
4971                         loop_level=last_level;
4972                       }
4973                   }
4974               }
4975
4976             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4977             continue;
4978           }
4979
4980         if (memcmp(type,mng_CLON,4) == 0)
4981           {
4982             if (mng_info->clon_warning == 0)
4983               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4984                 CoderError,"CLON is not implemented yet","`%s'",
4985                 image->filename);
4986
4987             mng_info->clon_warning++;
4988           }
4989
4990         if (memcmp(type,mng_MAGN,4) == 0)
4991           {
4992             png_uint_16
4993               magn_first,
4994               magn_last,
4995               magn_mb,
4996               magn_ml,
4997               magn_mr,
4998               magn_mt,
4999               magn_mx,
5000               magn_my,
5001               magn_methx,
5002               magn_methy;
5003
5004             if (length > 1)
5005               magn_first=(p[0] << 8) | p[1];
5006
5007             else
5008               magn_first=0;
5009
5010             if (length > 3)
5011               magn_last=(p[2] << 8) | p[3];
5012
5013             else
5014               magn_last=magn_first;
5015 #ifndef MNG_OBJECT_BUFFERS
5016             if (magn_first || magn_last)
5017               if (mng_info->magn_warning == 0)
5018                 {
5019                   (void) ThrowMagickException(&image->exception,
5020                      GetMagickModule(),CoderError,
5021                      "MAGN is not implemented yet for nonzero objects",
5022                      "`%s'",image->filename);
5023
5024                    mng_info->magn_warning++;
5025                 }
5026 #endif
5027             if (length > 4)
5028               magn_methx=p[4];
5029
5030             else
5031               magn_methx=0;
5032
5033             if (length > 6)
5034               magn_mx=(p[5] << 8) | p[6];
5035
5036             else
5037               magn_mx=1;
5038
5039             if (magn_mx == 0)
5040               magn_mx=1;
5041
5042             if (length > 8)
5043               magn_my=(p[7] << 8) | p[8];
5044
5045             else
5046               magn_my=magn_mx;
5047
5048             if (magn_my == 0)
5049               magn_my=1;
5050
5051             if (length > 10)
5052               magn_ml=(p[9] << 8) | p[10];
5053
5054             else
5055               magn_ml=magn_mx;
5056
5057             if (magn_ml == 0)
5058               magn_ml=1;
5059
5060             if (length > 12)
5061               magn_mr=(p[11] << 8) | p[12];
5062
5063             else
5064               magn_mr=magn_mx;
5065
5066             if (magn_mr == 0)
5067               magn_mr=1;
5068
5069             if (length > 14)
5070               magn_mt=(p[13] << 8) | p[14];
5071
5072             else
5073               magn_mt=magn_my;
5074
5075             if (magn_mt == 0)
5076               magn_mt=1;
5077
5078             if (length > 16)
5079               magn_mb=(p[15] << 8) | p[16];
5080
5081             else
5082               magn_mb=magn_my;
5083
5084             if (magn_mb == 0)
5085               magn_mb=1;
5086
5087             if (length > 17)
5088               magn_methy=p[17];
5089
5090             else
5091               magn_methy=magn_methx;
5092
5093
5094             if (magn_methx > 5 || magn_methy > 5)
5095               if (mng_info->magn_warning == 0)
5096                 {
5097                   (void) ThrowMagickException(&image->exception,
5098                      GetMagickModule(),CoderError,
5099                      "Unknown MAGN method in MNG datastream","`%s'",
5100                      image->filename);
5101
5102                    mng_info->magn_warning++;
5103                 }
5104 #ifdef MNG_OBJECT_BUFFERS
5105           /* Magnify existing objects in the range magn_first to magn_last */
5106 #endif
5107             if (magn_first == 0 || magn_last == 0)
5108               {
5109                 /* Save the magnification factors for object 0 */
5110                 mng_info->magn_mb=magn_mb;
5111                 mng_info->magn_ml=magn_ml;
5112                 mng_info->magn_mr=magn_mr;
5113                 mng_info->magn_mt=magn_mt;
5114                 mng_info->magn_mx=magn_mx;
5115                 mng_info->magn_my=magn_my;
5116                 mng_info->magn_methx=magn_methx;
5117                 mng_info->magn_methy=magn_methy;
5118               }
5119           }
5120
5121         if (memcmp(type,mng_PAST,4) == 0)
5122           {
5123             if (mng_info->past_warning == 0)
5124               (void) ThrowMagickException(&image->exception,GetMagickModule(),
5125                 CoderError,"PAST is not implemented yet","`%s'",
5126                 image->filename);
5127
5128             mng_info->past_warning++;
5129           }
5130
5131         if (memcmp(type,mng_SHOW,4) == 0)
5132           {
5133             if (mng_info->show_warning == 0)
5134               (void) ThrowMagickException(&image->exception,GetMagickModule(),
5135                 CoderError,"SHOW is not implemented yet","`%s'",
5136                 image->filename);
5137
5138             mng_info->show_warning++;
5139           }
5140
5141         if (memcmp(type,mng_sBIT,4) == 0)
5142           {
5143             if (length < 4)
5144               mng_info->have_global_sbit=MagickFalse;
5145
5146             else
5147               {
5148                 mng_info->global_sbit.gray=p[0];
5149                 mng_info->global_sbit.red=p[0];
5150                 mng_info->global_sbit.green=p[1];
5151                 mng_info->global_sbit.blue=p[2];
5152                 mng_info->global_sbit.alpha=p[3];
5153                 mng_info->have_global_sbit=MagickTrue;
5154              }
5155           }
5156         if (memcmp(type,mng_pHYs,4) == 0)
5157           {
5158             if (length > 8)
5159               {
5160                 mng_info->global_x_pixels_per_unit=
5161                     (size_t) mng_get_long(p);
5162                 mng_info->global_y_pixels_per_unit=
5163                     (size_t) mng_get_long(&p[4]);
5164                 mng_info->global_phys_unit_type=p[8];
5165                 mng_info->have_global_phys=MagickTrue;
5166               }
5167
5168             else
5169               mng_info->have_global_phys=MagickFalse;
5170           }
5171         if (memcmp(type,mng_pHYg,4) == 0)
5172           {
5173             if (mng_info->phyg_warning == 0)
5174               (void) ThrowMagickException(&image->exception,GetMagickModule(),
5175                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5176
5177             mng_info->phyg_warning++;
5178           }
5179         if (memcmp(type,mng_BASI,4) == 0)
5180           {
5181             skip_to_iend=MagickTrue;
5182
5183             if (mng_info->basi_warning == 0)
5184               (void) ThrowMagickException(&image->exception,GetMagickModule(),
5185                 CoderError,"BASI is not implemented yet","`%s'",
5186                 image->filename);
5187
5188             mng_info->basi_warning++;
5189 #ifdef MNG_BASI_SUPPORTED
5190             basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5191                (p[2] << 8) | p[3]);
5192             basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5193                (p[6] << 8) | p[7]);
5194             basi_color_type=p[8];
5195             basi_compression_method=p[9];
5196             basi_filter_type=p[10];
5197             basi_interlace_method=p[11];
5198             if (length > 11)
5199               basi_red=(p[12] << 8) & p[13];
5200
5201             else
5202               basi_red=0;
5203
5204             if (length > 13)
5205               basi_green=(p[14] << 8) & p[15];
5206
5207             else
5208               basi_green=0;
5209
5210             if (length > 15)
5211               basi_blue=(p[16] << 8) & p[17];
5212
5213             else
5214               basi_blue=0;
5215
5216             if (length > 17)
5217               basi_alpha=(p[18] << 8) & p[19];
5218
5219             else
5220               {
5221                 if (basi_sample_depth == 16)
5222                   basi_alpha=65535L;
5223                 else
5224                   basi_alpha=255;
5225               }
5226
5227             if (length > 19)
5228               basi_viewable=p[20];
5229
5230             else
5231               basi_viewable=0;
5232
5233 #endif
5234             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5235             continue;
5236           }
5237
5238         if (memcmp(type,mng_IHDR,4)
5239 #if defined(JNG_SUPPORTED)
5240             && memcmp(type,mng_JHDR,4)
5241 #endif
5242             )
5243           {
5244             /* Not an IHDR or JHDR chunk */
5245             if (length)
5246               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5247
5248             continue;
5249           }
5250 /* Process IHDR */
5251         if (logging != MagickFalse)
5252           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5253             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5254
5255         mng_info->exists[object_id]=MagickTrue;
5256         mng_info->viewable[object_id]=MagickTrue;
5257
5258         if (mng_info->invisible[object_id])
5259           {
5260             if (logging != MagickFalse)
5261               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5262                 "  Skipping invisible object");
5263
5264             skip_to_iend=MagickTrue;
5265             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5266             continue;
5267           }
5268 #if defined(MNG_INSERT_LAYERS)
5269         if (length < 8)
5270           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5271
5272         image_width=(size_t) mng_get_long(p);
5273         image_height=(size_t) mng_get_long(&p[4]);
5274 #endif
5275         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5276
5277         /*
5278           Insert a transparent background layer behind the entire animation
5279           if it is not full screen.
5280         */
5281 #if defined(MNG_INSERT_LAYERS)
5282         if (insert_layers && mng_type && first_mng_object)
5283           {
5284             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5285                 (image_width < mng_info->mng_width) ||
5286                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5287                 (image_height < mng_info->mng_height) ||
5288                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5289               {
5290                 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5291                   {
5292                     /*
5293                       Allocate next image structure.
5294                     */
5295                     AcquireNextImage(image_info,image);
5296
5297                     if (GetNextImageInList(image) == (Image *) NULL)
5298                       {
5299                         image=DestroyImageList(image);
5300                         MngInfoFreeStruct(mng_info,&have_mng_structure);
5301                         return((Image *) NULL);
5302                       }
5303
5304                     image=SyncNextImageInList(image);
5305                   }
5306                 mng_info->image=image;
5307
5308                 if (term_chunk_found)
5309                   {
5310                     image->start_loop=MagickTrue;
5311                     image->iterations=mng_iterations;
5312                     term_chunk_found=MagickFalse;
5313                   }
5314
5315                 else
5316                     image->start_loop=MagickFalse;
5317
5318                 /* Make a background rectangle.  */
5319
5320                 image->delay=0;
5321                 image->columns=mng_info->mng_width;
5322                 image->rows=mng_info->mng_height;
5323                 image->page.width=mng_info->mng_width;
5324                 image->page.height=mng_info->mng_height;
5325                 image->page.x=0;
5326                 image->page.y=0;
5327                 image->background_color=mng_background_color;
5328                 (void) SetImageBackgroundColor(image);
5329                 if (logging != MagickFalse)
5330                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5331                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
5332                     (double) mng_info->mng_width,(double) mng_info->mng_height);
5333               }
5334           }
5335         /*
5336           Insert a background layer behind the upcoming image if
5337           framing_mode is 3, and we haven't already inserted one.
5338         */
5339         if (insert_layers && (mng_info->framing_mode == 3) &&
5340                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5341                 (simplicity & 0x08)))
5342           {
5343             if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5344             {
5345               /*
5346                 Allocate next image structure.
5347               */
5348               AcquireNextImage(image_info,image);
5349
5350               if (GetNextImageInList(image) == (Image *) NULL)
5351                 {
5352                   image=DestroyImageList(image);
5353                   MngInfoFreeStruct(mng_info,&have_mng_structure);
5354                   return((Image *) NULL);
5355                 }
5356
5357               image=SyncNextImageInList(image);
5358             }
5359
5360             mng_info->image=image;
5361
5362             if (term_chunk_found)
5363               {
5364                 image->start_loop=MagickTrue;
5365                 image->iterations=mng_iterations;
5366                 term_chunk_found=MagickFalse;
5367               }
5368
5369             else
5370                 image->start_loop=MagickFalse;
5371
5372             image->delay=0;
5373             image->columns=subframe_width;
5374             image->rows=subframe_height;
5375             image->page.width=subframe_width;
5376             image->page.height=subframe_height;
5377             image->page.x=mng_info->clip.left;
5378             image->page.y=mng_info->clip.top;
5379             image->background_color=mng_background_color;
5380             image->matte=MagickFalse;
5381             (void) SetImageBackgroundColor(image);
5382
5383             if (logging != MagickFalse)
5384               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5385                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5386                 (double) mng_info->clip.left,(double) mng_info->clip.right,
5387                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5388           }
5389 #endif /* MNG_INSERT_LAYERS */
5390         first_mng_object=MagickFalse;
5391
5392         if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5393           {
5394             /*
5395               Allocate next image structure.
5396             */
5397             AcquireNextImage(image_info,image);
5398
5399             if (GetNextImageInList(image) == (Image *) NULL)
5400               {
5401                 image=DestroyImageList(image);
5402                 MngInfoFreeStruct(mng_info,&have_mng_structure);
5403                 return((Image *) NULL);
5404               }
5405
5406             image=SyncNextImageInList(image);
5407           }
5408         mng_info->image=image;
5409         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5410           GetBlobSize(image));
5411
5412         if (status == MagickFalse)
5413           break;
5414
5415         if (term_chunk_found)
5416           {
5417             image->start_loop=MagickTrue;
5418             term_chunk_found=MagickFalse;
5419           }
5420
5421         else
5422             image->start_loop=MagickFalse;
5423
5424         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5425           {
5426             image->delay=frame_delay;
5427             frame_delay=default_frame_delay;
5428           }
5429
5430         else
5431           image->delay=0;
5432
5433         image->page.width=mng_info->mng_width;
5434         image->page.height=mng_info->mng_height;
5435         image->page.x=mng_info->x_off[object_id];
5436         image->page.y=mng_info->y_off[object_id];
5437         image->iterations=mng_iterations;
5438
5439         /*
5440           Seek back to the beginning of the IHDR or JHDR chunk's length field.
5441         */
5442
5443         if (logging != MagickFalse)
5444           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5445             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5446             type[2],type[3]);
5447
5448         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
5449
5450         if (offset < 0)
5451           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5452       }
5453
5454     previous=image;
5455     mng_info->image=image;
5456     mng_info->mng_type=mng_type;
5457     mng_info->object_id=object_id;
5458
5459     if (memcmp(type,mng_IHDR,4) == 0)
5460       image=ReadOnePNGImage(mng_info,image_info,exception);
5461
5462 #if defined(JNG_SUPPORTED)
5463     else
5464       image=ReadOneJNGImage(mng_info,image_info,exception);
5465 #endif
5466
5467     if (image == (Image *) NULL)
5468       {
5469         if (IsImageObject(previous) != MagickFalse)
5470           {
5471             (void) DestroyImageList(previous);
5472             (void) CloseBlob(previous);
5473           }
5474
5475         MngInfoFreeStruct(mng_info,&have_mng_structure);
5476         return((Image *) NULL);
5477       }
5478
5479     if (image->columns == 0 || image->rows == 0)
5480       {
5481         (void) CloseBlob(image);
5482         image=DestroyImageList(image);
5483         MngInfoFreeStruct(mng_info,&have_mng_structure);
5484         return((Image *) NULL);
5485       }
5486
5487     mng_info->image=image;
5488
5489     if (mng_type)
5490       {
5491         MngBox
5492           crop_box;
5493
5494         if (mng_info->magn_methx || mng_info->magn_methy)
5495           {
5496             png_uint_32
5497                magnified_height,
5498                magnified_width;
5499
5500             if (logging != MagickFalse)
5501               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5502                 "  Processing MNG MAGN chunk");
5503
5504             if (mng_info->magn_methx == 1)
5505               {
5506                 magnified_width=mng_info->magn_ml;
5507
5508                 if (image->columns > 1)
5509                    magnified_width += mng_info->magn_mr;
5510
5511                 if (image->columns > 2)
5512                    magnified_width += (png_uint_32)
5513                       ((image->columns-2)*(mng_info->magn_mx));
5514               }
5515
5516             else
5517               {
5518                 magnified_width=(png_uint_32) image->columns;
5519
5520                 if (image->columns > 1)
5521                    magnified_width += mng_info->magn_ml-1;
5522
5523                 if (image->columns > 2)
5524                    magnified_width += mng_info->magn_mr-1;
5525
5526                 if (image->columns > 3)
5527                    magnified_width += (png_uint_32)
5528                       ((image->columns-3)*(mng_info->magn_mx-1));
5529               }
5530
5531             if (mng_info->magn_methy == 1)
5532               {
5533                 magnified_height=mng_info->magn_mt;
5534
5535                 if (image->rows > 1)
5536                    magnified_height += mng_info->magn_mb;
5537
5538                 if (image->rows > 2)
5539                    magnified_height += (png_uint_32)
5540                       ((image->rows-2)*(mng_info->magn_my));
5541               }
5542
5543             else
5544               {
5545                 magnified_height=(png_uint_32) image->rows;
5546
5547                 if (image->rows > 1)
5548                    magnified_height += mng_info->magn_mt-1;
5549
5550                 if (image->rows > 2)
5551                    magnified_height += mng_info->magn_mb-1;
5552
5553                 if (image->rows > 3)
5554                    magnified_height += (png_uint_32)
5555                       ((image->rows-3)*(mng_info->magn_my-1));
5556               }
5557
5558             if (magnified_height > image->rows ||
5559                 magnified_width > image->columns)
5560               {
5561                 Image
5562                   *large_image;
5563
5564                 int
5565                   yy;
5566
5567                 ssize_t
5568                   m,
5569                   y;
5570
5571                 register ssize_t
5572                   x;
5573
5574                 register PixelPacket
5575                   *n,
5576                   *q;
5577
5578                 PixelPacket
5579                   *next,
5580                   *prev;
5581
5582                 png_uint_16
5583                   magn_methx,
5584                   magn_methy;
5585
5586                 /* Allocate next image structure.  */
5587
5588                 if (logging != MagickFalse)
5589                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5590                     "    Allocate magnified image");
5591
5592                 AcquireNextImage(image_info,image);
5593
5594                 if (GetNextImageInList(image) == (Image *) NULL)
5595                   {
5596                     image=DestroyImageList(image);
5597                     MngInfoFreeStruct(mng_info,&have_mng_structure);
5598                     return((Image *) NULL);
5599                   }
5600
5601                 large_image=SyncNextImageInList(image);
5602
5603                 large_image->columns=magnified_width;
5604                 large_image->rows=magnified_height;
5605
5606                 magn_methx=mng_info->magn_methx;
5607                 magn_methy=mng_info->magn_methy;
5608
5609 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5610 #define QM unsigned short
5611                 if (magn_methx != 1 || magn_methy != 1)
5612                   {
5613                   /*
5614                      Scale pixels to unsigned shorts to prevent
5615                      overflow of intermediate values of interpolations
5616                   */
5617                      for (y=0; y < (ssize_t) image->rows; y++)
5618                      {
5619                        q=GetAuthenticPixels(image,0,y,image->columns,1,
5620                           exception);
5621
5622                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
5623                        {
5624                           q->red=ScaleQuantumToShort(q->red);
5625                           q->green=ScaleQuantumToShort(q->green);
5626                           q->blue=ScaleQuantumToShort(q->blue);
5627                           q->opacity=ScaleQuantumToShort(q->opacity);
5628                           q++;
5629                        }
5630
5631                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
5632                          break;
5633                      }
5634                   }
5635 #else
5636 #define QM Quantum
5637 #endif
5638
5639                 if (image->matte != MagickFalse)
5640                    (void) SetImageBackgroundColor(large_image);
5641
5642                 else
5643                   {
5644                     large_image->background_color.opacity=OpaqueOpacity;
5645                     (void) SetImageBackgroundColor(large_image);
5646
5647                     if (magn_methx == 4)
5648                       magn_methx=2;
5649
5650                     if (magn_methx == 5)
5651                       magn_methx=3;
5652
5653                     if (magn_methy == 4)
5654                       magn_methy=2;
5655
5656                     if (magn_methy == 5)
5657                       magn_methy=3;
5658                   }
5659
5660                 /* magnify the rows into the right side of the large image */
5661
5662                 if (logging != MagickFalse)
5663                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5664                     "    Magnify the rows to %.20g",(double) large_image->rows);
5665                 m=(ssize_t) mng_info->magn_mt;
5666                 yy=0;
5667                 length=(size_t) image->columns;
5668                 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5669                 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5670
5671                 if ((prev == (PixelPacket *) NULL) ||
5672                     (next == (PixelPacket *) NULL))
5673                   {
5674                      image=DestroyImageList(image);
5675                      MngInfoFreeStruct(mng_info,&have_mng_structure);
5676                      ThrowReaderException(ResourceLimitError,
5677                        "MemoryAllocationFailed");
5678                   }
5679
5680                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5681                 (void) CopyMagickMemory(next,n,length);
5682
5683                 for (y=0; y < (ssize_t) image->rows; y++)
5684                 {
5685                   if (y == 0)
5686                     m=(ssize_t) mng_info->magn_mt;
5687
5688                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5689                     m=(ssize_t) mng_info->magn_mb;
5690
5691                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5692                     m=(ssize_t) mng_info->magn_mb;
5693
5694                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
5695                     m=1;
5696
5697                   else
5698                     m=(ssize_t) mng_info->magn_my;
5699
5700                   n=prev;
5701                   prev=next;
5702                   next=n;
5703
5704                   if (y < (ssize_t) image->rows-1)
5705                     {
5706                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5707                           exception);
5708                       (void) CopyMagickMemory(next,n,length);
5709                     }
5710
5711                   for (i=0; i < m; i++, yy++)
5712                   {
5713                     register PixelPacket
5714                       *pixels;
5715
5716                     assert(yy < (ssize_t) large_image->rows);
5717                     pixels=prev;
5718                     n=next;
5719                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5720                           1,exception);
5721                     q+=(large_image->columns-image->columns);
5722
5723                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
5724                     {
5725                       /* To do: get color as function of indexes[x] */
5726                       /*
5727                       if (image->storage_class == PseudoClass)
5728                         {
5729                         }
5730                       */
5731
5732                       if (magn_methy <= 1)
5733                         {
5734                           *q=(*pixels); /* replicate previous */
5735                         }
5736
5737                       else if (magn_methy == 2 || magn_methy == 4)
5738                         {
5739                           if (i == 0)
5740                              *q=(*pixels);
5741
5742                           else
5743                             {
5744                               /* Interpolate */
5745                               (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5746                                  -(*pixels).red)+m))/((ssize_t) (m*2))
5747                                  +(*pixels).red);
5748                               (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5749                                  -(*pixels).green)+m))/((ssize_t) (m*2))
5750                                  +(*pixels).green);
5751                               (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5752                                  -(*pixels).blue)+m))/((ssize_t) (m*2))
5753                                  +(*pixels).blue);
5754
5755                               if (image->matte != MagickFalse)
5756                                  (*q).opacity=(QM) (((ssize_t)
5757                                  (2*i*((*n).opacity
5758                                  -(*pixels).opacity)+m))
5759                                  /((ssize_t) (m*2))+(*pixels).opacity);
5760                             }
5761
5762                           if (magn_methy == 4)
5763                             {
5764                               /* Replicate nearest */
5765                               if (i <= ((m+1) << 1))
5766                                  (*q).opacity=(*pixels).opacity+0;
5767                               else
5768                                  (*q).opacity=(*n).opacity+0;
5769                             }
5770                         }
5771
5772                       else /* if (magn_methy == 3 || magn_methy == 5) */
5773                         {
5774                           /* Replicate nearest */
5775                           if (i <= ((m+1) << 1))
5776                              *q=(*pixels);
5777
5778                           else
5779                              *q=(*n);
5780
5781                           if (magn_methy == 5)
5782                             {
5783                               (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5784                                  -(*pixels).opacity)+m))/((ssize_t) (m*2))
5785                                  +(*pixels).opacity);
5786                             }
5787                         }
5788                       n++;
5789                       q++;
5790                       pixels++;
5791                     } /* x */
5792
5793                     if (SyncAuthenticPixels(large_image,exception) == 0)
5794                       break;
5795
5796                   } /* i */
5797                 } /* y */
5798
5799                 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5800                 next=(PixelPacket *) RelinquishMagickMemory(next);
5801
5802                 length=image->columns;
5803
5804                 if (logging != MagickFalse)
5805                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5806                     "    Delete original image");
5807
5808                 DeleteImageFromList(&image);
5809
5810                 image=large_image;
5811
5812                 mng_info->image=image;
5813
5814                 /* magnify the columns */
5815                 if (logging != MagickFalse)
5816                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5817                     "    Magnify the columns to %.20g",(double) image->columns);
5818
5819                 for (y=0; y < (ssize_t) image->rows; y++)
5820                 {
5821                   register PixelPacket
5822                     *pixels;
5823
5824                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5825                   pixels=q+(image->columns-length);
5826                   n=pixels+1;
5827
5828                   for (x=(ssize_t) (image->columns-length);
5829                     x < (ssize_t) image->columns; x++)
5830                   {
5831                     if (x == (ssize_t) (image->columns-length))
5832                       m=(ssize_t) mng_info->magn_ml;
5833
5834                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5835                       m=(ssize_t) mng_info->magn_mr;
5836
5837                     else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5838                       m=(ssize_t) mng_info->magn_mr;
5839
5840                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
5841                       m=1;
5842
5843                     else
5844                       m=(ssize_t) mng_info->magn_mx;
5845
5846                     for (i=0; i < m; i++)
5847                     {
5848                       if (magn_methx <= 1)
5849                         {
5850                           /* replicate previous */
5851                           *q=(*pixels);
5852                         }
5853
5854                       else if (magn_methx == 2 || magn_methx == 4)
5855                         {
5856                           if (i == 0)
5857                             *q=(*pixels);
5858
5859                           else
5860                             {
5861                               /* Interpolate */
5862                               (*q).red=(QM) ((2*i*((*n).red
5863                                  -(*pixels).red)+m)
5864                                  /((ssize_t) (m*2))+(*pixels).red);
5865                               (*q).green=(QM) ((2*i*((*n).green
5866                                  -(*pixels).green)
5867                                  +m)/((ssize_t) (m*2))+(*pixels).green);
5868                               (*q).blue=(QM) ((2*i*((*n).blue
5869                                  -(*pixels).blue)+m)
5870                                  /((ssize_t) (m*2))+(*pixels).blue);
5871                               if (image->matte != MagickFalse)
5872                                  (*q).opacity=(QM) ((2*i*((*n).opacity
5873                                    -(*pixels).opacity)+m)/((ssize_t) (m*2))
5874                                    +(*pixels).opacity);
5875                             }
5876
5877                           if (magn_methx == 4)
5878                             {
5879                               /* Replicate nearest */
5880                               if (i <= ((m+1) << 1))
5881                                  (*q).opacity=(*pixels).opacity+0;
5882                               else
5883                                  (*q).opacity=(*n).opacity+0;
5884                             }
5885                         }
5886
5887                       else /* if (magn_methx == 3 || magn_methx == 5) */
5888                         {
5889                           /* Replicate nearest */
5890                           if (i <= ((m+1) << 1))
5891                              *q=(*pixels);
5892
5893                           else
5894                              *q=(*n);
5895
5896                           if (magn_methx == 5)
5897                             {
5898                               /* Interpolate */
5899                               (*q).opacity=(QM) ((2*i*((*n).opacity
5900                                  -(*pixels).opacity)+m) /((ssize_t) (m*2))
5901                                  +(*pixels).opacity);
5902                             }
5903                         }
5904                       q++;
5905                     }
5906                     n++;
5907                     p++;
5908                   }
5909
5910                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
5911                     break;
5912                 }
5913 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5914               if (magn_methx != 1 || magn_methy != 1)
5915                 {
5916                 /*
5917                    Rescale pixels to Quantum
5918                 */
5919                    for (y=0; y < (ssize_t) image->rows; y++)
5920                    {
5921                      q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5922
5923                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
5924                      {
5925                         q->red=ScaleShortToQuantum(q->red);
5926                         q->green=ScaleShortToQuantum(q->green);
5927                         q->blue=ScaleShortToQuantum(q->blue);
5928                         q->opacity=ScaleShortToQuantum(q->opacity);
5929                         q++;
5930                      }
5931
5932                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
5933                        break;
5934                    }
5935                 }
5936 #endif
5937                 if (logging != MagickFalse)
5938                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5939                     "  Finished MAGN processing");
5940               }
5941           }
5942
5943         /*
5944           Crop_box is with respect to the upper left corner of the MNG.
5945         */
5946         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5947         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5948         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5949         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5950         crop_box=mng_minimum_box(crop_box,mng_info->clip);
5951         crop_box=mng_minimum_box(crop_box,mng_info->frame);
5952         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5953         if ((crop_box.left != (mng_info->image_box.left
5954             +mng_info->x_off[object_id])) ||
5955             (crop_box.right != (mng_info->image_box.right
5956             +mng_info->x_off[object_id])) ||
5957             (crop_box.top != (mng_info->image_box.top
5958             +mng_info->y_off[object_id])) ||
5959             (crop_box.bottom != (mng_info->image_box.bottom
5960             +mng_info->y_off[object_id])))
5961           {
5962             if (logging != MagickFalse)
5963               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5964                 "  Crop the PNG image");
5965
5966             if ((crop_box.left < crop_box.right) &&
5967                 (crop_box.top < crop_box.bottom))
5968               {
5969                 Image
5970                   *im;
5971
5972                 RectangleInfo
5973                   crop_info;
5974
5975                 /*
5976                   Crop_info is with respect to the upper left corner of
5977                   the image.
5978                 */
5979                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5980                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
5981                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
5982                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
5983                 image->page.width=image->columns;
5984                 image->page.height=image->rows;
5985                 image->page.x=0;
5986                 image->page.y=0;
5987                 im=CropImage(image,&crop_info,exception);
5988
5989                 if (im != (Image *) NULL)
5990                   {
5991                     image->columns=im->columns;
5992                     image->rows=im->rows;
5993                     im=DestroyImage(im);
5994                     image->page.width=image->columns;
5995                     image->page.height=image->rows;
5996                     image->page.x=crop_box.left;
5997                     image->page.y=crop_box.top;
5998                   }
5999               }
6000
6001             else
6002               {
6003                 /*
6004                   No pixels in crop area.  The MNG spec still requires
6005                   a layer, though, so make a single transparent pixel in
6006                   the top left corner.
6007                 */
6008                 image->columns=1;
6009                 image->rows=1;
6010                 image->colors=2;
6011                 (void) SetImageBackgroundColor(image);
6012                 image->page.width=1;
6013                 image->page.height=1;
6014                 image->page.x=0;
6015                 image->page.y=0;
6016               }
6017           }
6018 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6019         image=mng_info->image;
6020 #endif
6021       }
6022
6023 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6024       /* PNG does not handle depths greater than 16 so reduce it even
6025        * if lossy
6026        */
6027       if (image->depth > 16)
6028          image->depth=16;
6029 #endif
6030
6031 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6032       if (LosslessReduceDepthOK(image) != MagickFalse)
6033          image->depth = 8;
6034 #endif
6035
6036       GetImageException(image,exception);
6037
6038       if (image_info->number_scenes != 0)
6039         {
6040           if (mng_info->scenes_found >
6041              (ssize_t) (image_info->first_scene+image_info->number_scenes))
6042             break;
6043         }
6044
6045       if (logging != MagickFalse)
6046         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6047           "  Finished reading image datastream.");
6048
6049   } while (LocaleCompare(image_info->magick,"MNG") == 0);
6050
6051   (void) CloseBlob(image);
6052
6053   if (logging != MagickFalse)
6054     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6055       "  Finished reading all image datastreams.");
6056
6057 #if defined(MNG_INSERT_LAYERS)
6058   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6059        (mng_info->mng_height))
6060     {
6061       /*
6062         Insert a background layer if nothing else was found.
6063       */
6064       if (logging != MagickFalse)
6065         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6066           "  No images found.  Inserting a background layer.");
6067
6068       if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6069         {
6070           /*
6071             Allocate next image structure.
6072           */
6073           AcquireNextImage(image_info,image);
6074           if (GetNextImageInList(image) == (Image *) NULL)
6075             {
6076               image=DestroyImageList(image);
6077               MngInfoFreeStruct(mng_info,&have_mng_structure);
6078
6079               if (logging != MagickFalse)
6080                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6081                   "  Allocation failed, returning NULL.");
6082
6083               return((Image *) NULL);
6084             }
6085           image=SyncNextImageInList(image);
6086         }
6087       image->columns=mng_info->mng_width;
6088       image->rows=mng_info->mng_height;
6089       image->page.width=mng_info->mng_width;
6090       image->page.height=mng_info->mng_height;
6091       image->page.x=0;
6092       image->page.y=0;
6093       image->background_color=mng_background_color;
6094       image->matte=MagickFalse;
6095
6096       if (image_info->ping == MagickFalse)
6097         (void) SetImageBackgroundColor(image);
6098
6099       mng_info->image_found++;
6100     }
6101 #endif
6102   image->iterations=mng_iterations;
6103
6104   if (mng_iterations == 1)
6105     image->start_loop=MagickTrue;
6106
6107   while (GetPreviousImageInList(image) != (Image *) NULL)
6108   {
6109     image_count++;
6110     if (image_count > 10*mng_info->image_found)
6111       {
6112         if (logging != MagickFalse)
6113           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
6114
6115         (void) ThrowMagickException(&image->exception,GetMagickModule(),
6116           CoderError,"Linked list is corrupted, beginning of list not found",
6117           "`%s'",image_info->filename);
6118
6119         return((Image *) NULL);
6120       }
6121
6122     image=GetPreviousImageInList(image);
6123
6124     if (GetNextImageInList(image) == (Image *) NULL)
6125       {
6126         if (logging != MagickFalse)
6127           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
6128
6129         (void) ThrowMagickException(&image->exception,GetMagickModule(),
6130           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6131           image_info->filename);
6132       }
6133   }
6134
6135   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6136              GetNextImageInList(image) ==
6137      (Image *) NULL)
6138     {
6139       if (logging != MagickFalse)
6140         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6141             "  First image null");
6142
6143       (void) ThrowMagickException(&image->exception,GetMagickModule(),
6144         CoderError,"image->next for first image is NULL but shouldn't be.",
6145         "`%s'",image_info->filename);
6146     }
6147
6148   if (mng_info->image_found == 0)
6149     {
6150       if (logging != MagickFalse)
6151         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6152           "  No visible images found.");
6153
6154       (void) ThrowMagickException(&image->exception,GetMagickModule(),
6155         CoderError,"No visible images in file","`%s'",image_info->filename);
6156
6157       if (image != (Image *) NULL)
6158         image=DestroyImageList(image);
6159
6160       MngInfoFreeStruct(mng_info,&have_mng_structure);
6161       return((Image *) NULL);
6162     }
6163
6164   if (mng_info->ticks_per_second)
6165     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6166             final_delay/mng_info->ticks_per_second;
6167
6168   else
6169     image->start_loop=MagickTrue;
6170
6171   /* Find final nonzero image delay */
6172   final_image_delay=0;
6173
6174   while (GetNextImageInList(image) != (Image *) NULL)
6175     {
6176       if (image->delay)
6177         final_image_delay=image->delay;
6178
6179       image=GetNextImageInList(image);
6180     }
6181
6182   if (final_delay < final_image_delay)
6183     final_delay=final_image_delay;
6184
6185   image->delay=final_delay;
6186
6187   if (logging != MagickFalse)
6188       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6189         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6190         (double) final_delay);
6191
6192   if (logging != MagickFalse)
6193     {
6194       int
6195         scene;
6196
6197       scene=0;
6198       image=GetFirstImageInList(image);
6199
6200       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6201         "  Before coalesce:");
6202
6203       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6204         "    scene 0 delay=%.20g",(double) image->delay);
6205
6206       while (GetNextImageInList(image) != (Image *) NULL)
6207       {
6208         image=GetNextImageInList(image);
6209         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6210           "    scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
6211       }
6212     }
6213
6214   image=GetFirstImageInList(image);
6215 #ifdef MNG_COALESCE_LAYERS
6216   if (insert_layers)
6217     {
6218       Image
6219         *next_image,
6220         *next;
6221
6222       size_t
6223         scene;
6224
6225       if (logging != MagickFalse)
6226         (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Coalesce Images");
6227
6228       scene=image->scene;
6229       next_image=CoalesceImages(image,&image->exception);
6230
6231       if (next_image == (Image *) NULL)
6232         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
6233
6234       image=DestroyImageList(image);
6235       image=next_image;
6236
6237       for (next=image; next != (Image *) NULL; next=next_image)
6238       {
6239          next->page.width=mng_info->mng_width;
6240          next->page.height=mng_info->mng_height;
6241          next->page.x=0;
6242          next->page.y=0;
6243          next->scene=scene++;
6244          next_image=GetNextImageInList(next);
6245
6246          if (next_image == (Image *) NULL)
6247            break;
6248
6249          if (next->delay == 0)
6250            {
6251              scene--;
6252              next_image->previous=GetPreviousImageInList(next);
6253              if (GetPreviousImageInList(next) == (Image *) NULL)
6254                image=next_image;
6255              else
6256                next->previous->next=next_image;
6257              next=DestroyImage(next);
6258            }
6259       }
6260     }
6261 #endif
6262
6263   while (GetNextImageInList(image) != (Image *) NULL)
6264       image=GetNextImageInList(image);
6265
6266   image->dispose=BackgroundDispose;
6267
6268   if (logging != MagickFalse)
6269     {
6270       int
6271         scene;
6272
6273       scene=0;
6274       image=GetFirstImageInList(image);
6275
6276       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6277         "  After coalesce:");
6278
6279       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6280         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6281         (double) image->dispose);
6282
6283       while (GetNextImageInList(image) != (Image *) NULL)
6284       {
6285         image=GetNextImageInList(image);
6286
6287         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6288           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6289           (double) image->delay,(double) image->dispose);
6290       }
6291    }
6292
6293   image=GetFirstImageInList(image);
6294   MngInfoFreeStruct(mng_info,&have_mng_structure);
6295   have_mng_structure=MagickFalse;
6296
6297   if (logging != MagickFalse)
6298     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6299
6300   return(GetFirstImageInList(image));
6301 }
6302 #else /* PNG_LIBPNG_VER > 10011 */
6303 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6304 {
6305   printf("Your PNG library is too old: You have libpng-%s\n",
6306      PNG_LIBPNG_VER_STRING);
6307
6308   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6309     "PNG library is too old","`%s'",image_info->filename);
6310
6311   return(Image *) NULL;
6312 }
6313
6314 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6315 {
6316   return(ReadPNGImage(image_info,exception));
6317 }
6318 #endif /* PNG_LIBPNG_VER > 10011 */
6319 #endif
6320 \f
6321 /*
6322 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6323 %                                                                             %
6324 %                                                                             %
6325 %                                                                             %
6326 %   R e g i s t e r P N G I m a g e                                           %
6327 %                                                                             %
6328 %                                                                             %
6329 %                                                                             %
6330 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6331 %
6332 %  RegisterPNGImage() adds properties for the PNG image format to
6333 %  the list of supported formats.  The properties include the image format
6334 %  tag, a method to read and/or write the format, whether the format
6335 %  supports the saving of more than one frame to the same file or blob,
6336 %  whether the format supports native in-memory I/O, and a brief
6337 %  description of the format.
6338 %
6339 %  The format of the RegisterPNGImage method is:
6340 %
6341 %      size_t RegisterPNGImage(void)
6342 %
6343 */
6344 ModuleExport size_t RegisterPNGImage(void)
6345 {
6346   char
6347     version[MaxTextExtent];
6348
6349   MagickInfo
6350     *entry;
6351
6352   static const char
6353     *PNGNote=
6354     {
6355       "See http://www.libpng.org/ for details about the PNG format."
6356     },
6357
6358     *JNGNote=
6359     {
6360       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6361       "format."
6362     },
6363
6364     *MNGNote=
6365     {
6366       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6367       "format."
6368     };
6369
6370   *version='\0';
6371
6372 #if defined(PNG_LIBPNG_VER_STRING)
6373   (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6374   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
6375
6376   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6377     {
6378       (void) ConcatenateMagickString(version,",",MaxTextExtent);
6379       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6380             MaxTextExtent);
6381     }
6382 #endif
6383
6384   entry=SetMagickInfo("MNG");
6385   entry->seekable_stream=MagickTrue;  /* To do: eliminate this. */
6386
6387 #if defined(MAGICKCORE_PNG_DELEGATE)
6388   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6389   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6390 #endif
6391
6392   entry->magick=(IsImageFormatHandler *) IsMNG;
6393   entry->description=ConstantString("Multiple-image Network Graphics");
6394
6395   if (*version != '\0')
6396     entry->version=ConstantString(version);
6397
6398   entry->module=ConstantString("PNG");
6399   entry->note=ConstantString(MNGNote);
6400   (void) RegisterMagickInfo(entry);
6401
6402   entry=SetMagickInfo("PNG");
6403
6404 #if defined(MAGICKCORE_PNG_DELEGATE)
6405   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6406   entry->encoder=(EncodeImageHandler *) WritePNGImage;
6407 #endif
6408
6409   entry->magick=(IsImageFormatHandler *) IsPNG;
6410   entry->adjoin=MagickFalse;
6411   entry->description=ConstantString("Portable Network Graphics");
6412   entry->module=ConstantString("PNG");
6413
6414   if (*version != '\0')
6415     entry->version=ConstantString(version);
6416
6417   entry->note=ConstantString(PNGNote);
6418   (void) RegisterMagickInfo(entry);
6419
6420   entry=SetMagickInfo("PNG8");
6421
6422 #if defined(MAGICKCORE_PNG_DELEGATE)
6423   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6424   entry->encoder=(EncodeImageHandler *) WritePNGImage;
6425 #endif
6426
6427   entry->magick=(IsImageFormatHandler *) IsPNG;
6428   entry->adjoin=MagickFalse;
6429   entry->description=ConstantString(
6430             "8-bit indexed with optional binary transparency");
6431   entry->module=ConstantString("PNG");
6432   (void) RegisterMagickInfo(entry);
6433
6434   entry=SetMagickInfo("PNG24");
6435   *version='\0';
6436
6437 #if defined(ZLIB_VERSION)
6438   (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6439   (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6440
6441   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6442     {
6443       (void) ConcatenateMagickString(version,",",MaxTextExtent);
6444       (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6445     }
6446 #endif
6447
6448   if (*version != '\0')
6449     entry->version=ConstantString(version);
6450
6451 #if defined(MAGICKCORE_PNG_DELEGATE)
6452   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6453   entry->encoder=(EncodeImageHandler *) WritePNGImage;
6454 #endif
6455
6456   entry->magick=(IsImageFormatHandler *) IsPNG;
6457   entry->adjoin=MagickFalse;
6458   entry->description=ConstantString("opaque 24-bit RGB");
6459   entry->module=ConstantString("PNG");
6460   (void) RegisterMagickInfo(entry);
6461
6462   entry=SetMagickInfo("PNG32");
6463
6464 #if defined(MAGICKCORE_PNG_DELEGATE)
6465   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6466   entry->encoder=(EncodeImageHandler *) WritePNGImage;
6467 #endif
6468
6469   entry->magick=(IsImageFormatHandler *) IsPNG;
6470   entry->adjoin=MagickFalse;
6471   entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6472   entry->module=ConstantString("PNG");
6473   (void) RegisterMagickInfo(entry);
6474
6475   entry=SetMagickInfo("JNG");
6476
6477 #if defined(JNG_SUPPORTED)
6478 #if defined(MAGICKCORE_PNG_DELEGATE)
6479   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6480   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6481 #endif
6482 #endif
6483
6484   entry->magick=(IsImageFormatHandler *) IsJNG;
6485   entry->adjoin=MagickFalse;
6486   entry->description=ConstantString("JPEG Network Graphics");
6487   entry->module=ConstantString("PNG");
6488   entry->note=ConstantString(JNGNote);
6489   (void) RegisterMagickInfo(entry);
6490
6491 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6492   ping_semaphore=AllocateSemaphoreInfo();
6493 #endif
6494
6495   return(MagickImageCoderSignature);
6496 }
6497 \f
6498 /*
6499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6500 %                                                                             %
6501 %                                                                             %
6502 %                                                                             %
6503 %   U n r e g i s t e r P N G I m a g e                                       %
6504 %                                                                             %
6505 %                                                                             %
6506 %                                                                             %
6507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6508 %
6509 %  UnregisterPNGImage() removes format registrations made by the
6510 %  PNG module from the list of supported formats.
6511 %
6512 %  The format of the UnregisterPNGImage method is:
6513 %
6514 %      UnregisterPNGImage(void)
6515 %
6516 */
6517 ModuleExport void UnregisterPNGImage(void)
6518 {
6519   (void) UnregisterMagickInfo("MNG");
6520   (void) UnregisterMagickInfo("PNG");
6521   (void) UnregisterMagickInfo("PNG8");
6522   (void) UnregisterMagickInfo("PNG24");
6523   (void) UnregisterMagickInfo("PNG32");
6524   (void) UnregisterMagickInfo("JNG");
6525
6526 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6527   if (ping_semaphore != (SemaphoreInfo *) NULL)
6528     DestroySemaphoreInfo(&ping_semaphore);
6529 #endif
6530 }
6531 \f
6532 #if defined(MAGICKCORE_PNG_DELEGATE)
6533 #if PNG_LIBPNG_VER > 10011
6534 /*
6535 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6536 %                                                                             %
6537 %                                                                             %
6538 %                                                                             %
6539 %   W r i t e M N G I m a g e                                                 %
6540 %                                                                             %
6541 %                                                                             %
6542 %                                                                             %
6543 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6544 %
6545 %  WriteMNGImage() writes an image in the Portable Network Graphics
6546 %  Group's "Multiple-image Network Graphics" encoded image format.
6547 %
6548 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
6549 %
6550 %  The format of the WriteMNGImage method is:
6551 %
6552 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6553 %
6554 %  A description of each parameter follows.
6555 %
6556 %    o image_info: the image info.
6557 %
6558 %    o image:  The image.
6559 %
6560 %
6561 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6562 %    "To do" under ReadPNGImage):
6563 %
6564 %    Preserve all unknown and not-yet-handled known chunks found in input
6565 %    PNG file and copy them  into output PNG files according to the PNG
6566 %    copying rules.
6567 %
6568 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
6569 %
6570 %    Improve selection of color type (use indexed-colour or indexed-colour
6571 %    with tRNS when 256 or fewer unique RGBA values are present).
6572 %
6573 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6574 %    This will be complicated if we limit ourselves to generating MNG-LC
6575 %    files.  For now we ignore disposal method 3 and simply overlay the next
6576 %    image on it.
6577 %
6578 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
6579 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
6580 %    [mostly done 15 June 1999 but still need to take care of tRNS]
6581 %
6582 %    Check for identical sRGB and replace with a global sRGB (and remove
6583 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6584 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6585 %    local gAMA/cHRM with local sRGB if appropriate).
6586 %
6587 %    Check for identical sBIT chunks and write global ones.
6588 %
6589 %    Provide option to skip writing the signature tEXt chunks.
6590 %
6591 %    Use signatures to detect identical objects and reuse the first
6592 %    instance of such objects instead of writing duplicate objects.
6593 %
6594 %    Use a smaller-than-32k value of compression window size when
6595 %    appropriate.
6596 %
6597 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
6598 %    ancillary text chunks and save profiles.
6599 %
6600 %    Provide an option to force LC files (to ensure exact framing rate)
6601 %    instead of VLC.
6602 %
6603 %    Provide an option to force VLC files instead of LC, even when offsets
6604 %    are present.  This will involve expanding the embedded images with a
6605 %    transparent region at the top and/or left.
6606 */
6607
6608 static void
6609 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6610    png_info *ping_info, unsigned char *profile_type, unsigned char
6611    *profile_description, unsigned char *profile_data, png_uint_32 length)
6612 {
6613    png_textp
6614      text;
6615
6616    register ssize_t
6617      i;
6618
6619    unsigned char
6620      *sp;
6621
6622    png_charp
6623      dp;
6624
6625    png_uint_32
6626      allocated_length,
6627      description_length;
6628
6629    unsigned char
6630      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6631
6632    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6633       return;
6634
6635    if (image_info->verbose)
6636      {
6637        (void) printf("writing raw profile: type=%s, length=%.20g\n",
6638          (char *) profile_type, (double) length);
6639      }
6640
6641    text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6642    description_length=(png_uint_32) strlen((const char *) profile_description);
6643    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6644       + description_length);
6645    text[0].text=(png_charp) png_malloc(ping,allocated_length);
6646    text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6647    text[0].key[0]='\0';
6648    (void) ConcatenateMagickString(text[0].key,
6649       "Raw profile type ",MaxTextExtent);
6650    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6651    sp=profile_data;
6652    dp=text[0].text;
6653    *dp++='\n';
6654    (void) CopyMagickString(dp,(const char *) profile_description,
6655      allocated_length);
6656    dp+=description_length;
6657    *dp++='\n';
6658    (void) FormatMagickString(dp,allocated_length-
6659      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6660    dp+=8;
6661
6662    for (i=0; i < (ssize_t) length; i++)
6663    {
6664      if (i%36 == 0)
6665        *dp++='\n';
6666      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6667      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6668    }
6669
6670    *dp++='\n';
6671    *dp='\0';
6672    text[0].text_length=(png_size_t) (dp-text[0].text);
6673    text[0].compression=image_info->compression == NoCompression ||
6674      (image_info->compression == UndefinedCompression &&
6675      text[0].text_length < 128) ? -1 : 0;
6676
6677    if (text[0].text_length <= allocated_length)
6678      png_set_text(ping,ping_info,text,1);
6679
6680    png_free(ping,text[0].text);
6681    png_free(ping,text[0].key);
6682    png_free(ping,text);
6683 }
6684
6685 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
6686   const char *string, MagickBooleanType logging)
6687 {
6688   char
6689     *name;
6690
6691   const StringInfo
6692     *profile;
6693
6694   unsigned char
6695     *data;
6696
6697   png_uint_32 length;
6698
6699   ResetImageProfileIterator(image);
6700
6701   for (name=GetNextImageProfile(image); name != (const char *) NULL; )
6702   {
6703     profile=GetImageProfile(image,name);
6704
6705     if (profile != (const StringInfo *) NULL)
6706       {
6707         StringInfo
6708           *ping_profile;
6709
6710         if (LocaleNCompare(name,string,11) == 0)
6711           {
6712             if (logging != MagickFalse)
6713                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6714                    "  Found %s profile",name);
6715
6716             ping_profile=CloneStringInfo(profile);
6717             data=GetStringInfoDatum(ping_profile),
6718             length=(png_uint_32) GetStringInfoLength(ping_profile);
6719             data[4]=data[3];
6720             data[3]=data[2];
6721             data[2]=data[1];
6722             data[1]=data[0];
6723             (void) WriteBlobMSBULong(image,length-5);  /* data length */
6724             (void) WriteBlob(image,length-1,data+1);
6725             (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6726             ping_profile=DestroyStringInfo(ping_profile);
6727           }
6728       }
6729
6730       name=GetNextImageProfile(image);
6731    }
6732
6733    return(MagickTrue);
6734 }
6735
6736
6737 /* Write one PNG image */
6738 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6739    const ImageInfo *IMimage_info,Image *IMimage)
6740 {
6741   Image
6742     *image;
6743
6744   ImageInfo
6745     *image_info;
6746
6747   char
6748     s[2];
6749
6750   const char
6751     *name,
6752     *property,
6753     *value;
6754
6755   const StringInfo
6756     *profile;
6757
6758   int
6759     num_passes,
6760     pass;
6761
6762   png_byte
6763      ping_trans_alpha[256];
6764
6765   png_color
6766      palette[257];
6767
6768   png_color_16
6769     ping_background,
6770     ping_trans_color;
6771
6772   png_info
6773     *ping_info;
6774
6775   png_struct
6776     *ping;
6777
6778   png_uint_32
6779     ping_height,
6780     ping_width;
6781
6782   ssize_t
6783     y;
6784
6785   MagickBooleanType
6786     image_matte,
6787     logging,
6788     matte,
6789
6790     ping_have_blob,
6791     ping_have_cheap_transparency,
6792     ping_have_color,
6793     ping_have_non_bw,
6794     ping_have_PLTE,
6795     ping_have_bKGD,
6796     ping_have_pHYs,
6797     ping_have_tRNS,
6798
6799     ping_exclude_bKGD,
6800     ping_exclude_cHRM,
6801     /* ping_exclude_EXIF, */
6802     ping_exclude_gAMA,
6803     ping_exclude_iCCP,
6804     /* ping_exclude_iTXt, */
6805     ping_exclude_oFFs,
6806     ping_exclude_pHYs,
6807     ping_exclude_sRGB,
6808     ping_exclude_tEXt,
6809     /* ping_exclude_tRNS, */
6810     ping_exclude_vpAg,
6811     ping_exclude_zCCP, /* hex-encoded iCCP */
6812     ping_exclude_zTXt,
6813
6814     ping_need_colortype_warning,
6815
6816     status;
6817
6818   QuantumInfo
6819     *quantum_info;
6820
6821   register ssize_t
6822     i,
6823     x;
6824
6825   unsigned char
6826     *ping_pixels;
6827
6828   volatile int
6829     image_colors,
6830     ping_bit_depth,
6831     ping_color_type,
6832     ping_interlace_method,
6833     ping_compression_method,
6834     ping_filter_method,
6835     ping_num_trans;
6836
6837   volatile size_t
6838     image_depth,
6839     old_bit_depth;
6840
6841   size_t
6842     quality,
6843     rowbytes,
6844     save_image_depth;
6845
6846   int
6847     j,
6848     number_colors,
6849     number_opaque,
6850     number_semitransparent,
6851     number_transparent,
6852     ping_pHYs_unit_type;
6853
6854   png_uint_32
6855     ping_pHYs_x_resolution,
6856     ping_pHYs_y_resolution;
6857
6858   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6859     "  Enter WriteOnePNGImage()");
6860
6861   image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
6862   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
6863
6864 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6865   LockSemaphoreInfo(ping_semaphore);
6866 #endif
6867
6868   /* Initialize some stuff */
6869   ping_bit_depth=0,
6870   ping_color_type=0,
6871   ping_interlace_method=0,
6872   ping_compression_method=0,
6873   ping_filter_method=0,
6874   ping_num_trans = 0;
6875
6876   ping_background.red = 0;
6877   ping_background.green = 0;
6878   ping_background.blue = 0;
6879   ping_background.gray = 0;
6880   ping_background.index = 0;
6881
6882   ping_trans_color.red=0;
6883   ping_trans_color.green=0;
6884   ping_trans_color.blue=0;
6885   ping_trans_color.gray=0;
6886
6887   ping_pHYs_unit_type = 0;
6888   ping_pHYs_x_resolution = 0;
6889   ping_pHYs_y_resolution = 0;
6890
6891   ping_have_blob=MagickFalse;
6892   ping_have_color=MagickTrue;
6893   ping_have_non_bw=MagickTrue;
6894   ping_have_PLTE=MagickFalse;
6895   ping_have_bKGD=MagickFalse;
6896   ping_have_pHYs=MagickFalse;
6897   ping_have_tRNS=MagickFalse;
6898
6899   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
6900   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6901   /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
6902   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
6903   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6904   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
6905   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
6906   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
6907   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
6908   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
6909   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
6910   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
6911   ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
6912   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
6913   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
6914
6915   ping_need_colortype_warning = MagickFalse;
6916
6917   number_opaque = 0;
6918   number_semitransparent = 0;
6919   number_transparent = 0;
6920
6921   if (logging != MagickFalse)
6922     {
6923       if (image->storage_class == UndefinedClass)
6924           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6925           "    storage_class=UndefinedClass");
6926       if (image->storage_class == DirectClass)
6927           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6928           "    storage_class=DirectClass");
6929       if (image->storage_class == PseudoClass)
6930           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6931           "    storage_class=PseudoClass");
6932     }
6933    
6934   if (image->colorspace != RGBColorspace)
6935     (void) TransformImageColorspace(image,RGBColorspace);
6936
6937   /*
6938     Sometimes we get PseudoClass images whose RGB values don't match
6939     the colors in the colormap.  This code syncs the RGB values.
6940   */
6941   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
6942      (void) SyncImage(image);
6943
6944 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
6945   if (image->depth > 8)
6946     {
6947       if (logging != MagickFalse)
6948         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6949           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
6950
6951       image->depth=8;
6952     }
6953 #endif
6954
6955
6956 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6957   /* PNG does not handle depths greater than 16 so reduce it even
6958    * if lossy
6959    */
6960   if (image->depth > 16)
6961       image->depth=16;
6962 #endif
6963
6964 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6965   if (image->depth == 16 && mng_info->write_png_depth != 16)
6966     if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
6967       image->depth = 8;
6968 #endif
6969
6970   /* Normally we run this just once, but in the case of writing PNG8
6971    * we reduce the transparency to binary and run again, then reduce
6972    * the colors to a simple 3-3-2 palette and run once more.
6973    */
6974   for (j=0; j<3; j++)
6975   {
6976     /* BUILD_PALETTE
6977      *
6978      * Sometimes we get DirectClass images that have 256 colors or fewer.
6979      * This code will build a colormap.
6980      *
6981      * Also, sometimes we get PseudoClass images with an out-of-date
6982      * colormap.  This code will replace the colormap with a new one.
6983      * Sometimes we get PseudoClass images that have more than 256 colors.
6984      * This code will delete the colormap and change the image to
6985      * DirectClass.
6986      *
6987      * If image->matte is MagickFalse, we ignore the opacity channel
6988      * even though it sometimes contains left-over non-opaque values.
6989      *
6990      * Also we gather some information (number of opaque, transparent,
6991      * and semitransparent pixels, and whether the image has any non-gray
6992      * pixels or only black-and-white pixels) that we might need later.
6993      *
6994      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
6995      * we need to check for bogus non-opaque values, at least.
6996      */
6997
6998 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
6999 #  define PNGK 0 /* Shift */
7000 #  define PNGM 1 /* Scale */
7001 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
7002 #  define PNGK 8
7003 #  define PNGM 0x0101
7004 #else
7005 #  define PNGK 24
7006 #  define PNGM 0x01010101
7007 #endif
7008
7009    ExceptionInfo
7010      *exception;
7011
7012    int
7013      n;
7014
7015    PixelPacket
7016      opaque[260],
7017      semitransparent[260],
7018      transparent[260];
7019
7020    register IndexPacket
7021      *indexes;
7022
7023    register const PixelPacket
7024      *s,
7025      *q;
7026
7027    register PixelPacket
7028      *r;
7029
7030    if (logging != MagickFalse)
7031      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7032          "    Enter BUILD_PALETTE:");
7033
7034    if (logging != MagickFalse)
7035      {
7036        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7037              "      image->columns=%.20g",(double) image->columns);
7038        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7039              "      image->rows=%.20g",(double) image->rows);
7040        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7041              "      image->matte=%.20g",(double) image->matte);
7042        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7043              "      image->depth=%.20g",(double) image->depth);
7044
7045        if (image->storage_class == PseudoClass && image->colormap != NULL)
7046        {
7047          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7048              "      Original colormap:");
7049          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7050              "        i    (red,green,blue,opacity)");
7051
7052          for (i=0; i < 256; i++)
7053          {
7054                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7055                    "        %d    (%d,%d,%d,%d)",
7056                     (int) i,
7057                     (int) image->colormap[i].red,
7058                     (int) image->colormap[i].green,
7059                     (int) image->colormap[i].blue,
7060                     (int) image->colormap[i].opacity);
7061          }
7062
7063          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7064          {
7065            if (i > 255)
7066              {
7067                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7068                    "        %d    (%d,%d,%d,%d)",
7069                     (int) i,
7070                     (int) image->colormap[i].red,
7071                     (int) image->colormap[i].green,
7072                     (int) image->colormap[i].blue,
7073                     (int) image->colormap[i].opacity);
7074              }
7075          }
7076        }
7077
7078        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7079            "      image->colors=%d",(int) image->colors);
7080
7081        if (image->colors == 0)
7082        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7083            "        (zero means unknown)");
7084
7085        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7086             "      Regenerate the colormap");
7087      }
7088
7089      exception=(&image->exception);
7090
7091      image_colors=0;
7092      number_opaque = 0;
7093      number_semitransparent = 0;
7094      number_transparent = 0;
7095
7096      for (y=0; y < (ssize_t) image->rows; y++)
7097      {
7098        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7099
7100        if (q == (PixelPacket *) NULL)
7101          break;
7102
7103        for (x=0; x < (ssize_t) image->columns; x++)
7104        {
7105            if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7106              {
7107                if (number_opaque < 259)
7108                  {
7109                    if (number_opaque == 0)
7110                      {
7111                        opaque[0]=*q;
7112                        opaque[0].opacity=OpaqueOpacity;
7113                        number_opaque=1;
7114                      }
7115
7116                    for (i=0; i< (ssize_t) number_opaque; i++)
7117                      {
7118                        if (IsColorEqual(opaque+i, (PixelPacket *) q))
7119                          break;
7120                      }
7121
7122                    if (i ==  (ssize_t) number_opaque &&
7123                        number_opaque < 259)
7124                      {
7125                        number_opaque++;
7126                        opaque[i] = *q;
7127                        opaque[i].opacity = OpaqueOpacity;
7128                      }
7129                  }
7130              }
7131            else if (q->opacity == TransparentOpacity)
7132              {
7133                if (number_transparent < 259)
7134                  {
7135                    if (number_transparent == 0)
7136                      {
7137                        transparent[0]=*q;
7138                        ping_trans_color.red=(unsigned short)(q->red);
7139                        ping_trans_color.green=(unsigned short) (q->green);
7140                        ping_trans_color.blue=(unsigned short) (q->blue);
7141                        ping_trans_color.gray=(unsigned short) (q->blue);
7142                        number_transparent = 1;
7143                      }
7144
7145                    for (i=0; i< (ssize_t) number_transparent; i++)
7146                      {
7147                        if (IsColorEqual(transparent+i, (PixelPacket *) q))
7148                          break;
7149                      }
7150
7151                    if (i ==  (ssize_t) number_transparent &&
7152                        number_transparent < 259)
7153                      {
7154                        number_transparent++;
7155                        transparent[i] = *q;
7156                      }
7157                  }
7158              }
7159            else
7160              {
7161                if (number_semitransparent < 259)
7162                  {
7163                    if (number_semitransparent == 0)
7164                      {
7165                        semitransparent[0]=*q;
7166                        number_semitransparent = 1;
7167                      }
7168
7169                    for (i=0; i< (ssize_t) number_semitransparent; i++)
7170                      {
7171                        if (IsColorEqual(semitransparent+i,
7172                           (PixelPacket *) q) &&
7173                           q->opacity == semitransparent[i].opacity)
7174                          break;
7175                      }
7176
7177                    if (i ==  (ssize_t) number_semitransparent &&
7178                        number_semitransparent < 259)
7179                      {
7180                        number_semitransparent++;
7181                        semitransparent[i] = *q;
7182                      }
7183                  }
7184              }
7185            q++;
7186         }
7187      }
7188
7189      if (ping_exclude_bKGD == MagickFalse)
7190        {
7191          /* Add the background color to the palette, if it
7192           * isn't already there.
7193           */
7194           for (i=0; i<number_opaque; i++)
7195           {
7196              if (IsColorEqual(opaque+i, &image->background_color))
7197              break;
7198           }
7199
7200           if (number_opaque < 259 && i == number_opaque)
7201             {
7202                opaque[i]=image->background_color;
7203                opaque[i].opacity = OpaqueOpacity;
7204                number_opaque++;
7205             }
7206           else if (logging != MagickFalse)
7207               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7208                   "      No room in the colormap to add background color");
7209        }
7210
7211      image_colors=number_opaque+number_transparent+number_semitransparent;
7212
7213      if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7214        {
7215          /* No room for the background color; remove it. */
7216          number_opaque--;
7217          image_colors--;
7218        }
7219
7220      if (logging != MagickFalse)
7221        {
7222          if (image_colors > 256)
7223             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7224                   "      image has more than 256 colors");
7225
7226          else
7227             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7228                   "      image has %d colors",image_colors);
7229        }
7230
7231      if (mng_info->write_png_colortype != 7) /* We won't need this info */
7232        {
7233          ping_have_color=MagickFalse;
7234          ping_have_non_bw=MagickFalse;
7235
7236          if(image_colors > 256)
7237            {
7238              for (y=0; y < (ssize_t) image->rows; y++)
7239              {
7240                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7241
7242                if (q == (PixelPacket *) NULL)
7243                  break;
7244
7245                /* Worst case is black-and-white; we are looking at every
7246                 * pixel twice.
7247                 */
7248
7249                if (ping_have_color == MagickFalse)
7250                  {
7251                    s=q;
7252                    for (x=0; x < (ssize_t) image->columns; x++)
7253                    {
7254                      if (s->red != s->green || s->red != s->blue)
7255                        {
7256                           ping_have_color=MagickTrue;
7257                           ping_have_non_bw=MagickTrue;
7258                           break;
7259                        }
7260                      s++;
7261                    }
7262                  }
7263
7264                if (ping_have_non_bw == MagickFalse)
7265                  {
7266                    s=q;
7267                    for (x=0; x < (ssize_t) image->columns; x++)
7268                    {
7269                      if (s->red != 0 && s->red != QuantumRange)
7270                        {
7271                          ping_have_non_bw=MagickTrue;
7272                        }
7273                      s++;
7274                    }
7275                  }
7276              }
7277            } 
7278        } 
7279
7280      if (image_colors < 257)
7281        {
7282          PixelPacket
7283            colormap[260];
7284              
7285          /*
7286           * Initialize image colormap.
7287           */
7288
7289          if (logging != MagickFalse)
7290             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7291                   "      Sort the new colormap");
7292
7293         /* Sort palette, transparent first */;
7294
7295          n = 0;
7296
7297          for (i=0; i<number_transparent; i++)
7298             colormap[n++] = transparent[i];
7299
7300          for (i=0; i<number_semitransparent; i++)
7301             colormap[n++] = semitransparent[i];
7302
7303          for (i=0; i<number_opaque; i++)
7304             colormap[n++] = opaque[i];
7305
7306          
7307          /* image_colors < 257; search the colormap instead of the pixels
7308           * to get ping_have_color and ping_have_non_bw
7309           */
7310          for (i=0; i<n; i++)
7311          {
7312            if (ping_have_color == MagickFalse)
7313              {
7314                 if (colormap[i].red != colormap[i].green ||
7315                     colormap[i].red != colormap[i].blue)
7316                   {
7317                      ping_have_color=MagickTrue;
7318                      ping_have_non_bw=MagickTrue;
7319                      break;
7320                   }
7321               }
7322
7323            if (ping_have_non_bw == MagickFalse)
7324              {
7325                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
7326                    ping_have_non_bw=MagickTrue;
7327              }
7328           }
7329
7330         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7331             (number_transparent == 0 && number_semitransparent == 0)) &&
7332             (((mng_info->write_png_colortype-1) ==
7333             PNG_COLOR_TYPE_PALETTE) ||
7334             (mng_info->write_png_colortype == 0)))
7335           {
7336             if (logging != MagickFalse)
7337               {
7338                 if (n !=  (ssize_t) image_colors)
7339                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7340                    "   image_colors (%d) and n (%d)  don't match",
7341                    image_colors, n);
7342
7343                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7344                    "      AcquireImageColormap");
7345               }
7346
7347             image->colors = image_colors;
7348
7349             if (AcquireImageColormap(image,image_colors) ==
7350                 MagickFalse)
7351                ThrowWriterException(ResourceLimitError,
7352                   "MemoryAllocationFailed");
7353
7354             for (i=0; i< (ssize_t) image_colors; i++)
7355                image->colormap[i] = colormap[i];
7356
7357             if (logging != MagickFalse)
7358               {
7359                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7360                       "      image->colors=%d (%d)",
7361                       (int) image->colors, image_colors);
7362  
7363                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7364                       "      Update the pixel indexes");
7365               }
7366
7367             /* Sync the pixel indices with the new colormap */
7368
7369             for (y=0; y < (ssize_t) image->rows; y++)
7370             {
7371               q=GetAuthenticPixels(image,0,y,image->columns,1,
7372                   exception);
7373
7374               if (q == (PixelPacket *) NULL)
7375                 break;
7376
7377               indexes=GetAuthenticIndexQueue(image);
7378  
7379               for (x=0; x < (ssize_t) image->columns; x++)
7380               {
7381                 for (i=0; i< (ssize_t) image_colors; i++)
7382                 {
7383                   if ((image->matte == MagickFalse ||
7384                       image->colormap[i].opacity == q->opacity) &&
7385                       (IsColorEqual(&image->colormap[i],
7386                          (PixelPacket *) q)))
7387                   {
7388                     indexes[x]=(IndexPacket) i;
7389                     break;
7390                   }
7391                 }
7392                 q++;
7393               }
7394
7395               if (SyncAuthenticPixels(image,exception) == MagickFalse)
7396                  break;
7397             }
7398           }
7399        }
7400
7401      if (logging != MagickFalse)
7402        {
7403          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7404             "      image->colors=%d", (int) image->colors);
7405
7406          if (image->colormap != NULL)
7407            {
7408              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7409                  "       i     (red,green,blue,opacity)");
7410
7411              for (i=0; i < (ssize_t) image->colors; i++)
7412              {
7413                if (i < 300 || i >= image->colors - 10)
7414                  {
7415                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7416                        "       %d     (%d,%d,%d,%d)",
7417                         (int) i,
7418                         (int) image->colormap[i].red,
7419                         (int) image->colormap[i].green,
7420                         (int) image->colormap[i].blue,
7421                         (int) image->colormap[i].opacity);
7422                  }
7423              }
7424            }
7425
7426            if (number_transparent < 257)
7427              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7428                    "      number_transparent     = %d",
7429                    number_transparent);
7430            else
7431
7432              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7433                    "      number_transparent     > 256");
7434
7435            if (number_opaque < 257)
7436              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7437                    "      number_opaque          = %d",
7438                    number_opaque);
7439
7440            else
7441              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7442                    "      number_opaque          > 256");
7443
7444            if (number_semitransparent < 257)
7445              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7446                    "      number_semitransparent = %d",
7447                    number_semitransparent);
7448
7449            else
7450              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7451                    "      number_semitransparent > 256");
7452
7453            if (ping_have_non_bw == MagickFalse)
7454               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7455                     "      All pixels and the background are black or white");
7456
7457            else if (ping_have_color == MagickFalse)
7458               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7459                     "      All pixels and the background are gray");
7460
7461            else
7462               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7463                     "      At least one pixel or the background is non-gray");
7464
7465            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7466                "    Exit BUILD_PALETTE:");
7467        }
7468
7469    if (mng_info->write_png8 == MagickFalse)
7470       break;
7471
7472    /* Make any reductions necessary for the PNG8 format */
7473     if (image_colors <= 256 &&
7474         image_colors != 0 && image->colormap != NULL &&
7475         number_semitransparent == 0 &&
7476         number_transparent <= 1)
7477       break;
7478
7479     /* PNG8 can't have semitransparent colors so we threshold the
7480      * opacity to 0 or OpaqueOpacity
7481      */
7482     if (number_semitransparent != 0)
7483       {
7484         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7485             "    Thresholding the alpha channel to binary");
7486
7487         for (y=0; y < (ssize_t) image->rows; y++)
7488         {
7489           r=GetAuthenticPixels(image,0,y,image->columns,1,
7490               exception);
7491
7492           if (r == (PixelPacket *) NULL)
7493             break;
7494
7495           for (x=0; x < (ssize_t) image->columns; x++)
7496           {
7497               r->opacity = r->opacity > OpaqueOpacity/2 ? 0 :
7498                    OpaqueOpacity;
7499               r++;
7500           }
7501   
7502           if (SyncAuthenticPixels(image,exception) == MagickFalse)
7503              break;
7504
7505           if (image_colors != 0 && image_colors <= 256 &&
7506              image->colormap != NULL)
7507             for (i=0; i<image_colors; i++)
7508                 image->colormap[i].opacity =
7509                     image->colormap[i].opacity > OpaqueOpacity/2 ? 0 :
7510                     OpaqueOpacity;
7511         }
7512       continue;
7513     }
7514
7515     /* PNG8 can't have more than 256 colors so we quantize the pixels and
7516      * background color to the 3-3-2 palette.
7517      */
7518
7519     if (image_colors == 0 || image_colors > 256)
7520       {
7521         if (logging != MagickFalse)
7522            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7523                "    Quantizing the background color to 3-3-2");
7524
7525         image->background_color.red=
7526             ((((((size_t)
7527             image->background_color.red) >> PNGK) & 0xe0)     )  |
7528             (((((size_t)
7529             image->background_color.red) >> PNGK) & 0xe0) >> 3)  |
7530             (((((size_t)
7531             image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7532         image->background_color.green=
7533             ((((((size_t)
7534             image->background_color.green) >> PNGK) & 0xe0)     )  |
7535             (((((size_t)
7536             image->background_color.green) >> PNGK) & 0xe0) >> 3)  |
7537             (((((size_t)
7538             image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7539         image->background_color.blue=
7540             ((((((size_t)
7541             image->background_color.blue) >> PNGK) & 0xc0)     )  |
7542             (((((size_t)
7543             image->background_color.blue) >> PNGK) & 0xc0) >> 2)  |
7544             (((((size_t)
7545             image->background_color.blue) >> PNGK) & 0xc0) >> 4)  |
7546             (((((size_t)
7547             image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7548
7549         if (logging != MagickFalse)
7550           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7551               "    Quantizing the pixel colors to 3-3-2");
7552
7553         if (image->colormap == NULL)
7554         {
7555           for (y=0; y < (ssize_t) image->rows; y++)
7556           {
7557             r=GetAuthenticPixels(image,0,y,image->columns,1,
7558                 exception);
7559
7560             if (r == (PixelPacket *) NULL)
7561               break;
7562
7563             for (x=0; x < (ssize_t) image->columns; x++)
7564             {
7565               r->red=
7566                    ((((((size_t) r->red) >> PNGK) & 0xe0)    )  |
7567                    (((((size_t) r->red) >> PNGK) & 0xe0) >> 3)  |
7568                    (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7569               r->green=
7570                    ((((((size_t) r->green) >> PNGK) & 0xe0)    )  |
7571                    (((((size_t) r->green) >> PNGK) & 0xe0) >> 3)  |
7572                    (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7573               r->blue=
7574                    ((((((size_t) r->blue) >> PNGK) & 0xc0)    )  |
7575                    (((((size_t) r->blue) >> PNGK) & 0xc0) >> 2)  |
7576                    (((((size_t) r->blue) >> PNGK) & 0xc0) >> 4)  |
7577                    (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7578               r++;
7579             }
7580     
7581             if (SyncAuthenticPixels(image,exception) == MagickFalse)
7582                break;
7583           }
7584         }
7585
7586         else /* Should not reach this; colormap already exists and
7587                 must be <= 256 */
7588         {
7589           if (logging != MagickFalse)
7590               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7591               "    Quantizing the colormap to 3-3-2");
7592           for (i=0; i<image_colors; i++)
7593           {
7594               image->colormap[i].red=
7595                   ((((((size_t)
7596                   image->colormap[i].red) >> PNGK) & 0xe0)     )  |
7597                   (((((size_t)
7598                   image->colormap[i].red) >> PNGK) & 0xe0) >> 3)  |
7599                   (((((size_t)
7600                   image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7601               image->colormap[i].green=
7602                   ((((((size_t)
7603                   image->colormap[i].green) >> PNGK) & 0xe0)     )  |
7604                   (((((size_t)
7605                   image->colormap[i].green) >> PNGK) & 0xe0) >> 3)  |
7606                   (((((size_t)
7607                   image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7608               image->colormap[i].blue=
7609                   ((((((size_t)
7610                   image->colormap[i].blue) >> PNGK) & 0xc0)     )  |
7611                   (((((size_t)
7612                   image->colormap[i].blue) >> PNGK) & 0xc0) >> 2)  |
7613                   (((((size_t)
7614                   image->colormap[i].blue) >> PNGK) & 0xc0) >> 4)  |
7615                   (((((size_t)
7616                   image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7617           }
7618       }
7619       continue;
7620     }
7621     break;
7622   }
7623   /* END OF BUILD_PALETTE */
7624
7625   /* If we are excluding the tRNS chunk and there is transparency,
7626    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
7627    * PNG.
7628    */
7629   if (mng_info->ping_exclude_tRNS != MagickFalse &&
7630      (number_transparent != 0 || number_semitransparent != 0))
7631     {
7632       int colortype=mng_info->write_png_colortype;
7633
7634       if (ping_have_color == MagickFalse)
7635         mng_info->write_png_colortype = 5;
7636
7637       else
7638         mng_info->write_png_colortype = 7;
7639
7640       if (colortype != 0 &&
7641          mng_info->write_png_colortype != (ssize_t) colortype)
7642         ping_need_colortype_warning=MagickTrue;
7643
7644     }
7645
7646   /* See if cheap transparency is possible.  It is only possible
7647    * when there is a single transparent color, no semitransparent
7648    * color, and no opaque color that has the same RGB components
7649    * as the transparent color.  We only need this information if
7650    * we are writing a PNG with colortype 0 or 2, and we have not
7651    * excluded the tRNS chunk.
7652    */
7653   if (number_transparent == 1 &&
7654       mng_info->write_png_colortype < 4)
7655     {
7656        ping_have_cheap_transparency = MagickTrue;
7657
7658        if (number_semitransparent != 0)
7659          ping_have_cheap_transparency = MagickFalse;
7660
7661        else if (image_colors == 0 || image_colors > 256 ||
7662            image->colormap == NULL)
7663          {
7664            ExceptionInfo
7665              *exception;
7666
7667            register const PixelPacket
7668              *q;
7669
7670            exception=(&image->exception);
7671
7672            for (y=0; y < (ssize_t) image->rows; y++)
7673            {
7674              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
7675
7676              if (q == (PixelPacket *) NULL)
7677                break;
7678
7679              for (x=0; x < (ssize_t) image->columns; x++)
7680              {
7681                  if (q->opacity != TransparentOpacity &&
7682                      (unsigned short) q->red == ping_trans_color.red &&
7683                      (unsigned short) q->green == ping_trans_color.green &&
7684                      (unsigned short) q->blue == ping_trans_color.blue)
7685                    {
7686                      ping_have_cheap_transparency = MagickFalse;
7687                      break;
7688                    }
7689
7690                  q++;
7691              }
7692     
7693              if (ping_have_cheap_transparency == MagickFalse)
7694                 break;
7695            }
7696          }
7697        else
7698          {
7699             /* Assuming that image->colormap[0] is the one transparent color
7700              * and that all others are opaque.
7701              */
7702             if (image_colors > 1)
7703               for (i=1; i<image_colors; i++)
7704                 if (image->colormap[i].red == image->colormap[0].red &&
7705                     image->colormap[i].green == image->colormap[0].green &&
7706                     image->colormap[i].blue == image->colormap[0].blue)
7707                   {
7708                      ping_have_cheap_transparency = MagickFalse;
7709                      break;
7710                   }
7711          }
7712        
7713        if (logging != MagickFalse)
7714          {
7715            if (ping_have_cheap_transparency == MagickFalse)
7716              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7717                  "   Cheap transparency is not possible.");
7718
7719            else
7720              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7721                  "   Cheap transparency is possible.");
7722          }
7723      }
7724   else
7725     ping_have_cheap_transparency = MagickFalse;
7726
7727   image_depth=image->depth;
7728
7729   quantum_info = (QuantumInfo *) NULL;
7730   number_colors=0;
7731   image_colors=(int) image->colors;
7732   image_matte=image->matte;
7733
7734   mng_info->IsPalette=image->storage_class == PseudoClass &&
7735     image_colors <= 256 && image->colormap != NULL;
7736
7737   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
7738      (image->colors == 0 || image->colormap == NULL))
7739     {
7740       image_info=DestroyImageInfo(image_info);
7741       image=DestroyImage(image);
7742       (void) ThrowMagickException(&IMimage->exception,
7743           GetMagickModule(),CoderError,
7744           "Cannot write PNG8 or color-type 3; colormap is NULL",
7745           "`%s'",IMimage->filename);
7746 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7747       UnlockSemaphoreInfo(ping_semaphore);
7748 #endif
7749       return(MagickFalse);
7750     }
7751
7752   /*
7753     Allocate the PNG structures
7754   */
7755 #ifdef PNG_USER_MEM_SUPPORTED
7756   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
7757     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
7758     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
7759
7760 #else
7761   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
7762     MagickPNGErrorHandler,MagickPNGWarningHandler);
7763
7764 #endif
7765   if (ping == (png_struct *) NULL)
7766     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7767
7768   ping_info=png_create_info_struct(ping);
7769
7770   if (ping_info == (png_info *) NULL)
7771     {
7772       png_destroy_write_struct(&ping,(png_info **) NULL);
7773       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7774     }
7775
7776   png_set_write_fn(ping,image,png_put_data,png_flush_data);
7777   ping_pixels=(unsigned char *) NULL;
7778
7779   if (setjmp(png_jmpbuf(ping)))
7780     {
7781       /*
7782         PNG write failed.
7783       */
7784 #ifdef PNG_DEBUG
7785      if (image_info->verbose)
7786         (void) printf("PNG write has failed.\n");
7787 #endif
7788       png_destroy_write_struct(&ping,&ping_info);
7789 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7790       UnlockSemaphoreInfo(ping_semaphore);
7791 #endif
7792       if (ping_have_blob != MagickFalse)
7793           (void) CloseBlob(image);
7794       image_info=DestroyImageInfo(image_info);
7795       image=DestroyImage(image);
7796       return(MagickFalse);
7797     }
7798   /*
7799     Prepare PNG for writing.
7800   */
7801 #if defined(PNG_MNG_FEATURES_SUPPORTED)
7802   if (mng_info->write_mng)
7803      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
7804
7805 #else
7806 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
7807   if (mng_info->write_mng)
7808      png_permit_empty_plte(ping,MagickTrue);
7809
7810 # endif
7811 #endif
7812
7813   x=0;
7814
7815   ping_width=(png_uint_32) image->columns;
7816   ping_height=(png_uint_32) image->rows;
7817
7818   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
7819      image_depth=8;
7820
7821   if (mng_info->write_png_depth != 0)
7822      image_depth=mng_info->write_png_depth;
7823
7824   /* Adjust requested depth to next higher valid depth if necessary */
7825   if (image_depth > 8)
7826      image_depth=16;
7827
7828   if ((image_depth > 4) && (image_depth < 8))
7829      image_depth=8;
7830
7831   if (image_depth == 3)
7832      image_depth=4;
7833
7834   if (logging != MagickFalse)
7835     {
7836      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7837         "    width=%.20g",(double) ping_width);
7838      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7839         "    height=%.20g",(double) ping_height);
7840      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7841         "    image_matte=%.20g",(double) image->matte);
7842      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7843         "    image->depth=%.20g",(double) image->depth);
7844      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7845         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
7846     }
7847
7848   save_image_depth=image_depth;
7849   ping_bit_depth=(png_byte) save_image_depth;
7850
7851
7852 #if defined(PNG_pHYs_SUPPORTED)
7853   if (ping_exclude_pHYs == MagickFalse)
7854   {
7855   if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
7856       (!mng_info->write_mng || !mng_info->equal_physs))
7857     {
7858       if (logging != MagickFalse)
7859         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7860             "    Setting up pHYs chunk");
7861
7862       if (image->units == PixelsPerInchResolution)
7863         {
7864           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
7865           ping_pHYs_x_resolution=
7866              (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
7867           ping_pHYs_y_resolution=
7868              (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
7869         }
7870
7871       else if (image->units == PixelsPerCentimeterResolution)
7872         {
7873           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
7874           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
7875           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
7876         }
7877
7878       else
7879         {
7880           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
7881           ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
7882           ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
7883         }
7884
7885       if (logging != MagickFalse)
7886         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7887           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
7888           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
7889           (int) ping_pHYs_unit_type);
7890        ping_have_pHYs = MagickTrue;
7891     }
7892   }
7893 #endif
7894
7895   if (ping_exclude_bKGD == MagickFalse)
7896   {
7897   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
7898     {
7899        unsigned int
7900          mask;
7901
7902        mask=0xffff;
7903        if (ping_bit_depth == 8)
7904           mask=0x00ff;
7905
7906        if (ping_bit_depth == 4)
7907           mask=0x000f;
7908
7909        if (ping_bit_depth == 2)
7910           mask=0x0003;
7911
7912        if (ping_bit_depth == 1)
7913           mask=0x0001;
7914
7915        ping_background.red=(png_uint_16)
7916          (ScaleQuantumToShort(image->background_color.red) & mask);
7917
7918        ping_background.green=(png_uint_16)
7919          (ScaleQuantumToShort(image->background_color.green) & mask);
7920
7921        ping_background.blue=(png_uint_16)
7922          (ScaleQuantumToShort(image->background_color.blue) & mask);
7923     }
7924
7925   if (logging != MagickFalse)
7926     {
7927       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7928           "    Setting up bKGD chunk (1)");
7929
7930       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7931           "    ping_bit_depth=%d",ping_bit_depth);
7932     }
7933
7934   ping_have_bKGD = MagickTrue;
7935   }
7936
7937   /*
7938     Select the color type.
7939   */
7940   matte=image_matte;
7941   old_bit_depth=0;
7942
7943   if (mng_info->IsPalette && mng_info->write_png8)
7944     {
7945
7946       /* To do: make this a function cause it's used twice, except
7947          for reducing the sample depth from 8. */
7948
7949       number_colors=image_colors;
7950
7951       ping_have_tRNS=MagickFalse;
7952
7953       /*
7954         Set image palette.
7955       */
7956       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
7957
7958       if (logging != MagickFalse)
7959         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7960             "  Setting up PLTE chunk with %d colors (%d)",
7961             number_colors, image_colors);
7962
7963       for (i=0; i < (ssize_t) number_colors; i++)
7964       {
7965         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
7966         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
7967         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
7968         if (logging != MagickFalse)
7969           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7970 #if MAGICKCORE_QUANTUM_DEPTH == 8
7971             "    %3ld (%3d,%3d,%3d)",
7972 #else
7973             "    %5ld (%5d,%5d,%5d)",
7974 #endif
7975             (long) i,palette[i].red,palette[i].green,palette[i].blue);
7976
7977       }
7978
7979       ping_have_PLTE=MagickTrue;
7980       image_depth=ping_bit_depth;
7981       ping_num_trans=0;
7982
7983       if (matte != MagickFalse)
7984       {
7985           /*
7986             Identify which colormap entry is transparent.
7987           */
7988           assert(number_colors <= 256);
7989           assert(image->colormap != NULL);
7990
7991           for (i=0; i < (ssize_t) number_transparent; i++)
7992              ping_trans_alpha[i]=0;
7993
7994
7995           ping_num_trans=(unsigned short) (number_transparent +
7996              number_semitransparent);
7997
7998           if (ping_num_trans == 0)
7999              ping_have_tRNS=MagickFalse;
8000
8001           else
8002              ping_have_tRNS=MagickTrue;
8003       }
8004
8005       if (ping_exclude_bKGD == MagickFalse)
8006       {
8007        /*
8008         * Identify which colormap entry is the background color.
8009         */
8010
8011         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8012           if (IsPNGColorEqual(ping_background,image->colormap[i]))
8013             break;
8014
8015         ping_background.index=(png_byte) i;
8016       }
8017     } /* end of write_png8 */
8018
8019   else if (mng_info->write_png24)
8020     {
8021       image_matte=MagickFalse;
8022       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8023     }
8024
8025   else if (mng_info->write_png32)
8026     {
8027       image_matte=MagickTrue;
8028       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8029     }
8030
8031   else /* mng_info->write_pngNN not specified */
8032     {
8033       image_depth=ping_bit_depth;
8034
8035       if (mng_info->write_png_colortype != 0)
8036         {
8037           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
8038
8039           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8040               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8041             image_matte=MagickTrue;
8042
8043           else
8044             image_matte=MagickFalse;
8045         }
8046
8047       else /* write_ping_colortype not specified */
8048         {
8049           if (logging != MagickFalse)
8050              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8051              "  Selecting PNG colortype:");
8052
8053           ping_color_type=(png_byte) ((matte != MagickFalse)?
8054             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
8055
8056           if (image_info->type == TrueColorType)
8057             {
8058               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8059               image_matte=MagickFalse;
8060             }
8061
8062           if (image_info->type == TrueColorMatteType)
8063             {
8064               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8065               image_matte=MagickTrue;
8066             }
8067
8068           if (image_info->type == PaletteType ||
8069               image_info->type == PaletteMatteType)
8070             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8071
8072           if (image_info->type == UndefinedType ||
8073              image_info->type == OptimizeType)
8074             {
8075               if (ping_have_color == MagickFalse)
8076                 {
8077                   if (image_matte == MagickFalse)
8078                     {
8079                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8080                       image_matte=MagickFalse;
8081                     }
8082
8083                   else
8084                     {
8085                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8086                       image_matte=MagickTrue;
8087                     }
8088                 }
8089               else
8090                 {
8091                   if (image_matte == MagickFalse)
8092                     {
8093                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8094                       image_matte=MagickFalse;
8095                     }
8096
8097                   else
8098                     {
8099                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8100                       image_matte=MagickTrue;
8101                     }
8102                  }
8103             }
8104
8105         }
8106
8107       if (logging != MagickFalse)
8108          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8109          "    Selected PNG colortype=%d",ping_color_type);
8110
8111       if (ping_bit_depth < 8)
8112         {
8113           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8114               ping_color_type == PNG_COLOR_TYPE_RGB ||
8115               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8116             ping_bit_depth=8;
8117         }
8118
8119       old_bit_depth=ping_bit_depth;
8120
8121       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8122         {
8123           if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8124              ping_bit_depth=1;
8125         }
8126
8127       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
8128         {
8129            size_t one = 1;
8130            ping_bit_depth=1;
8131
8132            if (image->colors == 0)
8133            {
8134               /* DO SOMETHING */
8135               (void) ThrowMagickException(&image->exception,
8136                  GetMagickModule(),CoderError,
8137                 "image has 0 colors", "`%s'","");
8138            }
8139
8140            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
8141              ping_bit_depth <<= 1;
8142         }
8143
8144       if (logging != MagickFalse)
8145          {
8146            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8147             "    Number of colors: %.20g",(double) image_colors);
8148
8149            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8150             "    Tentative PNG bit depth: %d",ping_bit_depth);
8151          }
8152
8153       if (ping_bit_depth < (int) mng_info->write_png_depth)
8154          ping_bit_depth = mng_info->write_png_depth;
8155     }
8156
8157   image_depth=ping_bit_depth;
8158
8159   if (logging != MagickFalse)
8160     {
8161       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8162         "    Tentative PNG color type: %.20g",(double) ping_color_type);
8163
8164       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8165         "    image_info->type: %.20g",(double) image_info->type);
8166
8167       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8168         "    image_depth: %.20g",(double) image_depth);
8169
8170       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8171
8172         "    image->depth: %.20g",(double) image->depth);
8173
8174       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8175         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
8176     }
8177
8178   if (matte != MagickFalse)
8179     {
8180       if (mng_info->IsPalette)
8181         {
8182           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8183
8184           if (ping_have_color != MagickFalse)
8185              ping_color_type=PNG_COLOR_TYPE_RGBA;
8186
8187           /*
8188            * Determine if there is any transparent color.
8189           */
8190           if (number_transparent + number_semitransparent == 0)
8191             {
8192               /*
8193                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
8194               */
8195
8196               image_matte=MagickFalse;
8197               ping_color_type&=0x03;
8198             }
8199
8200           else
8201             {
8202               unsigned int
8203                 mask; 
8204
8205               mask=0xffff;
8206
8207               if (ping_bit_depth == 8)
8208                  mask=0x00ff;
8209
8210               if (ping_bit_depth == 4)
8211                  mask=0x000f;
8212
8213               if (ping_bit_depth == 2)
8214                  mask=0x0003;
8215
8216               if (ping_bit_depth == 1)
8217                  mask=0x0001;
8218
8219               ping_trans_color.red=(png_uint_16)
8220                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8221
8222               ping_trans_color.green=(png_uint_16)
8223                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8224
8225               ping_trans_color.blue=(png_uint_16)
8226                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8227
8228               ping_trans_color.gray=(png_uint_16)
8229                 (ScaleQuantumToShort(PixelIntensityToQuantum(
8230                    image->colormap)) & mask);
8231
8232               ping_trans_color.index=(png_byte) 0;
8233
8234               ping_have_tRNS=MagickTrue;
8235             }
8236
8237           if (ping_have_tRNS != MagickFalse)
8238             {
8239               /*
8240                * Determine if there is one and only one transparent color
8241                * and if so if it is fully transparent.
8242                */
8243               if (ping_have_cheap_transparency == MagickFalse)
8244                 ping_have_tRNS=MagickFalse;
8245             }
8246
8247           if (ping_have_tRNS != MagickFalse)
8248             {
8249               ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
8250
8251               if (image_depth == 8)
8252                 {
8253                   ping_trans_color.red&=0xff;
8254                   ping_trans_color.green&=0xff;
8255                   ping_trans_color.blue&=0xff;
8256                   ping_trans_color.gray&=0xff;
8257                 }
8258             }
8259         }
8260       else
8261         {
8262           if (image_depth == 8)
8263             {
8264               ping_trans_color.red&=0xff;
8265               ping_trans_color.green&=0xff;
8266               ping_trans_color.blue&=0xff;
8267               ping_trans_color.gray&=0xff;
8268             }
8269         }
8270     }
8271
8272     matte=image_matte;
8273
8274     if (ping_have_tRNS != MagickFalse)
8275       image_matte=MagickFalse;
8276
8277     if ((mng_info->IsPalette) &&
8278         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
8279         ping_have_color == MagickFalse &&
8280         (image_matte == MagickFalse || image_depth >= 8))
8281       {
8282         size_t one=1;
8283
8284         if (image_matte != MagickFalse)
8285           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8286
8287         else
8288           {
8289             ping_color_type=PNG_COLOR_TYPE_GRAY;
8290
8291             if (save_image_depth == 16 && image_depth == 8)
8292               {
8293                 if (logging != MagickFalse)
8294                   {
8295                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8296                         "  Scaling ping_trans_color (0)");
8297                   }
8298                     ping_trans_color.gray*=0x0101;
8299               }
8300           }
8301
8302         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8303           image_depth=MAGICKCORE_QUANTUM_DEPTH;
8304
8305         if (image_colors == 0 || image_colors-1 > MaxColormapSize)
8306           image_colors=(int) (one << image_depth);
8307
8308         if (image_depth > 8)
8309           ping_bit_depth=16;
8310
8311         else
8312           {
8313             ping_bit_depth=8;
8314             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8315               {
8316                 if(!mng_info->write_png_depth)
8317                   {
8318                     ping_bit_depth=1;
8319
8320                     while ((int) (one << ping_bit_depth)
8321                         < (ssize_t) image_colors)
8322                       ping_bit_depth <<= 1;
8323                   }
8324               }
8325
8326             else if (ping_color_type ==
8327                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
8328                 mng_info->IsPalette)
8329               {
8330               /* Check if grayscale is reducible */
8331
8332                 int
8333                   depth_4_ok=MagickTrue,
8334                   depth_2_ok=MagickTrue,
8335                   depth_1_ok=MagickTrue;
8336
8337                 for (i=0; i < (ssize_t) image_colors; i++)
8338                 {
8339                    unsigned char
8340                      intensity;
8341
8342                    intensity=ScaleQuantumToChar(image->colormap[i].red);
8343
8344                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8345                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8346                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8347                      depth_2_ok=depth_1_ok=MagickFalse;
8348                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
8349                      depth_1_ok=MagickFalse;
8350                 }
8351
8352                 if (depth_1_ok && mng_info->write_png_depth <= 1)
8353                   ping_bit_depth=1;
8354
8355                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
8356                   ping_bit_depth=2;
8357
8358                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
8359                   ping_bit_depth=4;
8360               }
8361           }
8362
8363           image_depth=ping_bit_depth;
8364       }
8365
8366     else
8367
8368       if (mng_info->IsPalette)
8369       {
8370         number_colors=image_colors;
8371
8372         if (image_depth <= 8)
8373           {
8374             /*
8375               Set image palette.
8376             */
8377             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8378
8379             if (mng_info->have_write_global_plte && matte == MagickFalse)
8380               {
8381                 png_set_PLTE(ping,ping_info,NULL,0);
8382
8383                 if (logging != MagickFalse)
8384                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8385                     "  Setting up empty PLTE chunk");
8386               }
8387
8388             else
8389               {
8390                 for (i=0; i < (ssize_t) number_colors; i++)
8391                 {
8392                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8393                   palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8394                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8395                 }
8396
8397                 if (logging != MagickFalse)
8398                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8399                     "  Setting up PLTE chunk with %d colors",
8400                     number_colors);
8401
8402                 ping_have_PLTE=MagickTrue;
8403               }
8404
8405             /* color_type is PNG_COLOR_TYPE_PALETTE */
8406             if (mng_info->write_png_depth == 0)
8407               {
8408                 size_t
8409                   one;
8410
8411                 ping_bit_depth=1;
8412                 one=1;
8413
8414                 while ((one << ping_bit_depth) < number_colors)
8415                   ping_bit_depth <<= 1;
8416               }
8417
8418             ping_num_trans=0;
8419
8420             if (matte != MagickFalse)
8421               {
8422                 /*
8423                  * Set up trans_colors array.
8424                  */
8425                 assert(number_colors <= 256);
8426
8427                 ping_num_trans=(unsigned short) (number_transparent +
8428                   number_semitransparent);
8429
8430                 if (ping_num_trans == 0)
8431                   ping_have_tRNS=MagickFalse;
8432
8433                 else
8434                   {
8435                     if (logging != MagickFalse)
8436                       {
8437                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8438                           "  Scaling ping_trans_color (1)");
8439                       }
8440                     ping_have_tRNS=MagickTrue;
8441
8442                     for (i=0; i < ping_num_trans; i++)
8443                     {
8444                        ping_trans_alpha[i]= (png_byte) (255-
8445                           ScaleQuantumToChar(image->colormap[i].opacity));
8446                     }
8447                   }
8448               }
8449           }
8450       }
8451
8452     else
8453       {
8454
8455         if (image_depth < 8)
8456           image_depth=8;
8457
8458         if ((save_image_depth == 16) && (image_depth == 8))
8459           {
8460             if (logging != MagickFalse)
8461               {
8462                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8463                   "    Scaling ping_trans_color from (%d,%d,%d)",
8464                   (int) ping_trans_color.red,
8465                   (int) ping_trans_color.green,
8466                   (int) ping_trans_color.blue);
8467               }
8468
8469             ping_trans_color.red*=0x0101;
8470             ping_trans_color.green*=0x0101;
8471             ping_trans_color.blue*=0x0101;
8472             ping_trans_color.gray*=0x0101;
8473
8474             if (logging != MagickFalse)
8475               {
8476                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8477                   "    to (%d,%d,%d)",
8478                   (int) ping_trans_color.red,
8479                   (int) ping_trans_color.green,
8480                   (int) ping_trans_color.blue);
8481               }
8482           }
8483       }
8484
8485     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
8486          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
8487
8488     /*
8489       Adjust background and transparency samples in sub-8-bit grayscale files.
8490     */
8491     if (ping_bit_depth < 8 && ping_color_type ==
8492         PNG_COLOR_TYPE_GRAY)
8493       {
8494          png_uint_16
8495            maxval;
8496
8497          size_t
8498            one=1;
8499
8500          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
8501
8502          if (ping_exclude_bKGD == MagickFalse)
8503          {
8504
8505          ping_background.gray=(png_uint_16)
8506            (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8507
8508          if (logging != MagickFalse)
8509            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8510              "  Setting up bKGD chunk (2)");
8511
8512          ping_have_bKGD = MagickTrue;
8513          }
8514
8515          ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8516            ping_trans_color.gray));
8517       }
8518
8519   if (ping_exclude_bKGD == MagickFalse)
8520   {
8521     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8522       {
8523         /*
8524            Identify which colormap entry is the background color.
8525         */
8526
8527         number_colors=image_colors;
8528
8529         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8530           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
8531             break;
8532
8533         ping_background.index=(png_byte) i;
8534
8535         if (logging != MagickFalse)
8536           {
8537             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8538               "  Setting up bKGD chunk with index=%d",(int) i);
8539           }
8540
8541         if (i < (ssize_t) number_colors)
8542           {
8543             ping_have_bKGD = MagickTrue;
8544
8545             if (logging != MagickFalse)
8546               {
8547                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8548                   "     background   =(%d,%d,%d)",
8549                         (int) ping_background.red,
8550                         (int) ping_background.green,
8551                         (int) ping_background.blue);
8552               }
8553           }
8554
8555         else  /* Can't happen */
8556           {
8557             if (logging != MagickFalse)
8558               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8559                   "      No room in PLTE to add bKGD color");
8560             ping_have_bKGD = MagickFalse;
8561           }
8562       }
8563   }
8564
8565   if (logging != MagickFalse)
8566     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8567       "    PNG color type: %d",ping_color_type);
8568   /*
8569     Initialize compression level and filtering.
8570   */
8571   if (logging != MagickFalse)
8572     {
8573       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8574         "  Setting up deflate compression");
8575
8576       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8577         "    Compression buffer size: 32768");
8578     }
8579
8580   png_set_compression_buffer_size(ping,32768L);
8581
8582   if (logging != MagickFalse)
8583     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8584       "    Compression mem level: 9");
8585
8586   png_set_compression_mem_level(ping, 9);
8587
8588   quality=image->quality == UndefinedCompressionQuality ? 75UL :
8589      image->quality;
8590
8591   if (quality > 9)
8592     {
8593       int
8594         level;
8595
8596       level=(int) MagickMin((ssize_t) quality/10,9);
8597
8598       if (logging != MagickFalse)
8599         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8600           "    Compression level: %d",level);
8601
8602       png_set_compression_level(ping,level);
8603     }
8604
8605   else
8606     {
8607       if (logging != MagickFalse)
8608         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8609           "    Compression strategy: Z_HUFFMAN_ONLY");
8610
8611       png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
8612     }
8613
8614   if (logging != MagickFalse)
8615     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8616       "  Setting up filtering");
8617
8618 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
8619   /* This became available in libpng-1.0.9.  Output must be a MNG. */
8620   if (mng_info->write_mng && ((quality % 10) == 7))
8621     {
8622       if (logging != MagickFalse)
8623         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8624           "    Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
8625
8626       ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
8627     }
8628
8629   else
8630     if (logging != MagickFalse)
8631       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8632         "    Filter_type: 0");
8633 #endif
8634
8635   {
8636     int
8637       base_filter;
8638
8639     if ((quality % 10) > 5)
8640       base_filter=PNG_ALL_FILTERS;
8641
8642     else
8643       if ((quality % 10) != 5)
8644         base_filter=(int) quality % 10;
8645
8646       else
8647         if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
8648             ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
8649             (quality < 50))
8650           base_filter=PNG_NO_FILTERS;
8651
8652         else
8653           base_filter=PNG_ALL_FILTERS;
8654
8655     if (logging != MagickFalse)
8656       {
8657         if (base_filter == PNG_ALL_FILTERS)
8658           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8659             "    Base filter method: ADAPTIVE");
8660         else
8661           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8662             "    Base filter method: NONE");
8663       }
8664
8665     png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
8666   }
8667
8668   if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
8669      (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
8670     {
8671       ResetImageProfileIterator(image);
8672       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8673       {
8674         profile=GetImageProfile(image,name);
8675
8676         if (profile != (StringInfo *) NULL)
8677           {
8678 #ifdef PNG_WRITE_iCCP_SUPPORTED
8679             if ((LocaleCompare(name,"ICC") == 0) ||
8680                 (LocaleCompare(name,"ICM") == 0))
8681              {
8682
8683                if (ping_exclude_iCCP == MagickFalse)
8684                  {
8685                        png_set_iCCP(ping,ping_info,(const png_charp) name,0,
8686 #if (PNG_LIBPNG_VER < 10500)
8687                          (png_charp) GetStringInfoDatum(profile),
8688 #else
8689                          (png_const_bytep) GetStringInfoDatum(profile),
8690 #endif
8691                          (png_uint_32) GetStringInfoLength(profile));
8692                  }
8693              }
8694
8695             else
8696 #endif
8697               if (ping_exclude_zCCP == MagickFalse)
8698                 {
8699                   Magick_png_write_raw_profile(image_info,ping,ping_info,
8700                     (unsigned char *) name,(unsigned char *) name,
8701                     GetStringInfoDatum(profile),
8702                     (png_uint_32) GetStringInfoLength(profile));
8703                 }
8704           }
8705
8706           if (logging != MagickFalse)
8707             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8708               "  Setting up text chunk with %s profile",name);
8709
8710         name=GetNextImageProfile(image);
8711       }
8712   }
8713
8714 #if defined(PNG_WRITE_sRGB_SUPPORTED)
8715   if ((mng_info->have_write_global_srgb == 0) &&
8716       ((image->rendering_intent != UndefinedIntent) ||
8717       (image->colorspace == sRGBColorspace)))
8718     {
8719       if (ping_exclude_sRGB == MagickFalse)
8720         {
8721           /*
8722             Note image rendering intent.
8723           */
8724           if (logging != MagickFalse)
8725             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8726                 "  Setting up sRGB chunk");
8727
8728           (void) png_set_sRGB(ping,ping_info,(
8729             Magick_RenderingIntent_to_PNG_RenderingIntent(
8730               image->rendering_intent)));
8731
8732           if (ping_exclude_gAMA == MagickFalse)
8733             png_set_gAMA(ping,ping_info,0.45455);
8734         }
8735     }
8736
8737   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
8738 #endif
8739     {
8740       if (ping_exclude_gAMA == MagickFalse &&
8741           (ping_exclude_sRGB == MagickFalse ||
8742           (image->gamma < .45 || image->gamma > .46)))
8743       {
8744       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
8745         {
8746           /*
8747             Note image gamma.
8748             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8749           */
8750           if (logging != MagickFalse)
8751             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8752               "  Setting up gAMA chunk");
8753
8754           png_set_gAMA(ping,ping_info,image->gamma);
8755         }
8756       }
8757
8758       if (ping_exclude_cHRM == MagickFalse)
8759         {
8760           if ((mng_info->have_write_global_chrm == 0) &&
8761               (image->chromaticity.red_primary.x != 0.0))
8762             {
8763               /*
8764                 Note image chromaticity.
8765                 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8766               */
8767                PrimaryInfo
8768                  bp,
8769                  gp,
8770                  rp,
8771                  wp;
8772
8773                wp=image->chromaticity.white_point;
8774                rp=image->chromaticity.red_primary;
8775                gp=image->chromaticity.green_primary;
8776                bp=image->chromaticity.blue_primary;
8777
8778                if (logging != MagickFalse)
8779                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8780                    "  Setting up cHRM chunk");
8781
8782                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
8783                    bp.x,bp.y);
8784            }
8785         }
8786     }
8787
8788   ping_interlace_method=image_info->interlace != NoInterlace;
8789
8790   if (mng_info->write_mng)
8791     png_set_sig_bytes(ping,8);
8792
8793   /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
8794
8795   if (mng_info->write_png_colortype != 0)
8796     {
8797      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
8798        if (ping_have_color != MagickFalse)
8799          {
8800            ping_color_type = PNG_COLOR_TYPE_RGB;
8801
8802            if (ping_bit_depth < 8)
8803              ping_bit_depth=8;
8804          }
8805
8806      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
8807        if (ping_have_color != MagickFalse)
8808          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
8809     }
8810
8811   if (ping_need_colortype_warning != MagickFalse ||
8812      ((mng_info->write_png_depth &&
8813      (int) mng_info->write_png_depth != ping_bit_depth) ||
8814      (mng_info->write_png_colortype &&
8815      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
8816       mng_info->write_png_colortype != 7 &&
8817       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
8818     {
8819       if (logging != MagickFalse)
8820         {
8821           if (ping_need_colortype_warning != MagickFalse)
8822             {
8823               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8824                  "  Image has transparency but tRNS chunk was excluded");
8825             }
8826
8827           if (mng_info->write_png_depth)
8828             {
8829               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8830                   "  Defined PNG:bit-depth=%u, Computed depth=%u",
8831                   mng_info->write_png_depth,
8832                   ping_bit_depth);
8833             }
8834
8835           if (mng_info->write_png_colortype)
8836             {
8837               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8838                   "  Defined PNG:color-type=%u, Computed color type=%u",
8839                   mng_info->write_png_colortype-1,
8840                   ping_color_type);
8841             }
8842         }
8843
8844       png_warning(ping,
8845         "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
8846     }
8847
8848   if (image_matte != MagickFalse && image->matte == MagickFalse)
8849     {
8850       /* Add an opaque matte channel */
8851       image->matte = MagickTrue;
8852       (void) SetImageOpacity(image,0);
8853
8854       if (logging != MagickFalse)
8855         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8856           "  Added an opaque matte channel");
8857     }
8858
8859   if (number_transparent != 0 || number_semitransparent != 0)
8860     {
8861       if (ping_color_type < 4)
8862         {
8863            ping_have_tRNS=MagickTrue;
8864            if (logging != MagickFalse)
8865              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8866                "  Setting ping_have_tRNS=MagickTrue.");
8867         }
8868     }
8869
8870   if (logging != MagickFalse)
8871     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8872       "  Writing PNG header chunks");
8873
8874   png_set_IHDR(ping,ping_info,ping_width,ping_height,
8875                ping_bit_depth,ping_color_type,
8876                ping_interlace_method,ping_compression_method,
8877                ping_filter_method);
8878
8879   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
8880     {
8881       png_set_PLTE(ping,ping_info,palette,number_colors);
8882
8883       if (logging != MagickFalse)
8884         {
8885           for (i=0; i< (ssize_t) number_colors; i++)
8886           {
8887             if (i < ping_num_trans)
8888               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8889                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
8890                       (int) i,
8891                       (int) palette[i].red,
8892                       (int) palette[i].green,
8893                       (int) palette[i].blue,
8894                       (int) i,
8895                       (int) ping_trans_alpha[i]);
8896              else
8897               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8898                 "     PLTE[%d] = (%d,%d,%d)",
8899                       (int) i,
8900                       (int) palette[i].red,
8901                       (int) palette[i].green,
8902                       (int) palette[i].blue);
8903            }
8904          }
8905     }
8906
8907   if (ping_exclude_bKGD == MagickFalse)
8908     {
8909       if (ping_have_bKGD != MagickFalse)
8910           png_set_bKGD(ping,ping_info,&ping_background);
8911     }
8912
8913   if (ping_exclude_pHYs == MagickFalse)
8914     {
8915       if (ping_have_pHYs != MagickFalse)
8916         {
8917           png_set_pHYs(ping,ping_info,
8918              ping_pHYs_x_resolution,
8919              ping_pHYs_y_resolution,
8920              ping_pHYs_unit_type);
8921
8922           if (logging)
8923             {
8924               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8925                    "    Setting up pHYs chunk");
8926               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8927                    "      x_resolution=%lu",
8928                    (unsigned long) ping_pHYs_x_resolution);
8929               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8930                    "      y_resolution=%lu",
8931                    (unsigned long) ping_pHYs_y_resolution);
8932               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8933                    "      unit_type=%lu",
8934                    (unsigned long) ping_pHYs_unit_type);
8935             }
8936         }
8937     }
8938
8939 #if defined(PNG_oFFs_SUPPORTED)
8940   if (ping_exclude_oFFs == MagickFalse)
8941     {
8942       if (image->page.x || image->page.y)
8943         {
8944            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
8945               (png_int_32) image->page.y, 0);
8946
8947            if (logging != MagickFalse)
8948              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8949                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
8950                  (int) image->page.x, (int) image->page.y);
8951         }
8952     }
8953 #endif
8954
8955   if (mng_info->need_blob != MagickFalse)
8956   {
8957     if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
8958        MagickFalse)
8959        png_error(ping,"WriteBlob Failed");
8960
8961      ping_have_blob=MagickTrue;
8962   }
8963
8964   png_write_info_before_PLTE(ping, ping_info);
8965
8966   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
8967     {
8968       if (logging != MagickFalse)
8969         {
8970           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8971               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
8972         }
8973
8974       if (ping_color_type == 3)
8975          (void) png_set_tRNS(ping, ping_info,
8976                 ping_trans_alpha,
8977                 ping_num_trans,
8978                 NULL);
8979
8980       else
8981         {
8982            (void) png_set_tRNS(ping, ping_info,
8983                   NULL,
8984                   0,
8985                   &ping_trans_color);
8986
8987            if (logging != MagickFalse)
8988              {
8989                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8990                  "     tRNS color   =(%d,%d,%d)",
8991                        (int) ping_trans_color.red,
8992                        (int) ping_trans_color.green,
8993                        (int) ping_trans_color.blue);
8994              }
8995          }
8996     }
8997
8998   /* write any png-chunk-b profiles */
8999   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
9000
9001   png_write_info(ping,ping_info);
9002
9003   /* write any PNG-chunk-m profiles */
9004   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
9005
9006   if (ping_exclude_vpAg == MagickFalse)
9007     {
9008       if ((image->page.width != 0 && image->page.width != image->columns) ||
9009           (image->page.height != 0 && image->page.height != image->rows))
9010         {
9011           unsigned char
9012             chunk[14];
9013
9014           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
9015           PNGType(chunk,mng_vpAg);
9016           LogPNGChunk(logging,mng_vpAg,9L);
9017           PNGLong(chunk+4,(png_uint_32) image->page.width);
9018           PNGLong(chunk+8,(png_uint_32) image->page.height);
9019           chunk[12]=0;   /* unit = pixels */
9020           (void) WriteBlob(image,13,chunk);
9021           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9022         }
9023     }
9024
9025 #if (PNG_LIBPNG_VER == 10206)
9026     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
9027 #define PNG_HAVE_IDAT               0x04
9028     ping->mode |= PNG_HAVE_IDAT;
9029 #undef PNG_HAVE_IDAT
9030 #endif
9031
9032   png_set_packing(ping);
9033   /*
9034     Allocate memory.
9035   */
9036   rowbytes=image->columns;
9037   if (image_depth > 8)
9038     rowbytes*=2;
9039   switch (ping_color_type)
9040     {
9041       case PNG_COLOR_TYPE_RGB:
9042         rowbytes*=3;
9043         break;
9044
9045       case PNG_COLOR_TYPE_GRAY_ALPHA:
9046         rowbytes*=2;
9047         break;
9048
9049       case PNG_COLOR_TYPE_RGBA:
9050         rowbytes*=4;
9051         break;
9052
9053       default:
9054         break;
9055     }
9056
9057   if (logging != MagickFalse)
9058     {
9059       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9060         "  Writing PNG image data");
9061
9062       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9063         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
9064     }
9065   ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9066     sizeof(*ping_pixels));
9067
9068   if (ping_pixels == (unsigned char *) NULL)
9069     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9070
9071   /*
9072     Initialize image scanlines.
9073   */
9074   if (setjmp(png_jmpbuf(ping)))
9075     {
9076       /*
9077         PNG write failed.
9078       */
9079 #ifdef PNG_DEBUG
9080      if (image_info->verbose)
9081         (void) printf("PNG write has failed.\n");
9082 #endif
9083       png_destroy_write_struct(&ping,&ping_info);
9084       if (quantum_info != (QuantumInfo *) NULL)
9085         quantum_info=DestroyQuantumInfo(quantum_info);
9086       if (ping_pixels != (unsigned char *) NULL)
9087         ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9088 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9089       UnlockSemaphoreInfo(ping_semaphore);
9090 #endif
9091       if (ping_have_blob != MagickFalse)
9092           (void) CloseBlob(image);
9093       image_info=DestroyImageInfo(image_info);
9094       image=DestroyImage(image);
9095       return(MagickFalse);
9096     }
9097   quantum_info=AcquireQuantumInfo(image_info,image);
9098   if (quantum_info == (QuantumInfo *) NULL)
9099     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9100   quantum_info->format=UndefinedQuantumFormat;
9101   quantum_info->depth=image_depth;
9102   num_passes=png_set_interlace_handling(ping);
9103
9104   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9105        !mng_info->write_png32) &&
9106        (mng_info->IsPalette ||
9107        (image_info->type == BilevelType)) &&
9108        image_matte == MagickFalse &&
9109        ping_have_non_bw == MagickFalse)
9110     {
9111       /* Palette, Bilevel, or Opaque Monochrome */
9112       register const PixelPacket
9113         *p;
9114
9115       quantum_info->depth=8;
9116       for (pass=0; pass < num_passes; pass++)
9117       {
9118         /*
9119           Convert PseudoClass image to a PNG monochrome image.
9120         */
9121         for (y=0; y < (ssize_t) image->rows; y++)
9122         {
9123           if (logging != MagickFalse && y == 0)
9124              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9125                  "    Writing row of pixels (0)");
9126
9127           p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9128
9129           if (p == (const PixelPacket *) NULL)
9130             break;
9131
9132           if (mng_info->IsPalette)
9133             {
9134               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9135                 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9136               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9137                   mng_info->write_png_depth &&
9138                   mng_info->write_png_depth != old_bit_depth)
9139                 {
9140                   /* Undo pixel scaling */
9141                   for (i=0; i < (ssize_t) image->columns; i++)
9142                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
9143                      >> (8-old_bit_depth));
9144                 }
9145             }
9146
9147           else
9148             {
9149               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9150                 quantum_info,RedQuantum,ping_pixels,&image->exception);
9151             }
9152
9153           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
9154             for (i=0; i < (ssize_t) image->columns; i++)
9155                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
9156                       255 : 0);
9157
9158           if (logging != MagickFalse && y == 0)
9159             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9160                 "    Writing row of pixels (1)");
9161
9162           png_write_row(ping,ping_pixels);
9163         }
9164         if (image->previous == (Image *) NULL)
9165           {
9166             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9167             if (status == MagickFalse)
9168               break;
9169           }
9170       }
9171     }
9172
9173   else   /* Not Palette, Bilevel, or Opaque Monochrome */
9174     {
9175       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9176          !mng_info->write_png32) &&
9177          (image_matte != MagickFalse ||
9178          (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
9179          (mng_info->IsPalette) && ping_have_color == MagickFalse)
9180         {
9181           register const PixelPacket
9182             *p;
9183
9184           for (pass=0; pass < num_passes; pass++)
9185           {
9186
9187           for (y=0; y < (ssize_t) image->rows; y++)
9188           {
9189             p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9190
9191             if (p == (const PixelPacket *) NULL)
9192               break;
9193
9194             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9195               {
9196                 if (mng_info->IsPalette)
9197                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9198                     quantum_info,GrayQuantum,ping_pixels,&image->exception);
9199
9200                 else
9201                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9202                     quantum_info,RedQuantum,ping_pixels,&image->exception);
9203
9204                 if (logging != MagickFalse && y == 0)
9205                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9206                        "    Writing GRAY PNG pixels (2)");
9207               }
9208
9209             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9210               {
9211                 if (logging != MagickFalse && y == 0)
9212                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9213                          "    Writing GRAY_ALPHA PNG pixels (2)");
9214
9215                 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9216                   quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
9217               }
9218
9219             if (logging != MagickFalse && y == 0)
9220               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9221                   "    Writing row of pixels (2)");
9222
9223             png_write_row(ping,ping_pixels);
9224           }
9225
9226           if (image->previous == (Image *) NULL)
9227             {
9228               status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9229               if (status == MagickFalse)
9230                 break;
9231             }
9232           }
9233         }
9234
9235       else
9236         {
9237           register const PixelPacket
9238             *p;
9239
9240           for (pass=0; pass < num_passes; pass++)
9241           {
9242             if ((image_depth > 8) || (mng_info->write_png24 ||
9243                 mng_info->write_png32 ||
9244                 (!mng_info->write_png8 && !mng_info->IsPalette)))
9245             {
9246               for (y=0; y < (ssize_t) image->rows; y++)
9247               {
9248                 p=GetVirtualPixels(image,0,y,image->columns,1,
9249                    &image->exception);
9250
9251                 if (p == (const PixelPacket *) NULL)
9252                   break;
9253
9254                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9255                   {
9256                     if (image->storage_class == DirectClass)
9257                       (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9258                         quantum_info,RedQuantum,ping_pixels,&image->exception);
9259
9260                     else
9261                       (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9262                         quantum_info,GrayQuantum,ping_pixels,&image->exception);
9263                   }
9264
9265                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9266                   {
9267                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9268                       quantum_info,GrayAlphaQuantum,ping_pixels,
9269                       &image->exception);
9270
9271                     if (logging != MagickFalse && y == 0)
9272                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9273                            "    Writing GRAY_ALPHA PNG pixels (3)");
9274                   }
9275
9276                 else if (image_matte != MagickFalse)
9277                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9278                     quantum_info,RGBAQuantum,ping_pixels,&image->exception);
9279
9280                 else
9281                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9282                     quantum_info,RGBQuantum,ping_pixels,&image->exception);
9283
9284                 if (logging != MagickFalse && y == 0)
9285                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9286                       "    Writing row of pixels (3)");
9287
9288                 png_write_row(ping,ping_pixels);
9289               }
9290             }
9291
9292           else
9293             /* not ((image_depth > 8) || (mng_info->write_png24 ||
9294                 mng_info->write_png32 ||
9295                 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9296             {
9297               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9298                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9299                 {
9300                   if (logging != MagickFalse)
9301                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9302                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
9303
9304                   quantum_info->depth=8;
9305                   image_depth=8;
9306                 }
9307
9308               for (y=0; y < (ssize_t) image->rows; y++)
9309               {
9310                 if (logging != MagickFalse && y == 0)
9311                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9312                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
9313
9314                 p=GetVirtualPixels(image,0,y,image->columns,1,
9315                    &image->exception);
9316
9317                 if (p == (const PixelPacket *) NULL)
9318                   break;
9319
9320                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9321                   {
9322                     quantum_info->depth=image->depth;
9323
9324                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9325                        quantum_info,GrayQuantum,ping_pixels,&image->exception);
9326                   }
9327
9328                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9329                   {
9330                     if (logging != MagickFalse && y == 0)
9331                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9332                            "  Writing GRAY_ALPHA PNG pixels (4)");
9333
9334                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9335                          quantum_info,GrayAlphaQuantum,ping_pixels,
9336                          &image->exception);
9337                   }
9338
9339                 else
9340                   {
9341                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9342                       quantum_info,IndexQuantum,ping_pixels,&image->exception);
9343
9344                     if (logging != MagickFalse && y <= 2)
9345                     {
9346                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9347                           "  Writing row of non-gray pixels (4)");
9348
9349                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9350                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
9351                           (int)ping_pixels[0],(int)ping_pixels[1]);
9352                     }
9353                   }
9354                 png_write_row(ping,ping_pixels);
9355               }
9356             }
9357
9358             if (image->previous == (Image *) NULL)
9359               {
9360                 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9361                 if (status == MagickFalse)
9362                   break;
9363               }
9364           }
9365         }
9366     }
9367
9368   if (quantum_info != (QuantumInfo *) NULL)
9369     quantum_info=DestroyQuantumInfo(quantum_info);
9370
9371   if (logging != MagickFalse)
9372     {
9373       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9374         "  Wrote PNG image data");
9375
9376       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9377         "    Width: %.20g",(double) ping_width);
9378
9379       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9380         "    Height: %.20g",(double) ping_height);
9381
9382       if (mng_info->write_png_depth)
9383         {
9384           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9385             "    Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9386         }
9387
9388       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9389         "    PNG bit-depth written: %d",ping_bit_depth);
9390
9391       if (mng_info->write_png_colortype)
9392         {
9393           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9394             "    Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9395         }
9396
9397       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9398         "    PNG color-type written: %d",ping_color_type);
9399
9400       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9401         "    PNG Interlace method: %d",ping_interlace_method);
9402     }
9403   /*
9404     Generate text chunks.
9405   */
9406   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
9407   {
9408     ResetImagePropertyIterator(image);
9409     property=GetNextImageProperty(image);
9410     while (property != (const char *) NULL)
9411     {
9412       png_textp
9413         text;
9414
9415       value=GetImageProperty(image,property);
9416       if (ping_exclude_pHYs != MagickFalse       ||
9417           LocaleCompare(property,"density") != 0 ||
9418           LocaleCompare(property,"units") != 0)
9419         {
9420         if (value != (const char *) NULL)
9421           {
9422             text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9423             text[0].key=(char *) property;
9424             text[0].text=(char *) value;
9425             text[0].text_length=strlen(value);
9426
9427             if (ping_exclude_tEXt != MagickFalse)
9428                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9429
9430             else if (ping_exclude_zTXt != MagickFalse)
9431                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9432
9433             else
9434             {
9435                text[0].compression=image_info->compression == NoCompression ||
9436                  (image_info->compression == UndefinedCompression &&
9437                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9438                  PNG_TEXT_COMPRESSION_zTXt ;
9439             }
9440
9441             if (logging != MagickFalse)
9442               {
9443                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9444                   "  Setting up text chunk");
9445
9446                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9447                   "    keyword: %s",text[0].key);
9448               }
9449
9450             png_set_text(ping,ping_info,text,1);
9451             png_free(ping,text);
9452           }
9453         }
9454       property=GetNextImageProperty(image);
9455     }
9456   }
9457
9458   /* write any PNG-chunk-e profiles */
9459   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
9460
9461   if (logging != MagickFalse)
9462     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9463       "  Writing PNG end info");
9464
9465   png_write_end(ping,ping_info);
9466
9467   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9468     {
9469       if (mng_info->page.x || mng_info->page.y ||
9470           (ping_width != mng_info->page.width) ||
9471           (ping_height != mng_info->page.height))
9472         {
9473           unsigned char
9474             chunk[32];
9475
9476           /*
9477             Write FRAM 4 with clipping boundaries followed by FRAM 1.
9478           */
9479           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
9480           PNGType(chunk,mng_FRAM);
9481           LogPNGChunk(logging,mng_FRAM,27L);
9482           chunk[4]=4;
9483           chunk[5]=0;  /* frame name separator (no name) */
9484           chunk[6]=1;  /* flag for changing delay, for next frame only */
9485           chunk[7]=0;  /* flag for changing frame timeout */
9486           chunk[8]=1;  /* flag for changing frame clipping for next frame */
9487           chunk[9]=0;  /* flag for changing frame sync_id */
9488           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9489           chunk[14]=0; /* clipping boundaries delta type */
9490           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9491           PNGLong(chunk+19,
9492              (png_uint_32) (mng_info->page.x + ping_width));
9493           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9494           PNGLong(chunk+27,
9495              (png_uint_32) (mng_info->page.y + ping_height));
9496           (void) WriteBlob(image,31,chunk);
9497           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9498           mng_info->old_framing_mode=4;
9499           mng_info->framing_mode=1;
9500         }
9501
9502       else
9503         mng_info->framing_mode=3;
9504     }
9505   if (mng_info->write_mng && !mng_info->need_fram &&
9506       ((int) image->dispose == 3))
9507      (void) ThrowMagickException(&image->exception,GetMagickModule(),
9508        CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
9509        "`%s'",image->filename);
9510
9511   /*
9512     Free PNG resources.
9513   */
9514
9515   png_destroy_write_struct(&ping,&ping_info);
9516
9517   ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9518
9519 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9520   UnlockSemaphoreInfo(ping_semaphore);
9521 #endif
9522
9523   if (ping_have_blob != MagickFalse)
9524      (void) CloseBlob(image);
9525
9526   image_info=DestroyImageInfo(image_info);
9527   image=DestroyImage(image);
9528
9529   /* Store bit depth actually written */
9530   s[0]=(char) ping_bit_depth;
9531   s[1]='\0';
9532
9533   (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9534
9535   if (logging != MagickFalse)
9536     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9537       "  exit WriteOnePNGImage()");
9538
9539   return(MagickTrue);
9540 /*  End write one PNG image */
9541 }
9542
9543 /*
9544 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9545 %                                                                             %
9546 %                                                                             %
9547 %                                                                             %
9548 %   W r i t e P N G I m a g e                                                 %
9549 %                                                                             %
9550 %                                                                             %
9551 %                                                                             %
9552 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9553 %
9554 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
9555 %  Multiple-image Network Graphics (MNG) image file.
9556 %
9557 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
9558 %
9559 %  The format of the WritePNGImage method is:
9560 %
9561 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9562 %
9563 %  A description of each parameter follows:
9564 %
9565 %    o image_info: the image info.
9566 %
9567 %    o image:  The image.
9568 %
9569 %  Returns MagickTrue on success, MagickFalse on failure.
9570 %
9571 %  Communicating with the PNG encoder:
9572 %
9573 %  While the datastream written is always in PNG format and normally would
9574 %  be given the "png" file extension, this method also writes the following
9575 %  pseudo-formats which are subsets of PNG:
9576 %
9577 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
9578 %               a depth greater than 8, the depth is reduced. If transparency
9579 %               is present, the tRNS chunk must only have values 0 and 255
9580 %               (i.e., transparency is binary: fully opaque or fully
9581 %               transparent).  If other values are present they will be
9582 %               50%-thresholded to binary transparency.  If more than 256
9583 %               colors are present, they will be quantized to the 3-3-2
9584 %               palette.  If you want better quantization or dithering of
9585 %               the colors or alpha, you need to do it before calling the
9586 %               PNG encoder. The pixels contain 8-bit indices even if
9587 %               they could be represented with 1, 2, or 4 bits.  Grayscale
9588 %               images will be written as indexed PNG files even though the
9589 %               PNG grayscale type might be slightly more efficient.  Please
9590 %               note that writing to the PNG8 format may result in loss
9591 %               of color and alpha data.
9592 %
9593 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
9594 %               chunk can be present to convey binary transparency by naming
9595 %               one of the colors as transparent.  The only loss incurred
9596 %               is reduction of sample depth to 8.  If the image has more
9597 %               than one transparent color, has semitransparent pixels, or
9598 %               has an opaque pixel with the same RGB components as the
9599 %               transparent color, an image is not written.
9600 %
9601 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
9602 %               transparency is permitted, i.e., the alpha sample for
9603 %               each pixel can have any value from 0 to 255. The alpha
9604 %               channel is present even if the image is fully opaque.
9605 %               The only loss in data is the reduction of the sample depth
9606 %               to 8.
9607 %
9608 %    o -define: For more precise control of the PNG output, you can use the
9609 %               Image options "png:bit-depth" and "png:color-type".  These
9610 %               can be set from the commandline with "-define" and also
9611 %               from the application programming interfaces.  The options
9612 %               are case-independent and are converted to lowercase before
9613 %               being passed to this encoder.
9614 %
9615 %               png:color-type can be 0, 2, 3, 4, or 6.
9616 %
9617 %               When png:color-type is 0 (Grayscale), png:bit-depth can
9618 %               be 1, 2, 4, 8, or 16.
9619 %
9620 %               When png:color-type is 2 (RGB), png:bit-depth can
9621 %               be 8 or 16.
9622 %
9623 %               When png:color-type is 3 (Indexed), png:bit-depth can
9624 %               be 1, 2, 4, or 8.  This refers to the number of bits
9625 %               used to store the index.  The color samples always have
9626 %               bit-depth 8 in indexed PNG files.
9627 %
9628 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
9629 %               png:bit-depth can be 8 or 16.
9630 %
9631 %  If the image cannot be written without loss with the requested bit-depth
9632 %  and color-type, a PNG file will not be written, and the encoder will
9633 %  return MagickFalse.
9634 %
9635 %  Since image encoders should not be responsible for the "heavy lifting",
9636 %  the user should make sure that ImageMagick has already reduced the
9637 %  image depth and number of colors and limit transparency to binary
9638 %  transparency prior to attempting to write the image with depth, color,
9639 %   or transparency limitations.
9640 %
9641 %  TODO: Enforce the previous paragraph.
9642 %
9643 %  Note that another definition, "png:bit-depth-written" exists, but it
9644 %  is not intended for external use.  It is only used internally by the
9645 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
9646 %
9647 %  It is possible to request that the PNG encoder write previously-formatted
9648 %  ancillary chunks in the output PNG file, using the "-profile" commandline
9649 %  option as shown below or by setting the profile via a programming
9650 %  interface:
9651 %
9652 %     -profile PNG-chunk-x:<file>
9653 %
9654 %  where x is a location flag and <file> is a file containing the chunk
9655 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
9656 %  This encoder will compute the chunk length and CRC, so those must not
9657 %  be included in the file.
9658 %
9659 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
9660 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
9661 %  of the same type, then add a short unique string after the "x" to prevent
9662 %  subsequent profiles from overwriting the preceding ones, e.g.,
9663 %
9664 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
9665 %
9666 %  As of version 6.6.6 the following optimizations are always done:
9667 %
9668 %   o  32-bit depth is reduced to 16.
9669 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
9670 %      high byte and low byte are identical.
9671 %   o  Palette is sorted to remove unused entries and to put a
9672 %      transparent color first, if BUILD_PNG_PALETTE is defined.
9673 %   o  Opaque matte channel is removed when writing an indexed PNG.
9674 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
9675 %      this can be done without loss and a larger bit depth N was not
9676 %      requested via the "-define PNG:bit-depth=N" option.
9677 %   o  If matte channel is present but only one transparent color is
9678 %      present, RGB+tRNS is written instead of RGBA
9679 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
9680 %      was requested when converting an opaque image).
9681 %
9682 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9683 */
9684 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
9685   Image *image)
9686 {
9687   MagickBooleanType
9688     excluding,
9689     logging,
9690     have_mng_structure,
9691     status;
9692
9693   MngInfo
9694     *mng_info;
9695
9696   const char
9697     *value;
9698
9699   int
9700     i,
9701     source;
9702
9703   /*
9704     Open image file.
9705   */
9706   assert(image_info != (const ImageInfo *) NULL);
9707   assert(image_info->signature == MagickSignature);
9708   assert(image != (Image *) NULL);
9709   assert(image->signature == MagickSignature);
9710   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
9711   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
9712   /*
9713     Allocate a MngInfo structure.
9714   */
9715   have_mng_structure=MagickFalse;
9716   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
9717
9718   if (mng_info == (MngInfo *) NULL)
9719     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9720
9721   /*
9722     Initialize members of the MngInfo structure.
9723   */
9724   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
9725   mng_info->image=image;
9726   mng_info->equal_backgrounds=MagickTrue;
9727   have_mng_structure=MagickTrue;
9728
9729   /* See if user has requested a specific PNG subformat */
9730
9731   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
9732   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
9733   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
9734
9735   if (mng_info->write_png8)
9736     {
9737       mng_info->write_png_colortype = /* 3 */ 4;
9738       mng_info->write_png_depth = 8;
9739       image->depth = 8;
9740     }
9741
9742   if (mng_info->write_png24)
9743     {
9744       mng_info->write_png_colortype = /* 2 */ 3;
9745       mng_info->write_png_depth = 8;
9746       image->depth = 8;
9747
9748       if (image->matte == MagickTrue)
9749         (void) SetImageType(image,TrueColorMatteType);
9750
9751       else
9752         (void) SetImageType(image,TrueColorType);
9753
9754       (void) SyncImage(image);
9755     }
9756
9757   if (mng_info->write_png32)
9758     {
9759       mng_info->write_png_colortype = /* 6 */  7;
9760       mng_info->write_png_depth = 8;
9761       image->depth = 8;
9762
9763       if (image->matte == MagickTrue)
9764         (void) SetImageType(image,TrueColorMatteType);
9765
9766       else
9767         (void) SetImageType(image,TrueColorType);
9768
9769       (void) SyncImage(image);
9770     }
9771
9772   value=GetImageOption(image_info,"png:bit-depth");
9773
9774   if (value != (char *) NULL)
9775     {
9776       if (LocaleCompare(value,"1") == 0)
9777         mng_info->write_png_depth = 1;
9778
9779       else if (LocaleCompare(value,"2") == 0)
9780         mng_info->write_png_depth = 2;
9781
9782       else if (LocaleCompare(value,"4") == 0)
9783         mng_info->write_png_depth = 4;
9784
9785       else if (LocaleCompare(value,"8") == 0)
9786         mng_info->write_png_depth = 8;
9787
9788       else if (LocaleCompare(value,"16") == 0)
9789         mng_info->write_png_depth = 16;
9790
9791       else
9792         (void) ThrowMagickException(&image->exception,
9793              GetMagickModule(),CoderWarning,
9794              "ignoring invalid defined png:bit-depth",
9795              "=%s",value);
9796
9797       if (logging != MagickFalse)
9798         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9799           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
9800     }
9801
9802   value=GetImageOption(image_info,"png:color-type");
9803
9804   if (value != (char *) NULL)
9805     {
9806       /* We must store colortype+1 because 0 is a valid colortype */
9807       if (LocaleCompare(value,"0") == 0)
9808         mng_info->write_png_colortype = 1;
9809
9810       else if (LocaleCompare(value,"2") == 0)
9811         mng_info->write_png_colortype = 3;
9812
9813       else if (LocaleCompare(value,"3") == 0)
9814         mng_info->write_png_colortype = 4;
9815
9816       else if (LocaleCompare(value,"4") == 0)
9817         mng_info->write_png_colortype = 5;
9818
9819       else if (LocaleCompare(value,"6") == 0)
9820         mng_info->write_png_colortype = 7;
9821
9822       else
9823         (void) ThrowMagickException(&image->exception,
9824              GetMagickModule(),CoderWarning,
9825              "ignoring invalid defined png:color-type",
9826              "=%s",value);
9827
9828       if (logging != MagickFalse)
9829         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9830           "  png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
9831     }
9832
9833   /* Check for chunks to be excluded:
9834    *
9835    * The default is to not exclude any known chunks except for any
9836    * listed in the "unused_chunks" array, above.
9837    *
9838    * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
9839    * define (in the image properties or in the image artifacts)
9840    * or via a mng_info member.  For convenience, in addition
9841    * to or instead of a comma-separated list of chunks, the
9842    * "exclude-chunk" string can be simply "all" or "none".
9843    *
9844    * The exclude-chunk define takes priority over the mng_info.
9845    *
9846    * A "PNG:include-chunk" define takes  priority over both the
9847    * mng_info and the "PNG:exclude-chunk" define.  Like the
9848    * "exclude-chunk" string, it can define "all" or "none" as
9849    * well as a comma-separated list.  Chunks that are unknown to
9850    * ImageMagick are always excluded, regardless of their "copy-safe"
9851    * status according to the PNG specification, and even if they
9852    * appear in the "include-chunk" list.
9853    *
9854    * Finally, all chunks listed in the "unused_chunks" array are
9855    * automatically excluded, regardless of the other instructions
9856    * or lack thereof.
9857    *
9858    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
9859    * will not be written and the gAMA chunk will only be written if it
9860    * is not between .45 and .46, or approximately (1.0/2.2).
9861    *
9862    * If you exclude tRNS and the image has transparency, the colortype
9863    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
9864    *
9865    * The -strip option causes StripImage() to set the png:include-chunk
9866    * artifact to "none,gama".
9867    */
9868
9869   mng_info->ping_exclude_bKGD=MagickFalse;
9870   mng_info->ping_exclude_cHRM=MagickFalse;
9871   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
9872   mng_info->ping_exclude_gAMA=MagickFalse;
9873   mng_info->ping_exclude_cHRM=MagickFalse;
9874   mng_info->ping_exclude_iCCP=MagickFalse;
9875   /* mng_info->ping_exclude_iTXt=MagickFalse; */
9876   mng_info->ping_exclude_oFFs=MagickFalse;
9877   mng_info->ping_exclude_pHYs=MagickFalse;
9878   mng_info->ping_exclude_sRGB=MagickFalse;
9879   mng_info->ping_exclude_tEXt=MagickFalse;
9880   mng_info->ping_exclude_tRNS=MagickFalse;
9881   mng_info->ping_exclude_vpAg=MagickFalse;
9882   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
9883   mng_info->ping_exclude_zTXt=MagickFalse;
9884
9885   excluding=MagickFalse;
9886
9887   for (source=0; source<1; source++)
9888   {
9889     if (source==0)
9890       {
9891        value=GetImageArtifact(image,"png:exclude-chunk");
9892
9893        if (value == NULL)
9894          value=GetImageArtifact(image,"png:exclude-chunks");
9895       }
9896     else
9897       {
9898        value=GetImageOption(image_info,"png:exclude-chunk");
9899
9900        if (value == NULL)
9901          value=GetImageOption(image_info,"png:exclude-chunks");
9902       }
9903
9904     if (value != NULL)
9905     {
9906
9907     size_t
9908       last;
9909
9910     excluding=MagickTrue;
9911
9912     if (logging != MagickFalse)
9913       {
9914         if (source == 0)
9915            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9916               "  png:exclude-chunk=%s found in image artifacts.\n", value);
9917         else
9918            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9919               "  png:exclude-chunk=%s found in image properties.\n", value);
9920       }
9921
9922     last=strlen(value);
9923
9924     for (i=0; i<(int) last; i+=5)
9925     {
9926
9927       if (LocaleNCompare(value+i,"all",3) == 0)
9928       {
9929         mng_info->ping_exclude_bKGD=MagickTrue;
9930         mng_info->ping_exclude_cHRM=MagickTrue;
9931         mng_info->ping_exclude_EXIF=MagickTrue;
9932         mng_info->ping_exclude_gAMA=MagickTrue;
9933         mng_info->ping_exclude_iCCP=MagickTrue;
9934         /* mng_info->ping_exclude_iTXt=MagickTrue; */
9935         mng_info->ping_exclude_oFFs=MagickTrue;
9936         mng_info->ping_exclude_pHYs=MagickTrue;
9937         mng_info->ping_exclude_sRGB=MagickTrue;
9938         mng_info->ping_exclude_tEXt=MagickTrue;
9939         mng_info->ping_exclude_tRNS=MagickTrue;
9940         mng_info->ping_exclude_vpAg=MagickTrue;
9941         mng_info->ping_exclude_zCCP=MagickTrue;
9942         mng_info->ping_exclude_zTXt=MagickTrue;
9943         i--;
9944       }
9945
9946       if (LocaleNCompare(value+i,"none",4) == 0)
9947       {
9948         mng_info->ping_exclude_bKGD=MagickFalse;
9949         mng_info->ping_exclude_cHRM=MagickFalse;
9950         mng_info->ping_exclude_EXIF=MagickFalse;
9951         mng_info->ping_exclude_gAMA=MagickFalse;
9952         mng_info->ping_exclude_iCCP=MagickFalse;
9953         /* mng_info->ping_exclude_iTXt=MagickFalse; */
9954         mng_info->ping_exclude_oFFs=MagickFalse;
9955         mng_info->ping_exclude_pHYs=MagickFalse;
9956         mng_info->ping_exclude_sRGB=MagickFalse;
9957         mng_info->ping_exclude_tEXt=MagickFalse;
9958         mng_info->ping_exclude_tRNS=MagickFalse;
9959         mng_info->ping_exclude_vpAg=MagickFalse;
9960         mng_info->ping_exclude_zCCP=MagickFalse;
9961         mng_info->ping_exclude_zTXt=MagickFalse;
9962       }
9963
9964       if (LocaleNCompare(value+i,"bkgd",4) == 0)
9965         mng_info->ping_exclude_bKGD=MagickTrue;
9966
9967       if (LocaleNCompare(value+i,"chrm",4) == 0)
9968         mng_info->ping_exclude_cHRM=MagickTrue;
9969
9970       if (LocaleNCompare(value+i,"exif",4) == 0)
9971         mng_info->ping_exclude_EXIF=MagickTrue;
9972
9973       if (LocaleNCompare(value+i,"gama",4) == 0)
9974         mng_info->ping_exclude_gAMA=MagickTrue;
9975
9976       if (LocaleNCompare(value+i,"iccp",4) == 0)
9977         mng_info->ping_exclude_iCCP=MagickTrue;
9978
9979     /*
9980       if (LocaleNCompare(value+i,"itxt",4) == 0)
9981         mng_info->ping_exclude_iTXt=MagickTrue;
9982      */
9983
9984       if (LocaleNCompare(value+i,"gama",4) == 0)
9985         mng_info->ping_exclude_gAMA=MagickTrue;
9986
9987       if (LocaleNCompare(value+i,"offs",4) == 0)
9988         mng_info->ping_exclude_oFFs=MagickTrue;
9989
9990       if (LocaleNCompare(value+i,"phys",4) == 0)
9991         mng_info->ping_exclude_pHYs=MagickTrue;
9992
9993       if (LocaleNCompare(value+i,"srgb",4) == 0)
9994         mng_info->ping_exclude_sRGB=MagickTrue;
9995
9996       if (LocaleNCompare(value+i,"text",4) == 0)
9997         mng_info->ping_exclude_tEXt=MagickTrue;
9998
9999       if (LocaleNCompare(value+i,"trns",4) == 0)
10000         mng_info->ping_exclude_tRNS=MagickTrue;
10001
10002       if (LocaleNCompare(value+i,"vpag",4) == 0)
10003         mng_info->ping_exclude_vpAg=MagickTrue;
10004
10005       if (LocaleNCompare(value+i,"zccp",4) == 0)
10006         mng_info->ping_exclude_zCCP=MagickTrue;
10007
10008       if (LocaleNCompare(value+i,"ztxt",4) == 0)
10009         mng_info->ping_exclude_zTXt=MagickTrue;
10010
10011       }
10012     }
10013   }
10014
10015   for (source=0; source<1; source++)
10016   {
10017     if (source==0)
10018       {
10019        value=GetImageArtifact(image,"png:include-chunk");
10020
10021        if (value == NULL)
10022          value=GetImageArtifact(image,"png:include-chunks");
10023       }
10024     else
10025       {
10026        value=GetImageOption(image_info,"png:include-chunk");
10027
10028        if (value == NULL)
10029          value=GetImageOption(image_info,"png:include-chunks");
10030       }
10031
10032     if (value != NULL)
10033     {
10034     size_t
10035       last;
10036
10037     excluding=MagickTrue;
10038
10039     if (logging != MagickFalse)
10040       {
10041         if (source == 0)
10042            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10043               "  png:include-chunk=%s found in image artifacts.\n", value);
10044         else
10045            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10046               "  png:include-chunk=%s found in image properties.\n", value);
10047       }
10048
10049     last=strlen(value);
10050
10051     for (i=0; i<(int) last; i+=5)
10052       {
10053       if (LocaleNCompare(value+i,"all",3) == 0)
10054         {
10055           mng_info->ping_exclude_bKGD=MagickFalse;
10056           mng_info->ping_exclude_cHRM=MagickFalse;
10057           mng_info->ping_exclude_EXIF=MagickFalse;
10058           mng_info->ping_exclude_gAMA=MagickFalse;
10059           mng_info->ping_exclude_iCCP=MagickFalse;
10060           /* mng_info->ping_exclude_iTXt=MagickFalse; */
10061           mng_info->ping_exclude_oFFs=MagickFalse;
10062           mng_info->ping_exclude_pHYs=MagickFalse;
10063           mng_info->ping_exclude_sRGB=MagickFalse;
10064           mng_info->ping_exclude_tEXt=MagickFalse;
10065           mng_info->ping_exclude_tRNS=MagickFalse;
10066           mng_info->ping_exclude_vpAg=MagickFalse;
10067           mng_info->ping_exclude_zCCP=MagickFalse;
10068           mng_info->ping_exclude_zTXt=MagickFalse;
10069           i--;
10070         }
10071
10072       if (LocaleNCompare(value+i,"none",4) == 0)
10073         {
10074           mng_info->ping_exclude_bKGD=MagickTrue;
10075           mng_info->ping_exclude_cHRM=MagickTrue;
10076           mng_info->ping_exclude_EXIF=MagickTrue;
10077           mng_info->ping_exclude_gAMA=MagickTrue;
10078           mng_info->ping_exclude_iCCP=MagickTrue;
10079           /* mng_info->ping_exclude_iTXt=MagickTrue; */
10080           mng_info->ping_exclude_oFFs=MagickTrue;
10081           mng_info->ping_exclude_pHYs=MagickTrue;
10082           mng_info->ping_exclude_sRGB=MagickTrue;
10083           mng_info->ping_exclude_tEXt=MagickTrue;
10084           mng_info->ping_exclude_tRNS=MagickTrue;
10085           mng_info->ping_exclude_vpAg=MagickTrue;
10086           mng_info->ping_exclude_zCCP=MagickTrue;
10087           mng_info->ping_exclude_zTXt=MagickTrue;
10088         }
10089
10090       if (LocaleNCompare(value+i,"bkgd",4) == 0)
10091         mng_info->ping_exclude_bKGD=MagickFalse;
10092
10093       if (LocaleNCompare(value+i,"chrm",4) == 0)
10094         mng_info->ping_exclude_cHRM=MagickFalse;
10095
10096       if (LocaleNCompare(value+i,"exif",4) == 0)
10097         mng_info->ping_exclude_EXIF=MagickFalse;
10098
10099       if (LocaleNCompare(value+i,"gama",4) == 0)
10100         mng_info->ping_exclude_gAMA=MagickFalse;
10101
10102       if (LocaleNCompare(value+i,"iccp",4) == 0)
10103         mng_info->ping_exclude_iCCP=MagickFalse;
10104
10105     /*
10106       if (LocaleNCompare(value+i,"itxt",4) == 0)
10107         mng_info->ping_exclude_iTXt=MagickFalse;
10108      */
10109
10110       if (LocaleNCompare(value+i,"gama",4) == 0)
10111         mng_info->ping_exclude_gAMA=MagickFalse;
10112
10113       if (LocaleNCompare(value+i,"offs",4) == 0)
10114         mng_info->ping_exclude_oFFs=MagickFalse;
10115
10116       if (LocaleNCompare(value+i,"phys",4) == 0)
10117         mng_info->ping_exclude_pHYs=MagickFalse;
10118
10119       if (LocaleNCompare(value+i,"srgb",4) == 0)
10120         mng_info->ping_exclude_sRGB=MagickFalse;
10121
10122       if (LocaleNCompare(value+i,"text",4) == 0)
10123         mng_info->ping_exclude_tEXt=MagickFalse;
10124
10125       if (LocaleNCompare(value+i,"trns",4) == 0)
10126         mng_info->ping_exclude_tRNS=MagickFalse;
10127
10128       if (LocaleNCompare(value+i,"vpag",4) == 0)
10129         mng_info->ping_exclude_vpAg=MagickFalse;
10130
10131       if (LocaleNCompare(value+i,"zccp",4) == 0)
10132         mng_info->ping_exclude_zCCP=MagickFalse;
10133
10134       if (LocaleNCompare(value+i,"ztxt",4) == 0)
10135         mng_info->ping_exclude_zTXt=MagickFalse;
10136
10137       }
10138     }
10139   }
10140
10141   if (excluding != MagickFalse && logging != MagickFalse)
10142   {
10143     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10144       "  Chunks to be excluded from the output PNG:");
10145     if (mng_info->ping_exclude_bKGD != MagickFalse)
10146       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10147           "    bKGD");
10148     if (mng_info->ping_exclude_cHRM != MagickFalse)
10149       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10150           "    cHRM");
10151     if (mng_info->ping_exclude_EXIF != MagickFalse)
10152       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10153           "    EXIF");
10154     if (mng_info->ping_exclude_gAMA != MagickFalse)
10155       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10156           "    gAMA");
10157     if (mng_info->ping_exclude_iCCP != MagickFalse)
10158       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10159           "    iCCP");
10160 /*
10161     if (mng_info->ping_exclude_iTXt != MagickFalse)
10162       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10163           "    iTXt");
10164 */
10165     if (mng_info->ping_exclude_oFFs != MagickFalse)
10166       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10167           "    oFFs");
10168     if (mng_info->ping_exclude_pHYs != MagickFalse)
10169       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10170           "    pHYs");
10171     if (mng_info->ping_exclude_sRGB != MagickFalse)
10172       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10173           "    sRGB");
10174     if (mng_info->ping_exclude_tEXt != MagickFalse)
10175       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10176           "    tEXt");
10177     if (mng_info->ping_exclude_tRNS != MagickFalse)
10178       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10179           "    tRNS");
10180     if (mng_info->ping_exclude_vpAg != MagickFalse)
10181       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10182           "    vpAg");
10183     if (mng_info->ping_exclude_zCCP != MagickFalse)
10184       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10185           "    zCCP");
10186     if (mng_info->ping_exclude_zTXt != MagickFalse)
10187       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10188           "    zTXt");
10189   }
10190
10191   mng_info->need_blob = MagickTrue;
10192
10193   status=WriteOnePNGImage(mng_info,image_info,image);
10194
10195   MngInfoFreeStruct(mng_info,&have_mng_structure);
10196
10197   if (logging != MagickFalse)
10198     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
10199
10200   return(status);
10201 }
10202
10203 #if defined(JNG_SUPPORTED)
10204
10205 /* Write one JNG image */
10206 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10207    const ImageInfo *image_info,Image *image)
10208 {
10209   Image
10210     *jpeg_image;
10211
10212   ImageInfo
10213     *jpeg_image_info;
10214
10215   MagickBooleanType
10216     logging,
10217     status;
10218
10219   size_t
10220     length;
10221
10222   unsigned char
10223     *blob,
10224     chunk[80],
10225     *p;
10226
10227   unsigned int
10228     jng_alpha_compression_method,
10229     jng_alpha_sample_depth,
10230     jng_color_type,
10231     transparent;
10232
10233   size_t
10234     jng_quality;
10235
10236   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
10237     "  Enter WriteOneJNGImage()");
10238
10239   blob=(unsigned char *) NULL;
10240   jpeg_image=(Image *) NULL;
10241   jpeg_image_info=(ImageInfo *) NULL;
10242
10243   status=MagickTrue;
10244   transparent=image_info->type==GrayscaleMatteType ||
10245      image_info->type==TrueColorMatteType;
10246   jng_color_type=10;
10247   jng_alpha_sample_depth=0;
10248   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10249   jng_alpha_compression_method=0;
10250
10251   if (image->matte != MagickFalse)
10252     {
10253       /* if any pixels are transparent */
10254       transparent=MagickTrue;
10255       if (image_info->compression==JPEGCompression)
10256         jng_alpha_compression_method=8;
10257     }
10258
10259   if (transparent)
10260     {
10261       jng_color_type=14;
10262
10263       /* Create JPEG blob, image, and image_info */
10264       if (logging != MagickFalse)
10265         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10266           "  Creating jpeg_image_info for opacity.");
10267
10268       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10269
10270       if (jpeg_image_info == (ImageInfo *) NULL)
10271         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10272
10273       if (logging != MagickFalse)
10274         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10275           "  Creating jpeg_image.");
10276
10277       jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10278
10279       if (jpeg_image == (Image *) NULL)
10280         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10281
10282       (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10283       status=SeparateImageChannel(jpeg_image,OpacityChannel);
10284       status=NegateImage(jpeg_image,MagickFalse);
10285       jpeg_image->matte=MagickFalse;
10286
10287       if (jng_quality >= 1000)
10288         jpeg_image_info->quality=jng_quality/1000;
10289
10290       else
10291         jpeg_image_info->quality=jng_quality;
10292
10293       jpeg_image_info->type=GrayscaleType;
10294       (void) SetImageType(jpeg_image,GrayscaleType);
10295       (void) AcquireUniqueFilename(jpeg_image->filename);
10296       (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10297         "%s",jpeg_image->filename);
10298     }
10299
10300   /* To do: check bit depth of PNG alpha channel */
10301
10302   /* Check if image is grayscale. */
10303   if (image_info->type != TrueColorMatteType && image_info->type !=
10304     TrueColorType && ImageIsGray(image))
10305     jng_color_type-=2;
10306
10307   if (transparent)
10308     {
10309       if (jng_alpha_compression_method==0)
10310         {
10311           const char
10312             *value;
10313
10314           /* Encode opacity as a grayscale PNG blob */
10315           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10316             &image->exception);
10317           if (logging != MagickFalse)
10318             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10319               "  Creating PNG blob.");
10320           length=0;
10321
10322           (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10323           (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10324           jpeg_image_info->interlace=NoInterlace;
10325
10326           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10327             &image->exception);
10328
10329           /* Retrieve sample depth used */
10330           value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10331           if (value != (char *) NULL)
10332             jng_alpha_sample_depth= (unsigned int) value[0];
10333         }
10334       else
10335         {
10336           /* Encode opacity as a grayscale JPEG blob */
10337
10338           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10339             &image->exception);
10340
10341           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10342           (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10343           jpeg_image_info->interlace=NoInterlace;
10344           if (logging != MagickFalse)
10345             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10346               "  Creating blob.");
10347           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10348            &image->exception);
10349           jng_alpha_sample_depth=8;
10350
10351           if (logging != MagickFalse)
10352             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10353               "  Successfully read jpeg_image into a blob, length=%.20g.",
10354               (double) length);
10355
10356         }
10357       /* Destroy JPEG image and image_info */
10358       jpeg_image=DestroyImage(jpeg_image);
10359       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10360       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10361     }
10362
10363   /* Write JHDR chunk */
10364   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
10365   PNGType(chunk,mng_JHDR);
10366   LogPNGChunk(logging,mng_JHDR,16L);
10367   PNGLong(chunk+4,(png_uint_32) image->columns);
10368   PNGLong(chunk+8,(png_uint_32) image->rows);
10369   chunk[12]=jng_color_type;
10370   chunk[13]=8;  /* sample depth */
10371   chunk[14]=8; /*jng_image_compression_method */
10372   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10373   chunk[16]=jng_alpha_sample_depth;
10374   chunk[17]=jng_alpha_compression_method;
10375   chunk[18]=0; /*jng_alpha_filter_method */
10376   chunk[19]=0; /*jng_alpha_interlace_method */
10377   (void) WriteBlob(image,20,chunk);
10378   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10379   if (logging != MagickFalse)
10380     {
10381       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10382         "    JNG width:%15lu",(unsigned long) image->columns);
10383
10384       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10385         "    JNG height:%14lu",(unsigned long) image->rows);
10386
10387       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10388         "    JNG color type:%10d",jng_color_type);
10389
10390       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10391         "    JNG sample depth:%8d",8);
10392
10393       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10394         "    JNG compression:%9d",8);
10395
10396       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10397         "    JNG interlace:%11d",0);
10398
10399       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10400         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
10401
10402       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10403         "    JNG alpha compression:%3d",jng_alpha_compression_method);
10404
10405       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10406         "    JNG alpha filter:%8d",0);
10407
10408       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10409         "    JNG alpha interlace:%5d",0);
10410     }
10411
10412   /* Write any JNG-chunk-b profiles */
10413   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
10414
10415   /*
10416      Write leading ancillary chunks
10417   */
10418
10419   if (transparent)
10420   {
10421     /*
10422       Write JNG bKGD chunk
10423     */
10424
10425     unsigned char
10426       blue,
10427       green,
10428       red;
10429
10430     ssize_t
10431       num_bytes;
10432
10433     if (jng_color_type == 8 || jng_color_type == 12)
10434       num_bytes=6L;
10435     else
10436       num_bytes=10L;
10437     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
10438     PNGType(chunk,mng_bKGD);
10439     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
10440     red=ScaleQuantumToChar(image->background_color.red);
10441     green=ScaleQuantumToChar(image->background_color.green);
10442     blue=ScaleQuantumToChar(image->background_color.blue);
10443     *(chunk+4)=0;
10444     *(chunk+5)=red;
10445     *(chunk+6)=0;
10446     *(chunk+7)=green;
10447     *(chunk+8)=0;
10448     *(chunk+9)=blue;
10449     (void) WriteBlob(image,(size_t) num_bytes,chunk);
10450     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10451   }
10452
10453   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10454     {
10455       /*
10456         Write JNG sRGB chunk
10457       */
10458       (void) WriteBlobMSBULong(image,1L);
10459       PNGType(chunk,mng_sRGB);
10460       LogPNGChunk(logging,mng_sRGB,1L);
10461
10462       if (image->rendering_intent != UndefinedIntent)
10463         chunk[4]=(unsigned char)
10464           Magick_RenderingIntent_to_PNG_RenderingIntent(
10465           (image->rendering_intent));
10466
10467       else
10468         chunk[4]=(unsigned char)
10469           Magick_RenderingIntent_to_PNG_RenderingIntent(
10470           (PerceptualIntent));
10471
10472       (void) WriteBlob(image,5,chunk);
10473       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10474     }
10475   else
10476     {
10477       if (image->gamma != 0.0)
10478         {
10479           /*
10480              Write JNG gAMA chunk
10481           */
10482           (void) WriteBlobMSBULong(image,4L);
10483           PNGType(chunk,mng_gAMA);
10484           LogPNGChunk(logging,mng_gAMA,4L);
10485           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10486           (void) WriteBlob(image,8,chunk);
10487           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10488         }
10489
10490       if ((mng_info->equal_chrms == MagickFalse) &&
10491           (image->chromaticity.red_primary.x != 0.0))
10492         {
10493           PrimaryInfo
10494             primary;
10495
10496           /*
10497              Write JNG cHRM chunk
10498           */
10499           (void) WriteBlobMSBULong(image,32L);
10500           PNGType(chunk,mng_cHRM);
10501           LogPNGChunk(logging,mng_cHRM,32L);
10502           primary=image->chromaticity.white_point;
10503           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10504           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
10505           primary=image->chromaticity.red_primary;
10506           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10507           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
10508           primary=image->chromaticity.green_primary;
10509           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10510           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
10511           primary=image->chromaticity.blue_primary;
10512           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10513           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
10514           (void) WriteBlob(image,36,chunk);
10515           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10516         }
10517     }
10518
10519   if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10520     {
10521       /*
10522          Write JNG pHYs chunk
10523       */
10524       (void) WriteBlobMSBULong(image,9L);
10525       PNGType(chunk,mng_pHYs);
10526       LogPNGChunk(logging,mng_pHYs,9L);
10527       if (image->units == PixelsPerInchResolution)
10528         {
10529           PNGLong(chunk+4,(png_uint_32)
10530             (image->x_resolution*100.0/2.54+0.5));
10531
10532           PNGLong(chunk+8,(png_uint_32)
10533             (image->y_resolution*100.0/2.54+0.5));
10534
10535           chunk[12]=1;
10536         }
10537
10538       else
10539         {
10540           if (image->units == PixelsPerCentimeterResolution)
10541             {
10542               PNGLong(chunk+4,(png_uint_32)
10543                 (image->x_resolution*100.0+0.5));
10544
10545               PNGLong(chunk+8,(png_uint_32)
10546                 (image->y_resolution*100.0+0.5));
10547
10548               chunk[12]=1;
10549             }
10550
10551           else
10552             {
10553               PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10554               PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
10555               chunk[12]=0;
10556             }
10557         }
10558       (void) WriteBlob(image,13,chunk);
10559       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10560     }
10561
10562   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10563     {
10564       /*
10565          Write JNG oFFs chunk
10566       */
10567       (void) WriteBlobMSBULong(image,9L);
10568       PNGType(chunk,mng_oFFs);
10569       LogPNGChunk(logging,mng_oFFs,9L);
10570       PNGsLong(chunk+4,(ssize_t) (image->page.x));
10571       PNGsLong(chunk+8,(ssize_t) (image->page.y));
10572       chunk[12]=0;
10573       (void) WriteBlob(image,13,chunk);
10574       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10575     }
10576   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10577     {
10578        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
10579        PNGType(chunk,mng_vpAg);
10580        LogPNGChunk(logging,mng_vpAg,9L);
10581        PNGLong(chunk+4,(png_uint_32) image->page.width);
10582        PNGLong(chunk+8,(png_uint_32) image->page.height);
10583        chunk[12]=0;   /* unit = pixels */
10584        (void) WriteBlob(image,13,chunk);
10585        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10586     }
10587
10588
10589   if (transparent)
10590     {
10591       if (jng_alpha_compression_method==0)
10592         {
10593           register ssize_t
10594             i;
10595
10596           ssize_t
10597             len;
10598
10599           /* Write IDAT chunk header */
10600           if (logging != MagickFalse)
10601             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10602               "  Write IDAT chunks from blob, length=%.20g.",(double)
10603               length);
10604
10605           /* Copy IDAT chunks */
10606           len=0;
10607           p=blob+8;
10608           for (i=8; i<(ssize_t) length; i+=len+12)
10609           {
10610             len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
10611             p+=4;
10612
10613             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
10614               {
10615                 /* Found an IDAT chunk. */
10616                 (void) WriteBlobMSBULong(image,(size_t) len);
10617                 LogPNGChunk(logging,mng_IDAT,(size_t) len);
10618                 (void) WriteBlob(image,(size_t) len+4,p);
10619                 (void) WriteBlobMSBULong(image,
10620                     crc32(0,p,(uInt) len+4));
10621               }
10622
10623             else
10624               {
10625                 if (logging != MagickFalse)
10626                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10627                     "    Skipping %c%c%c%c chunk, length=%.20g.",
10628                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
10629               }
10630             p+=(8+len);
10631           }
10632         }
10633       else
10634         {
10635           /* Write JDAA chunk header */
10636           if (logging != MagickFalse)
10637             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10638               "  Write JDAA chunk, length=%.20g.",(double) length);
10639           (void) WriteBlobMSBULong(image,(size_t) length);
10640           PNGType(chunk,mng_JDAA);
10641           LogPNGChunk(logging,mng_JDAA,length);
10642           /* Write JDAT chunk(s) data */
10643           (void) WriteBlob(image,4,chunk);
10644           (void) WriteBlob(image,length,blob);
10645           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
10646              (uInt) length));
10647         }
10648       blob=(unsigned char *) RelinquishMagickMemory(blob);
10649     }
10650
10651   /* Encode image as a JPEG blob */
10652   if (logging != MagickFalse)
10653     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10654       "  Creating jpeg_image_info.");
10655   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10656   if (jpeg_image_info == (ImageInfo *) NULL)
10657     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10658
10659   if (logging != MagickFalse)
10660     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10661       "  Creating jpeg_image.");
10662
10663   jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10664   if (jpeg_image == (Image *) NULL)
10665     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10666   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10667
10668   (void) AcquireUniqueFilename(jpeg_image->filename);
10669   (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
10670     jpeg_image->filename);
10671
10672   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10673     &image->exception);
10674
10675   if (logging != MagickFalse)
10676     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10677       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
10678       (double) jpeg_image->rows);
10679
10680   if (jng_color_type == 8 || jng_color_type == 12)
10681     jpeg_image_info->type=GrayscaleType;
10682
10683   jpeg_image_info->quality=jng_quality % 1000;
10684   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10685   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10686
10687   if (logging != MagickFalse)
10688     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10689       "  Creating blob.");
10690
10691   blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
10692
10693   if (logging != MagickFalse)
10694     {
10695       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10696         "  Successfully read jpeg_image into a blob, length=%.20g.",
10697         (double) length);
10698
10699       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10700         "  Write JDAT chunk, length=%.20g.",(double) length);
10701     }
10702
10703   /* Write JDAT chunk(s) */
10704   (void) WriteBlobMSBULong(image,(size_t) length);
10705   PNGType(chunk,mng_JDAT);
10706   LogPNGChunk(logging,mng_JDAT,length);
10707   (void) WriteBlob(image,4,chunk);
10708   (void) WriteBlob(image,length,blob);
10709   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
10710
10711   jpeg_image=DestroyImage(jpeg_image);
10712   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10713   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10714   blob=(unsigned char *) RelinquishMagickMemory(blob);
10715
10716   /* Write any JNG-chunk-e profiles */
10717   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
10718
10719   /* Write IEND chunk */
10720   (void) WriteBlobMSBULong(image,0L);
10721   PNGType(chunk,mng_IEND);
10722   LogPNGChunk(logging,mng_IEND,0);
10723   (void) WriteBlob(image,4,chunk);
10724   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
10725
10726   if (logging != MagickFalse)
10727     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10728       "  exit WriteOneJNGImage()");
10729
10730   return(status);
10731 }
10732
10733
10734 /*
10735 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10736 %                                                                             %
10737 %                                                                             %
10738 %                                                                             %
10739 %   W r i t e J N G I m a g e                                                 %
10740 %                                                                             %
10741 %                                                                             %
10742 %                                                                             %
10743 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10744 %
10745 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
10746 %
10747 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
10748 %
10749 %  The format of the WriteJNGImage method is:
10750 %
10751 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10752 %
10753 %  A description of each parameter follows:
10754 %
10755 %    o image_info: the image info.
10756 %
10757 %    o image:  The image.
10758 %
10759 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10760 */
10761 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10762 {
10763   MagickBooleanType
10764     have_mng_structure,
10765     logging,
10766     status;
10767
10768   MngInfo
10769     *mng_info;
10770
10771   /*
10772     Open image file.
10773   */
10774   assert(image_info != (const ImageInfo *) NULL);
10775   assert(image_info->signature == MagickSignature);
10776   assert(image != (Image *) NULL);
10777   assert(image->signature == MagickSignature);
10778   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10779   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
10780   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
10781   if (status == MagickFalse)
10782     return(status);
10783
10784   /*
10785     Allocate a MngInfo structure.
10786   */
10787   have_mng_structure=MagickFalse;
10788   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10789   if (mng_info == (MngInfo *) NULL)
10790     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10791   /*
10792     Initialize members of the MngInfo structure.
10793   */
10794   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10795   mng_info->image=image;
10796   have_mng_structure=MagickTrue;
10797
10798   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
10799
10800   status=WriteOneJNGImage(mng_info,image_info,image);
10801   (void) CloseBlob(image);
10802
10803   (void) CatchImageException(image);
10804   MngInfoFreeStruct(mng_info,&have_mng_structure);
10805   if (logging != MagickFalse)
10806     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
10807   return(status);
10808 }
10809 #endif
10810
10811
10812
10813 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
10814 {
10815   const char
10816     *option;
10817
10818   Image
10819     *next_image;
10820
10821   MagickBooleanType
10822     have_mng_structure,
10823     status;
10824
10825   volatile MagickBooleanType
10826     logging;
10827
10828   MngInfo
10829     *mng_info;
10830
10831   int
10832     image_count,
10833     need_iterations,
10834     need_matte;
10835
10836   volatile int
10837 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
10838     defined(PNG_MNG_FEATURES_SUPPORTED)
10839     need_local_plte,
10840 #endif
10841     all_images_are_gray,
10842     need_defi,
10843     use_global_plte;
10844
10845   register ssize_t
10846     i;
10847
10848   unsigned char
10849     chunk[800];
10850
10851   volatile unsigned int
10852     write_jng,
10853     write_mng;
10854
10855   volatile size_t
10856     scene;
10857
10858   size_t
10859     final_delay=0,
10860     initial_delay;
10861
10862 #if (PNG_LIBPNG_VER < 10200)
10863     if (image_info->verbose)
10864       printf("Your PNG library (libpng-%s) is rather old.\n",
10865          PNG_LIBPNG_VER_STRING);
10866 #endif
10867
10868   /*
10869     Open image file.
10870   */
10871   assert(image_info != (const ImageInfo *) NULL);
10872   assert(image_info->signature == MagickSignature);
10873   assert(image != (Image *) NULL);
10874   assert(image->signature == MagickSignature);
10875   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10876   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
10877   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
10878   if (status == MagickFalse)
10879     return(status);
10880
10881   /*
10882     Allocate a MngInfo structure.
10883   */
10884   have_mng_structure=MagickFalse;
10885   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10886   if (mng_info == (MngInfo *) NULL)
10887     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10888   /*
10889     Initialize members of the MngInfo structure.
10890   */
10891   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10892   mng_info->image=image;
10893   have_mng_structure=MagickTrue;
10894   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
10895
10896   /*
10897    * See if user has requested a specific PNG subformat to be used
10898    * for all of the PNGs in the MNG being written, e.g.,
10899    *
10900    *    convert *.png png8:animation.mng
10901    *
10902    * To do: check -define png:bit_depth and png:color_type as well,
10903    * or perhaps use mng:bit_depth and mng:color_type instead for
10904    * global settings.
10905    */
10906
10907   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10908   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10909   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10910
10911   write_jng=MagickFalse;
10912   if (image_info->compression == JPEGCompression)
10913     write_jng=MagickTrue;
10914
10915   mng_info->adjoin=image_info->adjoin &&
10916     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
10917
10918   if (logging != MagickFalse)
10919     {
10920       /* Log some info about the input */
10921       Image
10922         *p;
10923
10924       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10925         "  Checking input image(s)");
10926
10927       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10928         "    Image_info depth: %.20g",(double) image_info->depth);
10929
10930       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10931         "    Type: %d",image_info->type);
10932
10933       scene=0;
10934       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
10935       {
10936         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10937           "    Scene: %.20g",(double) scene++);
10938
10939         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10940           "      Image depth: %.20g",(double) p->depth);
10941
10942         if (p->matte)
10943           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10944             "      Matte: True");
10945
10946         else
10947           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10948             "      Matte: False");
10949
10950         if (p->storage_class == PseudoClass)
10951           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10952             "      Storage class: PseudoClass");
10953
10954         else
10955           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10956             "      Storage class: DirectClass");
10957
10958         if (p->colors)
10959           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10960             "      Number of colors: %.20g",(double) p->colors);
10961
10962         else
10963           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10964             "      Number of colors: unspecified");
10965
10966         if (mng_info->adjoin == MagickFalse)
10967           break;
10968       }
10969     }
10970
10971   use_global_plte=MagickFalse;
10972   all_images_are_gray=MagickFalse;
10973 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
10974   need_local_plte=MagickTrue;
10975 #endif
10976   need_defi=MagickFalse;
10977   need_matte=MagickFalse;
10978   mng_info->framing_mode=1;
10979   mng_info->old_framing_mode=1;
10980
10981   if (write_mng)
10982       if (image_info->page != (char *) NULL)
10983         {
10984           /*
10985             Determine image bounding box.
10986           */
10987           SetGeometry(image,&mng_info->page);
10988           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
10989             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
10990         }
10991   if (write_mng)
10992     {
10993       unsigned int
10994         need_geom;
10995
10996       unsigned short
10997         red,
10998         green,
10999         blue;
11000
11001       mng_info->page=image->page;
11002       need_geom=MagickTrue;
11003       if (mng_info->page.width || mng_info->page.height)
11004          need_geom=MagickFalse;
11005       /*
11006         Check all the scenes.
11007       */
11008       initial_delay=image->delay;
11009       need_iterations=MagickFalse;
11010       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11011       mng_info->equal_physs=MagickTrue,
11012       mng_info->equal_gammas=MagickTrue;
11013       mng_info->equal_srgbs=MagickTrue;
11014       mng_info->equal_backgrounds=MagickTrue;
11015       image_count=0;
11016 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11017     defined(PNG_MNG_FEATURES_SUPPORTED)
11018       all_images_are_gray=MagickTrue;
11019       mng_info->equal_palettes=MagickFalse;
11020       need_local_plte=MagickFalse;
11021 #endif
11022       for (next_image=image; next_image != (Image *) NULL; )
11023       {
11024         if (need_geom)
11025           {
11026             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11027               mng_info->page.width=next_image->columns+next_image->page.x;
11028
11029             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11030               mng_info->page.height=next_image->rows+next_image->page.y;
11031           }
11032
11033         if (next_image->page.x || next_image->page.y)
11034           need_defi=MagickTrue;
11035
11036         if (next_image->matte)
11037           need_matte=MagickTrue;
11038
11039         if ((int) next_image->dispose >= BackgroundDispose)
11040           if (next_image->matte || next_image->page.x || next_image->page.y ||
11041               ((next_image->columns < mng_info->page.width) &&
11042                (next_image->rows < mng_info->page.height)))
11043             mng_info->need_fram=MagickTrue;
11044
11045         if (next_image->iterations)
11046           need_iterations=MagickTrue;
11047
11048         final_delay=next_image->delay;
11049
11050         if (final_delay != initial_delay || final_delay > 1UL*
11051            next_image->ticks_per_second)
11052           mng_info->need_fram=1;
11053
11054 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11055     defined(PNG_MNG_FEATURES_SUPPORTED)
11056         /*
11057           check for global palette possibility.
11058         */
11059         if (image->matte != MagickFalse)
11060            need_local_plte=MagickTrue;
11061
11062         if (need_local_plte == 0)
11063           {
11064             if (ImageIsGray(image) == MagickFalse)
11065               all_images_are_gray=MagickFalse;
11066             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11067             if (use_global_plte == 0)
11068               use_global_plte=mng_info->equal_palettes;
11069             need_local_plte=!mng_info->equal_palettes;
11070           }
11071 #endif
11072         if (GetNextImageInList(next_image) != (Image *) NULL)
11073           {
11074             if (next_image->background_color.red !=
11075                 next_image->next->background_color.red ||
11076                 next_image->background_color.green !=
11077                 next_image->next->background_color.green ||
11078                 next_image->background_color.blue !=
11079                 next_image->next->background_color.blue)
11080               mng_info->equal_backgrounds=MagickFalse;
11081
11082             if (next_image->gamma != next_image->next->gamma)
11083               mng_info->equal_gammas=MagickFalse;
11084
11085             if (next_image->rendering_intent !=
11086                 next_image->next->rendering_intent)
11087               mng_info->equal_srgbs=MagickFalse;
11088
11089             if ((next_image->units != next_image->next->units) ||
11090                 (next_image->x_resolution != next_image->next->x_resolution) ||
11091                 (next_image->y_resolution != next_image->next->y_resolution))
11092               mng_info->equal_physs=MagickFalse;
11093
11094             if (mng_info->equal_chrms)
11095               {
11096                 if (next_image->chromaticity.red_primary.x !=
11097                     next_image->next->chromaticity.red_primary.x ||
11098                     next_image->chromaticity.red_primary.y !=
11099                     next_image->next->chromaticity.red_primary.y ||
11100                     next_image->chromaticity.green_primary.x !=
11101                     next_image->next->chromaticity.green_primary.x ||
11102                     next_image->chromaticity.green_primary.y !=
11103                     next_image->next->chromaticity.green_primary.y ||
11104                     next_image->chromaticity.blue_primary.x !=
11105                     next_image->next->chromaticity.blue_primary.x ||
11106                     next_image->chromaticity.blue_primary.y !=
11107                     next_image->next->chromaticity.blue_primary.y ||
11108                     next_image->chromaticity.white_point.x !=
11109                     next_image->next->chromaticity.white_point.x ||
11110                     next_image->chromaticity.white_point.y !=
11111                     next_image->next->chromaticity.white_point.y)
11112                   mng_info->equal_chrms=MagickFalse;
11113               }
11114           }
11115         image_count++;
11116         next_image=GetNextImageInList(next_image);
11117       }
11118       if (image_count < 2)
11119         {
11120           mng_info->equal_backgrounds=MagickFalse;
11121           mng_info->equal_chrms=MagickFalse;
11122           mng_info->equal_gammas=MagickFalse;
11123           mng_info->equal_srgbs=MagickFalse;
11124           mng_info->equal_physs=MagickFalse;
11125           use_global_plte=MagickFalse;
11126 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11127           need_local_plte=MagickTrue;
11128 #endif
11129           need_iterations=MagickFalse;
11130         }
11131
11132      if (mng_info->need_fram == MagickFalse)
11133        {
11134          /*
11135            Only certain framing rates 100/n are exactly representable without
11136            the FRAM chunk but we'll allow some slop in VLC files
11137          */
11138          if (final_delay == 0)
11139            {
11140              if (need_iterations != MagickFalse)
11141                {
11142                  /*
11143                    It's probably a GIF with loop; don't run it *too* fast.
11144                  */
11145                  if (mng_info->adjoin)
11146                    {
11147                      final_delay=10;
11148                      (void) ThrowMagickException(&image->exception,
11149                         GetMagickModule(),CoderWarning,
11150                        "input has zero delay between all frames; assuming",
11151                        " 10 cs `%s'","");
11152                    }
11153                }
11154              else
11155                mng_info->ticks_per_second=0;
11156            }
11157          if (final_delay != 0)
11158            mng_info->ticks_per_second=(png_uint_32)
11159               (image->ticks_per_second/final_delay);
11160          if (final_delay > 50)
11161            mng_info->ticks_per_second=2;
11162
11163          if (final_delay > 75)
11164            mng_info->ticks_per_second=1;
11165
11166          if (final_delay > 125)
11167            mng_info->need_fram=MagickTrue;
11168
11169          if (need_defi && final_delay > 2 && (final_delay != 4) &&
11170             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11171             (final_delay != 25) && (final_delay != 50) && (final_delay !=
11172                1UL*image->ticks_per_second))
11173            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
11174        }
11175
11176      if (mng_info->need_fram != MagickFalse)
11177         mng_info->ticks_per_second=1UL*image->ticks_per_second;
11178      /*
11179         If pseudocolor, we should also check to see if all the
11180         palettes are identical and write a global PLTE if they are.
11181         ../glennrp Feb 99.
11182      */
11183      /*
11184         Write the MNG version 1.0 signature and MHDR chunk.
11185      */
11186      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11187      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
11188      PNGType(chunk,mng_MHDR);
11189      LogPNGChunk(logging,mng_MHDR,28L);
11190      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11191      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
11192      PNGLong(chunk+12,mng_info->ticks_per_second);
11193      PNGLong(chunk+16,0L);  /* layer count=unknown */
11194      PNGLong(chunk+20,0L);  /* frame count=unknown */
11195      PNGLong(chunk+24,0L);  /* play time=unknown   */
11196      if (write_jng)
11197        {
11198          if (need_matte)
11199            {
11200              if (need_defi || mng_info->need_fram || use_global_plte)
11201                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
11202
11203              else
11204                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
11205            }
11206
11207          else
11208            {
11209              if (need_defi || mng_info->need_fram || use_global_plte)
11210                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
11211
11212              else
11213                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
11214            }
11215        }
11216
11217      else
11218        {
11219          if (need_matte)
11220            {
11221              if (need_defi || mng_info->need_fram || use_global_plte)
11222                PNGLong(chunk+28,11L);    /* simplicity=LC */
11223
11224              else
11225                PNGLong(chunk+28,9L);    /* simplicity=VLC */
11226            }
11227
11228          else
11229            {
11230              if (need_defi || mng_info->need_fram || use_global_plte)
11231                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
11232
11233              else
11234                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
11235            }
11236        }
11237      (void) WriteBlob(image,32,chunk);
11238      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11239      option=GetImageOption(image_info,"mng:need-cacheoff");
11240      if (option != (const char *) NULL)
11241        {
11242          size_t
11243            length;
11244
11245          /*
11246            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11247          */
11248          PNGType(chunk,mng_nEED);
11249          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
11250          (void) WriteBlobMSBULong(image,(size_t) length);
11251          LogPNGChunk(logging,mng_nEED,(size_t) length);
11252          length+=4;
11253          (void) WriteBlob(image,length,chunk);
11254          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11255        }
11256      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11257          (GetNextImageInList(image) != (Image *) NULL) &&
11258          (image->iterations != 1))
11259        {
11260          /*
11261            Write MNG TERM chunk
11262          */
11263          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
11264          PNGType(chunk,mng_TERM);
11265          LogPNGChunk(logging,mng_TERM,10L);
11266          chunk[4]=3;  /* repeat animation */
11267          chunk[5]=0;  /* show last frame when done */
11268          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11269             final_delay/MagickMax(image->ticks_per_second,1)));
11270
11271          if (image->iterations == 0)
11272            PNGLong(chunk+10,PNG_UINT_31_MAX);
11273
11274          else
11275            PNGLong(chunk+10,(png_uint_32) image->iterations);
11276
11277          if (logging != MagickFalse)
11278            {
11279              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11280                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11281               final_delay/MagickMax(image->ticks_per_second,1)));
11282
11283              if (image->iterations == 0)
11284                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11285                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
11286
11287              else
11288                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11289                  "     Image iterations: %.20g",(double) image->iterations);
11290            }
11291          (void) WriteBlob(image,14,chunk);
11292          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11293        }
11294      /*
11295        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11296      */
11297      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11298           mng_info->equal_srgbs)
11299        {
11300          /*
11301            Write MNG sRGB chunk
11302          */
11303          (void) WriteBlobMSBULong(image,1L);
11304          PNGType(chunk,mng_sRGB);
11305          LogPNGChunk(logging,mng_sRGB,1L);
11306
11307          if (image->rendering_intent != UndefinedIntent)
11308            chunk[4]=(unsigned char)
11309              Magick_RenderingIntent_to_PNG_RenderingIntent(
11310              (image->rendering_intent));
11311
11312          else
11313            chunk[4]=(unsigned char)
11314              Magick_RenderingIntent_to_PNG_RenderingIntent(
11315                (PerceptualIntent));
11316
11317          (void) WriteBlob(image,5,chunk);
11318          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11319          mng_info->have_write_global_srgb=MagickTrue;
11320        }
11321
11322      else
11323        {
11324          if (image->gamma && mng_info->equal_gammas)
11325            {
11326              /*
11327                 Write MNG gAMA chunk
11328              */
11329              (void) WriteBlobMSBULong(image,4L);
11330              PNGType(chunk,mng_gAMA);
11331              LogPNGChunk(logging,mng_gAMA,4L);
11332              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11333              (void) WriteBlob(image,8,chunk);
11334              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11335              mng_info->have_write_global_gama=MagickTrue;
11336            }
11337          if (mng_info->equal_chrms)
11338            {
11339              PrimaryInfo
11340                primary;
11341
11342              /*
11343                 Write MNG cHRM chunk
11344              */
11345              (void) WriteBlobMSBULong(image,32L);
11346              PNGType(chunk,mng_cHRM);
11347              LogPNGChunk(logging,mng_cHRM,32L);
11348              primary=image->chromaticity.white_point;
11349              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11350              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11351              primary=image->chromaticity.red_primary;
11352              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11353              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11354              primary=image->chromaticity.green_primary;
11355              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11356              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11357              primary=image->chromaticity.blue_primary;
11358              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11359              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11360              (void) WriteBlob(image,36,chunk);
11361              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11362              mng_info->have_write_global_chrm=MagickTrue;
11363            }
11364        }
11365      if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11366        {
11367          /*
11368             Write MNG pHYs chunk
11369          */
11370          (void) WriteBlobMSBULong(image,9L);
11371          PNGType(chunk,mng_pHYs);
11372          LogPNGChunk(logging,mng_pHYs,9L);
11373
11374          if (image->units == PixelsPerInchResolution)
11375            {
11376              PNGLong(chunk+4,(png_uint_32)
11377                (image->x_resolution*100.0/2.54+0.5));
11378
11379              PNGLong(chunk+8,(png_uint_32)
11380                (image->y_resolution*100.0/2.54+0.5));
11381
11382              chunk[12]=1;
11383            }
11384
11385          else
11386            {
11387              if (image->units == PixelsPerCentimeterResolution)
11388                {
11389                  PNGLong(chunk+4,(png_uint_32)
11390                    (image->x_resolution*100.0+0.5));
11391
11392                  PNGLong(chunk+8,(png_uint_32)
11393                    (image->y_resolution*100.0+0.5));
11394
11395                  chunk[12]=1;
11396                }
11397
11398              else
11399                {
11400                  PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11401                  PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11402                  chunk[12]=0;
11403                }
11404            }
11405          (void) WriteBlob(image,13,chunk);
11406          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11407        }
11408      /*
11409        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11410        or does not cover the entire frame.
11411      */
11412      if (write_mng && (image->matte || image->page.x > 0 ||
11413          image->page.y > 0 || (image->page.width &&
11414          (image->page.width+image->page.x < mng_info->page.width))
11415          || (image->page.height && (image->page.height+image->page.y
11416          < mng_info->page.height))))
11417        {
11418          (void) WriteBlobMSBULong(image,6L);
11419          PNGType(chunk,mng_BACK);
11420          LogPNGChunk(logging,mng_BACK,6L);
11421          red=ScaleQuantumToShort(image->background_color.red);
11422          green=ScaleQuantumToShort(image->background_color.green);
11423          blue=ScaleQuantumToShort(image->background_color.blue);
11424          PNGShort(chunk+4,red);
11425          PNGShort(chunk+6,green);
11426          PNGShort(chunk+8,blue);
11427          (void) WriteBlob(image,10,chunk);
11428          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11429          if (mng_info->equal_backgrounds)
11430            {
11431              (void) WriteBlobMSBULong(image,6L);
11432              PNGType(chunk,mng_bKGD);
11433              LogPNGChunk(logging,mng_bKGD,6L);
11434              (void) WriteBlob(image,10,chunk);
11435              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11436            }
11437        }
11438
11439 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11440      if ((need_local_plte == MagickFalse) &&
11441          (image->storage_class == PseudoClass) &&
11442          (all_images_are_gray == MagickFalse))
11443        {
11444          size_t
11445            data_length;
11446
11447          /*
11448            Write MNG PLTE chunk
11449          */
11450          data_length=3*image->colors;
11451          (void) WriteBlobMSBULong(image,data_length);
11452          PNGType(chunk,mng_PLTE);
11453          LogPNGChunk(logging,mng_PLTE,data_length);
11454
11455          for (i=0; i < (ssize_t) image->colors; i++)
11456          {
11457            chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11458            chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11459            chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11460          }
11461
11462          (void) WriteBlob(image,data_length+4,chunk);
11463          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11464          mng_info->have_write_global_plte=MagickTrue;
11465        }
11466 #endif
11467     }
11468   scene=0;
11469   mng_info->delay=0;
11470 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11471     defined(PNG_MNG_FEATURES_SUPPORTED)
11472   mng_info->equal_palettes=MagickFalse;
11473 #endif
11474   do
11475   {
11476     if (mng_info->adjoin)
11477     {
11478 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11479     defined(PNG_MNG_FEATURES_SUPPORTED)
11480     /*
11481       If we aren't using a global palette for the entire MNG, check to
11482       see if we can use one for two or more consecutive images.
11483     */
11484     if (need_local_plte && use_global_plte && !all_images_are_gray)
11485       {
11486         if (mng_info->IsPalette)
11487           {
11488             /*
11489               When equal_palettes is true, this image has the same palette
11490               as the previous PseudoClass image
11491             */
11492             mng_info->have_write_global_plte=mng_info->equal_palettes;
11493             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11494             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11495               {
11496                 /*
11497                   Write MNG PLTE chunk
11498                 */
11499                 size_t
11500                   data_length;
11501
11502                 data_length=3*image->colors;
11503                 (void) WriteBlobMSBULong(image,data_length);
11504                 PNGType(chunk,mng_PLTE);
11505                 LogPNGChunk(logging,mng_PLTE,data_length);
11506
11507                 for (i=0; i < (ssize_t) image->colors; i++)
11508                 {
11509                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11510                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11511                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11512                 }
11513
11514                 (void) WriteBlob(image,data_length+4,chunk);
11515                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11516                    (uInt) (data_length+4)));
11517                 mng_info->have_write_global_plte=MagickTrue;
11518               }
11519           }
11520         else
11521           mng_info->have_write_global_plte=MagickFalse;
11522       }
11523 #endif
11524     if (need_defi)
11525       {
11526         ssize_t
11527           previous_x,
11528           previous_y;
11529
11530         if (scene)
11531           {
11532             previous_x=mng_info->page.x;
11533             previous_y=mng_info->page.y;
11534           }
11535         else
11536           {
11537             previous_x=0;
11538             previous_y=0;
11539           }
11540         mng_info->page=image->page;
11541         if ((mng_info->page.x !=  previous_x) ||
11542             (mng_info->page.y != previous_y))
11543           {
11544              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
11545              PNGType(chunk,mng_DEFI);
11546              LogPNGChunk(logging,mng_DEFI,12L);
11547              chunk[4]=0; /* object 0 MSB */
11548              chunk[5]=0; /* object 0 LSB */
11549              chunk[6]=0; /* visible  */
11550              chunk[7]=0; /* abstract */
11551              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11552              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11553              (void) WriteBlob(image,16,chunk);
11554              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11555           }
11556       }
11557     }
11558
11559    mng_info->write_mng=write_mng;
11560
11561    if ((int) image->dispose >= 3)
11562      mng_info->framing_mode=3;
11563
11564    if (mng_info->need_fram && mng_info->adjoin &&
11565        ((image->delay != mng_info->delay) ||
11566         (mng_info->framing_mode != mng_info->old_framing_mode)))
11567      {
11568        if (image->delay == mng_info->delay)
11569          {
11570            /*
11571              Write a MNG FRAM chunk with the new framing mode.
11572            */
11573            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
11574            PNGType(chunk,mng_FRAM);
11575            LogPNGChunk(logging,mng_FRAM,1L);
11576            chunk[4]=(unsigned char) mng_info->framing_mode;
11577            (void) WriteBlob(image,5,chunk);
11578            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11579          }
11580        else
11581          {
11582            /*
11583              Write a MNG FRAM chunk with the delay.
11584            */
11585            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
11586            PNGType(chunk,mng_FRAM);
11587            LogPNGChunk(logging,mng_FRAM,10L);
11588            chunk[4]=(unsigned char) mng_info->framing_mode;
11589            chunk[5]=0;  /* frame name separator (no name) */
11590            chunk[6]=2;  /* flag for changing default delay */
11591            chunk[7]=0;  /* flag for changing frame timeout */
11592            chunk[8]=0;  /* flag for changing frame clipping */
11593            chunk[9]=0;  /* flag for changing frame sync_id */
11594            PNGLong(chunk+10,(png_uint_32)
11595              ((mng_info->ticks_per_second*
11596              image->delay)/MagickMax(image->ticks_per_second,1)));
11597            (void) WriteBlob(image,14,chunk);
11598            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11599            mng_info->delay=(png_uint_32) image->delay;
11600          }
11601        mng_info->old_framing_mode=mng_info->framing_mode;
11602      }
11603
11604 #if defined(JNG_SUPPORTED)
11605    if (image_info->compression == JPEGCompression)
11606      {
11607        ImageInfo
11608          *write_info;
11609
11610        if (logging != MagickFalse)
11611          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11612            "  Writing JNG object.");
11613        /* To do: specify the desired alpha compression method. */
11614        write_info=CloneImageInfo(image_info);
11615        write_info->compression=UndefinedCompression;
11616        status=WriteOneJNGImage(mng_info,write_info,image);
11617        write_info=DestroyImageInfo(write_info);
11618      }
11619    else
11620 #endif
11621      {
11622        if (logging != MagickFalse)
11623          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11624            "  Writing PNG object.");
11625
11626        mng_info->need_blob = MagickFalse;
11627
11628        /* We don't want any ancillary chunks written */
11629        mng_info->ping_exclude_bKGD=MagickTrue;
11630        mng_info->ping_exclude_cHRM=MagickTrue;
11631        mng_info->ping_exclude_EXIF=MagickTrue;
11632        mng_info->ping_exclude_gAMA=MagickTrue;
11633        mng_info->ping_exclude_cHRM=MagickTrue;
11634        mng_info->ping_exclude_iCCP=MagickTrue;
11635        /* mng_info->ping_exclude_iTXt=MagickTrue; */
11636        mng_info->ping_exclude_oFFs=MagickTrue;
11637        mng_info->ping_exclude_pHYs=MagickTrue;
11638        mng_info->ping_exclude_sRGB=MagickTrue;
11639        mng_info->ping_exclude_tEXt=MagickTrue;
11640        mng_info->ping_exclude_tRNS=MagickTrue;
11641        mng_info->ping_exclude_vpAg=MagickTrue;
11642        mng_info->ping_exclude_zCCP=MagickTrue;
11643        mng_info->ping_exclude_zTXt=MagickTrue;
11644
11645        status=WriteOnePNGImage(mng_info,image_info,image);
11646      }
11647
11648     if (status == MagickFalse)
11649       {
11650         MngInfoFreeStruct(mng_info,&have_mng_structure);
11651         (void) CloseBlob(image);
11652         return(MagickFalse);
11653       }
11654     (void) CatchImageException(image);
11655     if (GetNextImageInList(image) == (Image *) NULL)
11656       break;
11657     image=SyncNextImageInList(image);
11658     status=SetImageProgress(image,SaveImagesTag,scene++,
11659       GetImageListLength(image));
11660
11661     if (status == MagickFalse)
11662       break;
11663
11664   } while (mng_info->adjoin);
11665
11666   if (write_mng)
11667     {
11668       while (GetPreviousImageInList(image) != (Image *) NULL)
11669         image=GetPreviousImageInList(image);
11670       /*
11671         Write the MEND chunk.
11672       */
11673       (void) WriteBlobMSBULong(image,0x00000000L);
11674       PNGType(chunk,mng_MEND);
11675       LogPNGChunk(logging,mng_MEND,0L);
11676       (void) WriteBlob(image,4,chunk);
11677       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11678     }
11679   /*
11680     Relinquish resources.
11681   */
11682   (void) CloseBlob(image);
11683   MngInfoFreeStruct(mng_info,&have_mng_structure);
11684
11685   if (logging != MagickFalse)
11686     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
11687
11688   return(MagickTrue);
11689 }
11690 #else /* PNG_LIBPNG_VER > 10011 */
11691
11692 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
11693 {
11694   image=image;
11695   printf("Your PNG library is too old: You have libpng-%s\n",
11696      PNG_LIBPNG_VER_STRING);
11697
11698   ThrowBinaryException(CoderError,"PNG library is too old",
11699      image_info->filename);
11700 }
11701
11702 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11703 {
11704   return(WritePNGImage(image_info,image));
11705 }
11706 #endif /* PNG_LIBPNG_VER > 10011 */
11707 #endif