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