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