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