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