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