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