]> granicus.if.org Git - imagemagick/blob - coders/png.c
Preserve colors better using 4-4-4 palette when possible when reducing to PNG8.
[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     tried_333,
6818     tried_444;
6819
6820   QuantumInfo
6821     *quantum_info;
6822
6823   register ssize_t
6824     i,
6825     x;
6826
6827   unsigned char
6828     *ping_pixels;
6829
6830   volatile int
6831     image_colors,
6832     ping_bit_depth,
6833     ping_color_type,
6834     ping_interlace_method,
6835     ping_compression_method,
6836     ping_filter_method,
6837     ping_num_trans;
6838
6839   volatile size_t
6840     image_depth,
6841     old_bit_depth;
6842
6843   size_t
6844     quality,
6845     rowbytes,
6846     save_image_depth;
6847
6848   int
6849     j,
6850     number_colors,
6851     number_opaque,
6852     number_semitransparent,
6853     number_transparent,
6854     ping_pHYs_unit_type;
6855
6856   png_uint_32
6857     ping_pHYs_x_resolution,
6858     ping_pHYs_y_resolution;
6859
6860   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6861     "  Enter WriteOnePNGImage()");
6862
6863   image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
6864   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
6865
6866 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6867   LockSemaphoreInfo(ping_semaphore);
6868 #endif
6869
6870   /* Initialize some stuff */
6871   ping_bit_depth=0,
6872   ping_color_type=0,
6873   ping_interlace_method=0,
6874   ping_compression_method=0,
6875   ping_filter_method=0,
6876   ping_num_trans = 0;
6877
6878   ping_background.red = 0;
6879   ping_background.green = 0;
6880   ping_background.blue = 0;
6881   ping_background.gray = 0;
6882   ping_background.index = 0;
6883
6884   ping_trans_color.red=0;
6885   ping_trans_color.green=0;
6886   ping_trans_color.blue=0;
6887   ping_trans_color.gray=0;
6888
6889   ping_pHYs_unit_type = 0;
6890   ping_pHYs_x_resolution = 0;
6891   ping_pHYs_y_resolution = 0;
6892
6893   ping_have_blob=MagickFalse;
6894   ping_have_color=MagickTrue;
6895   ping_have_non_bw=MagickTrue;
6896   ping_have_PLTE=MagickFalse;
6897   ping_have_bKGD=MagickFalse;
6898   ping_have_pHYs=MagickFalse;
6899   ping_have_tRNS=MagickFalse;
6900
6901   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
6902   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6903   /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
6904   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
6905   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
6906   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
6907   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
6908   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
6909   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
6910   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
6911   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
6912   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
6913   ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
6914   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
6915   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
6916
6917   ping_need_colortype_warning = MagickFalse;
6918
6919   number_opaque = 0;
6920   number_semitransparent = 0;
6921   number_transparent = 0;
6922
6923   if (logging != MagickFalse)
6924     {
6925       if (image->storage_class == UndefinedClass)
6926           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6927           "    storage_class=UndefinedClass");
6928       if (image->storage_class == DirectClass)
6929           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6930           "    storage_class=DirectClass");
6931       if (image->storage_class == PseudoClass)
6932           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6933           "    storage_class=PseudoClass");
6934     }
6935    
6936   if (image->colorspace != RGBColorspace)
6937     (void) TransformImageColorspace(image,RGBColorspace);
6938
6939   /*
6940     Sometimes we get PseudoClass images whose RGB values don't match
6941     the colors in the colormap.  This code syncs the RGB values.
6942   */
6943   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
6944      (void) SyncImage(image);
6945
6946 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
6947   if (image->depth > 8)
6948     {
6949       if (logging != MagickFalse)
6950         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6951           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
6952
6953       image->depth=8;
6954     }
6955 #endif
6956
6957
6958 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6959   /* PNG does not handle depths greater than 16 so reduce it even
6960    * if lossy
6961    */
6962   if (image->depth > 16)
6963       image->depth=16;
6964 #endif
6965
6966 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
6967   if (image->depth == 16 && mng_info->write_png_depth != 16)
6968     if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
6969       image->depth = 8;
6970 #endif
6971
6972   /* Normally we run this just once, but in the case of writing PNG8
6973    * we reduce the transparency to binary and run again, then reduce
6974    * the colors to a simple 3-3-3 palette and run once more, and finally
6975    * to a simple 3-3-2 palette.
6976    */
6977
6978   tried_333 = MagickFalse;
6979   tried_444 = MagickFalse;
6980
6981   for (j=0; j<5; j++)
6982   {
6983     /* BUILD_PALETTE
6984      *
6985      * Sometimes we get DirectClass images that have 256 colors or fewer.
6986      * This code will build a colormap.
6987      *
6988      * Also, sometimes we get PseudoClass images with an out-of-date
6989      * colormap.  This code will replace the colormap with a new one.
6990      * Sometimes we get PseudoClass images that have more than 256 colors.
6991      * This code will delete the colormap and change the image to
6992      * DirectClass.
6993      *
6994      * If image->matte is MagickFalse, we ignore the opacity channel
6995      * even though it sometimes contains left-over non-opaque values.
6996      *
6997      * Also we gather some information (number of opaque, transparent,
6998      * and semitransparent pixels, and whether the image has any non-gray
6999      * pixels or only black-and-white pixels) that we might need later.
7000      *
7001      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7002      * we need to check for bogus non-opaque values, at least.
7003      */
7004
7005 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
7006 #  define PNGK 0 /* Shift */
7007 #  define PNGM 1 /* Scale */
7008 #elif (MAGICKCORE_QUANTUM_DEPTH == 16)
7009 #  define PNGK 8
7010 #  define PNGM 0x0101
7011 #else
7012 #  define PNGK 24
7013 #  define PNGM 0x01010101
7014 #endif
7015
7016    ExceptionInfo
7017      *exception;
7018
7019    int
7020      n;
7021
7022    PixelPacket
7023      opaque[260],
7024      semitransparent[260],
7025      transparent[260];
7026
7027    register IndexPacket
7028      *indexes;
7029
7030    register const PixelPacket
7031      *s,
7032      *q;
7033
7034    register PixelPacket
7035      *r;
7036
7037    if (logging != MagickFalse)
7038      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7039          "    Enter BUILD_PALETTE:");
7040
7041    if (logging != MagickFalse)
7042      {
7043        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7044              "      image->columns=%.20g",(double) image->columns);
7045        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7046              "      image->rows=%.20g",(double) image->rows);
7047        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7048              "      image->matte=%.20g",(double) image->matte);
7049        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7050              "      image->depth=%.20g",(double) image->depth);
7051
7052        if (image->storage_class == PseudoClass && image->colormap != NULL)
7053        {
7054          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7055              "      Original colormap:");
7056          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7057              "        i    (red,green,blue,opacity)");
7058
7059          for (i=0; i < 256; i++)
7060          {
7061                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7062                    "        %d    (%d,%d,%d,%d)",
7063                     (int) i,
7064                     (int) image->colormap[i].red,
7065                     (int) image->colormap[i].green,
7066                     (int) image->colormap[i].blue,
7067                     (int) image->colormap[i].opacity);
7068          }
7069
7070          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7071          {
7072            if (i > 255)
7073              {
7074                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7075                    "        %d    (%d,%d,%d,%d)",
7076                     (int) i,
7077                     (int) image->colormap[i].red,
7078                     (int) image->colormap[i].green,
7079                     (int) image->colormap[i].blue,
7080                     (int) image->colormap[i].opacity);
7081              }
7082          }
7083        }
7084
7085        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7086            "      image->colors=%d",(int) image->colors);
7087
7088        if (image->colors == 0)
7089        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7090            "        (zero means unknown)");
7091
7092        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7093             "      Regenerate the colormap");
7094      }
7095
7096      exception=(&image->exception);
7097
7098      image_colors=0;
7099      number_opaque = 0;
7100      number_semitransparent = 0;
7101      number_transparent = 0;
7102
7103      for (y=0; y < (ssize_t) image->rows; y++)
7104      {
7105        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7106
7107        if (q == (PixelPacket *) NULL)
7108          break;
7109
7110        for (x=0; x < (ssize_t) image->columns; x++)
7111        {
7112            if (image->matte == MagickFalse || q->opacity == OpaqueOpacity)
7113              {
7114                if (number_opaque < 259)
7115                  {
7116                    if (number_opaque == 0)
7117                      {
7118                        opaque[0]=*q;
7119                        opaque[0].opacity=OpaqueOpacity;
7120                        number_opaque=1;
7121                      }
7122
7123                    for (i=0; i< (ssize_t) number_opaque; i++)
7124                      {
7125                        if (IsColorEqual(opaque+i, (PixelPacket *) q))
7126                          break;
7127                      }
7128
7129                    if (i ==  (ssize_t) number_opaque &&
7130                        number_opaque < 259)
7131                      {
7132                        number_opaque++;
7133                        opaque[i] = *q;
7134                        opaque[i].opacity = OpaqueOpacity;
7135                      }
7136                  }
7137              }
7138            else if (q->opacity == TransparentOpacity)
7139              {
7140                if (number_transparent < 259)
7141                  {
7142                    if (number_transparent == 0)
7143                      {
7144                        transparent[0]=*q;
7145                        ping_trans_color.red=(unsigned short)(q->red);
7146                        ping_trans_color.green=(unsigned short) (q->green);
7147                        ping_trans_color.blue=(unsigned short) (q->blue);
7148                        ping_trans_color.gray=(unsigned short) (q->blue);
7149                        number_transparent = 1;
7150                      }
7151
7152                    for (i=0; i< (ssize_t) number_transparent; i++)
7153                      {
7154                        if (IsColorEqual(transparent+i, (PixelPacket *) q))
7155                          break;
7156                      }
7157
7158                    if (i ==  (ssize_t) number_transparent &&
7159                        number_transparent < 259)
7160                      {
7161                        number_transparent++;
7162                        transparent[i] = *q;
7163                      }
7164                  }
7165              }
7166            else
7167              {
7168                if (number_semitransparent < 259)
7169                  {
7170                    if (number_semitransparent == 0)
7171                      {
7172                        semitransparent[0]=*q;
7173                        number_semitransparent = 1;
7174                      }
7175
7176                    for (i=0; i< (ssize_t) number_semitransparent; i++)
7177                      {
7178                        if (IsColorEqual(semitransparent+i,
7179                           (PixelPacket *) q) &&
7180                           q->opacity == semitransparent[i].opacity)
7181                          break;
7182                      }
7183
7184                    if (i ==  (ssize_t) number_semitransparent &&
7185                        number_semitransparent < 259)
7186                      {
7187                        number_semitransparent++;
7188                        semitransparent[i] = *q;
7189                      }
7190                  }
7191              }
7192            q++;
7193         }
7194      }
7195
7196      if (ping_exclude_bKGD == MagickFalse)
7197        {
7198          /* Add the background color to the palette, if it
7199           * isn't already there.
7200           */
7201           for (i=0; i<number_opaque; i++)
7202           {
7203              if (IsColorEqual(opaque+i, &image->background_color))
7204              break;
7205           }
7206
7207           if (number_opaque < 259 && i == number_opaque)
7208             {
7209                opaque[i]=image->background_color;
7210                opaque[i].opacity = OpaqueOpacity;
7211                number_opaque++;
7212             }
7213           else if (logging != MagickFalse)
7214               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7215                   "      No room in the colormap to add background color");
7216        }
7217
7218      image_colors=number_opaque+number_transparent+number_semitransparent;
7219
7220      if (mng_info->write_png8 != MagickFalse && image_colors > 256)
7221        {
7222          /* No room for the background color; remove it. */
7223          number_opaque--;
7224          image_colors--;
7225        }
7226
7227      if (logging != MagickFalse)
7228        {
7229          if (image_colors > 256)
7230             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7231                   "      image has more than 256 colors");
7232
7233          else
7234             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7235                   "      image has %d colors",image_colors);
7236        }
7237
7238      if (mng_info->write_png_colortype != 7) /* We won't need this info */
7239        {
7240          ping_have_color=MagickFalse;
7241          ping_have_non_bw=MagickFalse;
7242
7243          if(image_colors > 256)
7244            {
7245              for (y=0; y < (ssize_t) image->rows; y++)
7246              {
7247                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7248
7249                if (q == (PixelPacket *) NULL)
7250                  break;
7251
7252                /* Worst case is black-and-white; we are looking at every
7253                 * pixel twice.
7254                 */
7255
7256                if (ping_have_color == MagickFalse)
7257                  {
7258                    s=q;
7259                    for (x=0; x < (ssize_t) image->columns; x++)
7260                    {
7261                      if (s->red != s->green || s->red != s->blue)
7262                        {
7263                           ping_have_color=MagickTrue;
7264                           ping_have_non_bw=MagickTrue;
7265                           break;
7266                        }
7267                      s++;
7268                    }
7269                  }
7270
7271                if (ping_have_non_bw == MagickFalse)
7272                  {
7273                    s=q;
7274                    for (x=0; x < (ssize_t) image->columns; x++)
7275                    {
7276                      if (s->red != 0 && s->red != QuantumRange)
7277                        {
7278                          ping_have_non_bw=MagickTrue;
7279                        }
7280                      s++;
7281                    }
7282                  }
7283              }
7284            } 
7285        } 
7286
7287      if (image_colors < 257)
7288        {
7289          PixelPacket
7290            colormap[260];
7291              
7292          /*
7293           * Initialize image colormap.
7294           */
7295
7296          if (logging != MagickFalse)
7297             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7298                   "      Sort the new colormap");
7299
7300         /* Sort palette, transparent first */;
7301
7302          n = 0;
7303
7304          for (i=0; i<number_transparent; i++)
7305             colormap[n++] = transparent[i];
7306
7307          for (i=0; i<number_semitransparent; i++)
7308             colormap[n++] = semitransparent[i];
7309
7310          for (i=0; i<number_opaque; i++)
7311             colormap[n++] = opaque[i];
7312
7313          
7314          /* image_colors < 257; search the colormap instead of the pixels
7315           * to get ping_have_color and ping_have_non_bw
7316           */
7317          for (i=0; i<n; i++)
7318          {
7319            if (ping_have_color == MagickFalse)
7320              {
7321                 if (colormap[i].red != colormap[i].green ||
7322                     colormap[i].red != colormap[i].blue)
7323                   {
7324                      ping_have_color=MagickTrue;
7325                      ping_have_non_bw=MagickTrue;
7326                      break;
7327                   }
7328               }
7329
7330            if (ping_have_non_bw == MagickFalse)
7331              {
7332                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
7333                    ping_have_non_bw=MagickTrue;
7334              }
7335           }
7336
7337         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
7338             (number_transparent == 0 && number_semitransparent == 0)) &&
7339             (((mng_info->write_png_colortype-1) ==
7340             PNG_COLOR_TYPE_PALETTE) ||
7341             (mng_info->write_png_colortype == 0)))
7342           {
7343             if (logging != MagickFalse)
7344               {
7345                 if (n !=  (ssize_t) image_colors)
7346                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7347                    "   image_colors (%d) and n (%d)  don't match",
7348                    image_colors, n);
7349
7350                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7351                    "      AcquireImageColormap");
7352               }
7353
7354             image->colors = image_colors;
7355
7356             if (AcquireImageColormap(image,image_colors) ==
7357                 MagickFalse)
7358                ThrowWriterException(ResourceLimitError,
7359                   "MemoryAllocationFailed");
7360
7361             for (i=0; i< (ssize_t) image_colors; i++)
7362                image->colormap[i] = colormap[i];
7363
7364             if (logging != MagickFalse)
7365               {
7366                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7367                       "      image->colors=%d (%d)",
7368                       (int) image->colors, image_colors);
7369  
7370                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7371                       "      Update the pixel indexes");
7372               }
7373
7374             /* Sync the pixel indices with the new colormap */
7375
7376             for (y=0; y < (ssize_t) image->rows; y++)
7377             {
7378               q=GetAuthenticPixels(image,0,y,image->columns,1,
7379                   exception);
7380
7381               if (q == (PixelPacket *) NULL)
7382                 break;
7383
7384               indexes=GetAuthenticIndexQueue(image);
7385  
7386               for (x=0; x < (ssize_t) image->columns; x++)
7387               {
7388                 for (i=0; i< (ssize_t) image_colors; i++)
7389                 {
7390                   if ((image->matte == MagickFalse ||
7391                       image->colormap[i].opacity == q->opacity) &&
7392                       (IsColorEqual(&image->colormap[i],
7393                          (PixelPacket *) q)))
7394                   {
7395                     indexes[x]=(IndexPacket) i;
7396                     break;
7397                   }
7398                 }
7399                 q++;
7400               }
7401
7402               if (SyncAuthenticPixels(image,exception) == MagickFalse)
7403                  break;
7404             }
7405           }
7406        }
7407
7408      if (logging != MagickFalse)
7409        {
7410          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7411             "      image->colors=%d", (int) image->colors);
7412
7413          if (image->colormap != NULL)
7414            {
7415              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7416                  "       i     (red,green,blue,opacity)");
7417
7418              for (i=0; i < (ssize_t) image->colors; i++)
7419              {
7420                if (i < 300 || i >= image->colors - 10)
7421                  {
7422                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7423                        "       %d     (%d,%d,%d,%d)",
7424                         (int) i,
7425                         (int) image->colormap[i].red,
7426                         (int) image->colormap[i].green,
7427                         (int) image->colormap[i].blue,
7428                         (int) image->colormap[i].opacity);
7429                  }
7430              }
7431            }
7432
7433            if (number_transparent < 257)
7434              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7435                    "      number_transparent     = %d",
7436                    number_transparent);
7437            else
7438
7439              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7440                    "      number_transparent     > 256");
7441
7442            if (number_opaque < 257)
7443              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7444                    "      number_opaque          = %d",
7445                    number_opaque);
7446
7447            else
7448              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7449                    "      number_opaque          > 256");
7450
7451            if (number_semitransparent < 257)
7452              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7453                    "      number_semitransparent = %d",
7454                    number_semitransparent);
7455
7456            else
7457              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7458                    "      number_semitransparent > 256");
7459
7460            if (ping_have_non_bw == MagickFalse)
7461               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7462                     "      All pixels and the background are black or white");
7463
7464            else if (ping_have_color == MagickFalse)
7465               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7466                     "      All pixels and the background are gray");
7467
7468            else
7469               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7470                     "      At least one pixel or the background is non-gray");
7471
7472            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7473                "    Exit BUILD_PALETTE:");
7474        }
7475
7476    if (mng_info->write_png8 == MagickFalse)
7477       break;
7478
7479    /* Make any reductions necessary for the PNG8 format */
7480     if (image_colors <= 256 &&
7481         image_colors != 0 && image->colormap != NULL &&
7482         number_semitransparent == 0 &&
7483         number_transparent <= 1)
7484       break;
7485
7486     /* PNG8 can't have semitransparent colors so we threshold the
7487      * opacity to 0 or OpaqueOpacity
7488      */
7489     if (number_semitransparent != 0)
7490       {
7491         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7492             "    Thresholding the alpha channel to binary");
7493
7494         for (y=0; y < (ssize_t) image->rows; y++)
7495         {
7496           r=GetAuthenticPixels(image,0,y,image->columns,1,
7497               exception);
7498
7499           if (r == (PixelPacket *) NULL)
7500             break;
7501
7502           for (x=0; x < (ssize_t) image->columns; x++)
7503           {
7504               r->opacity = r->opacity > TransparentOpacity/2 ?
7505                    TransparentOpacity : OpaqueOpacity;
7506               r++;
7507           }
7508   
7509           if (SyncAuthenticPixels(image,exception) == MagickFalse)
7510              break;
7511
7512           if (image_colors != 0 && image_colors <= 256 &&
7513              image->colormap != NULL)
7514             for (i=0; i<image_colors; i++)
7515                 image->colormap[i].opacity =
7516                     image->colormap[i].opacity > TransparentOpacity/2 ?
7517                     TransparentOpacity : OpaqueOpacity;
7518         }
7519       continue;
7520     }
7521
7522     /* PNG8 can't have more than 256 colors so we quantize the pixels and
7523      * background color to the 4-4-4, 3-3-3 or 3-3-2 palette.  If the image is
7524      * mostly gray, the 4-4-4 palette should end up with 256 colors or less.
7525      */
7526     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
7527       {
7528         if (logging != MagickFalse)
7529            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7530                "    Quantizing the background color to 4-4-4");
7531
7532         tried_444 = MagickTrue;
7533
7534         image->background_color.red=
7535             ((((((size_t)
7536             image->background_color.red) >> PNGK) & 0xf0)     )  |
7537             (((((size_t)
7538             image->background_color.red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7539         image->background_color.green=
7540             ((((((size_t)
7541             image->background_color.green) >> PNGK) & 0xf0)     )  |
7542             (((((size_t)
7543             image->background_color.green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7544         image->background_color.blue=
7545             ((((((size_t)
7546             image->background_color.blue) >> PNGK) & 0xf0)     )  |
7547             (((((size_t)
7548             image->background_color.blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7549
7550         if (logging != MagickFalse)
7551           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7552               "    Quantizing the pixel colors to 4-4-4");
7553
7554         if (image->colormap == NULL)
7555         {
7556           for (y=0; y < (ssize_t) image->rows; y++)
7557           {
7558             r=GetAuthenticPixels(image,0,y,image->columns,1,
7559                 exception);
7560
7561             if (r == (PixelPacket *) NULL)
7562               break;
7563
7564             for (x=0; x < (ssize_t) image->columns; x++)
7565             {
7566               if (r->opacity == TransparentOpacity)
7567                 {
7568                   r->red = image->background_color.red;
7569                   r->green = image->background_color.green;
7570                   r->blue = image->background_color.blue;
7571                 }
7572               else
7573                 {
7574                   r->red=
7575                        ((((((size_t) r->red) >> PNGK) & 0xf0)    )  |
7576                        (((((size_t) r->red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7577                   r->green=
7578                        ((((((size_t) r->green) >> PNGK) & 0xf0)    )  |
7579                        (((((size_t) r->green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7580                   r->blue=
7581                        ((((((size_t) r->blue) >> PNGK) & 0xf0)    )  |
7582                        (((((size_t) r->blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7583                 }
7584               r++;
7585             }
7586     
7587             if (SyncAuthenticPixels(image,exception) == MagickFalse)
7588                break;
7589           }
7590         }
7591
7592         else /* Should not reach this; colormap already exists and
7593                 must be <= 256 */
7594         {
7595           if (logging != MagickFalse)
7596               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7597               "    Quantizing the colormap to 4-4-4");
7598           for (i=0; i<image_colors; i++)
7599           {
7600               image->colormap[i].red=
7601                   ((((((size_t)
7602                   image->colormap[i].red) >> PNGK) & 0xf0)     )  |
7603                   (((((size_t)
7604                   image->colormap[i].red) >> PNGK) & 0xf0) >> 4)) * PNGM;
7605               image->colormap[i].green=
7606                   ((((((size_t)
7607                   image->colormap[i].green) >> PNGK) & 0xf0)     )  |
7608                   (((((size_t)
7609                   image->colormap[i].green) >> PNGK) & 0xf0) >> 4)) * PNGM;
7610               image->colormap[i].blue=
7611                   ((((((size_t)
7612                   image->colormap[i].blue) >> PNGK) & 0xf0)     )  |
7613                   (((((size_t)
7614                   image->colormap[i].blue) >> PNGK) & 0xf0) >> 4)) * PNGM;
7615           }
7616         }
7617         continue;
7618       }
7619
7620     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
7621       {
7622         if (logging != MagickFalse)
7623            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7624                "    Quantizing the background color to 3-3-3");
7625
7626         tried_333 = MagickTrue;
7627
7628         image->background_color.red=
7629             ((((((size_t)
7630             image->background_color.red) >> PNGK) & 0xe0)     )  |
7631             (((((size_t)
7632             image->background_color.red) >> PNGK) & 0xe0) >> 3)  |
7633             (((((size_t)
7634             image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7635         image->background_color.green=
7636             ((((((size_t)
7637             image->background_color.green) >> PNGK) & 0xe0)     )  |
7638             (((((size_t)
7639             image->background_color.green) >> PNGK) & 0xe0) >> 3)  |
7640             (((((size_t)
7641             image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7642         image->background_color.blue=
7643             ((((((size_t)
7644             image->background_color.blue) >> PNGK) & 0xe0)     )  |
7645             (((((size_t)
7646             image->background_color.blue) >> PNGK) & 0xe0) >> 3)  |
7647             (((((size_t)
7648             image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7649
7650         if (logging != MagickFalse)
7651           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7652               "    Quantizing the pixel colors to 3-3-3");
7653
7654         if (image->colormap == NULL)
7655         {
7656           for (y=0; y < (ssize_t) image->rows; y++)
7657           {
7658             r=GetAuthenticPixels(image,0,y,image->columns,1,
7659                 exception);
7660
7661             if (r == (PixelPacket *) NULL)
7662               break;
7663
7664             for (x=0; x < (ssize_t) image->columns; x++)
7665             {
7666               if (r->opacity == TransparentOpacity)
7667                 {
7668                   r->red = image->background_color.red;
7669                   r->green = image->background_color.green;
7670                   r->blue = image->background_color.blue;
7671                 }
7672               else
7673                 {
7674                   r->red=
7675                        ((((((size_t) r->red) >> PNGK) & 0xe0)    )  |
7676                        (((((size_t) r->red) >> PNGK) & 0xe0) >> 3)  |
7677                        (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7678                   r->green=
7679                        ((((((size_t) r->green) >> PNGK) & 0xe0)    )  |
7680                        (((((size_t) r->green) >> PNGK) & 0xe0) >> 3)  |
7681                        (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7682                   r->blue=
7683                        ((((((size_t) r->blue) >> PNGK) & 0xe0)    )  |
7684                        (((((size_t) r->blue) >> PNGK) & 0xe0) >> 3)  |
7685                        (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7686                 }
7687               r++;
7688             }
7689     
7690             if (SyncAuthenticPixels(image,exception) == MagickFalse)
7691                break;
7692           }
7693         }
7694
7695         else /* Should not reach this; colormap already exists and
7696                 must be <= 256 */
7697         {
7698           if (logging != MagickFalse)
7699               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7700               "    Quantizing the colormap to 3-3-3");
7701           for (i=0; i<image_colors; i++)
7702           {
7703               image->colormap[i].red=
7704                   ((((((size_t)
7705                   image->colormap[i].red) >> PNGK) & 0xe0)     )  |
7706                   (((((size_t)
7707                   image->colormap[i].red) >> PNGK) & 0xe0) >> 3)  |
7708                   (((((size_t)
7709                   image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7710               image->colormap[i].green=
7711                   ((((((size_t)
7712                   image->colormap[i].green) >> PNGK) & 0xe0)     )  |
7713                   (((((size_t)
7714                   image->colormap[i].green) >> PNGK) & 0xe0) >> 3)  |
7715                   (((((size_t)
7716                   image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7717               image->colormap[i].blue=
7718                   ((((((size_t)
7719                   image->colormap[i].blue) >> PNGK) & 0xe0)     )  |
7720                   (((((size_t)
7721                   image->colormap[i].blue) >> PNGK) & 0xe0) >> 3)  |
7722                   (((((size_t)
7723                   image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7724           }
7725         }
7726         continue;
7727       }
7728
7729     if (image_colors == 0 || image_colors > 256)
7730       {
7731         if (logging != MagickFalse)
7732            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7733                "    Quantizing the background color to 3-3-2");
7734
7735         image->background_color.red=
7736             ((((((size_t)
7737             image->background_color.red) >> PNGK) & 0xe0)     )  |
7738             (((((size_t)
7739             image->background_color.red) >> PNGK) & 0xe0) >> 3)  |
7740             (((((size_t)
7741             image->background_color.red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7742         image->background_color.green=
7743             ((((((size_t)
7744             image->background_color.green) >> PNGK) & 0xe0)     )  |
7745             (((((size_t)
7746             image->background_color.green) >> PNGK) & 0xe0) >> 3)  |
7747             (((((size_t)
7748             image->background_color.green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7749         image->background_color.blue=
7750             ((((((size_t)
7751             image->background_color.blue) >> PNGK) & 0xc0)     )  |
7752             (((((size_t)
7753             image->background_color.blue) >> PNGK) & 0xc0) >> 2)  |
7754             (((((size_t)
7755             image->background_color.blue) >> PNGK) & 0xc0) >> 4)  |
7756             (((((size_t)
7757             image->background_color.blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7758
7759         if (logging != MagickFalse)
7760           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7761               "    Quantizing the pixel colors to 3-3-2");
7762
7763         if (image->colormap == NULL)
7764         {
7765           for (y=0; y < (ssize_t) image->rows; y++)
7766           {
7767             r=GetAuthenticPixels(image,0,y,image->columns,1,
7768                 exception);
7769
7770             if (r == (PixelPacket *) NULL)
7771               break;
7772
7773             for (x=0; x < (ssize_t) image->columns; x++)
7774             {
7775               if (r->opacity == TransparentOpacity)
7776                 {
7777                   r->red = image->background_color.red;
7778                   r->green = image->background_color.green;
7779                   r->blue = image->background_color.blue;
7780                 }
7781               else
7782                 {
7783                   r->red=
7784                        ((((((size_t) r->red) >> PNGK) & 0xe0)    )  |
7785                        (((((size_t) r->red) >> PNGK) & 0xe0) >> 3)  |
7786                        (((((size_t) r->red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7787                   r->green=
7788                        ((((((size_t) r->green) >> PNGK) & 0xe0)    )  |
7789                        (((((size_t) r->green) >> PNGK) & 0xe0) >> 3)  |
7790                        (((((size_t) r->green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7791                   r->blue=
7792                        ((((((size_t) r->blue) >> PNGK) & 0xc0)    )  |
7793                        (((((size_t) r->blue) >> PNGK) & 0xc0) >> 2)  |
7794                        (((((size_t) r->blue) >> PNGK) & 0xc0) >> 4)  |
7795                        (((((size_t) r->blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7796                 }
7797               r++;
7798             }
7799     
7800             if (SyncAuthenticPixels(image,exception) == MagickFalse)
7801                break;
7802           }
7803         }
7804
7805         else /* Should not reach this; colormap already exists and
7806                 must be <= 256 */
7807         {
7808           if (logging != MagickFalse)
7809               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7810               "    Quantizing the colormap to 3-3-2");
7811           for (i=0; i<image_colors; i++)
7812           {
7813               image->colormap[i].red=
7814                   ((((((size_t)
7815                   image->colormap[i].red) >> PNGK) & 0xe0)     )  |
7816                   (((((size_t)
7817                   image->colormap[i].red) >> PNGK) & 0xe0) >> 3)  |
7818                   (((((size_t)
7819                   image->colormap[i].red) >> PNGK) & 0xc0) >> 6)) * PNGM;
7820               image->colormap[i].green=
7821                   ((((((size_t)
7822                   image->colormap[i].green) >> PNGK) & 0xe0)     )  |
7823                   (((((size_t)
7824                   image->colormap[i].green) >> PNGK) & 0xe0) >> 3)  |
7825                   (((((size_t)
7826                   image->colormap[i].green) >> PNGK) & 0xc0) >> 6)) * PNGM;
7827               image->colormap[i].blue=
7828                   ((((((size_t)
7829                   image->colormap[i].blue) >> PNGK) & 0xc0)     )  |
7830                   (((((size_t)
7831                   image->colormap[i].blue) >> PNGK) & 0xc0) >> 2)  |
7832                   (((((size_t)
7833                   image->colormap[i].blue) >> PNGK) & 0xc0) >> 4)  |
7834                   (((((size_t)
7835                   image->colormap[i].blue) >> PNGK) & 0xc0) >> 6)) * PNGM;
7836           }
7837       }
7838       continue;
7839     }
7840     break;
7841   }
7842   /* END OF BUILD_PALETTE */
7843
7844   /* If we are excluding the tRNS chunk and there is transparency,
7845    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
7846    * PNG.
7847    */
7848   if (mng_info->ping_exclude_tRNS != MagickFalse &&
7849      (number_transparent != 0 || number_semitransparent != 0))
7850     {
7851       int colortype=mng_info->write_png_colortype;
7852
7853       if (ping_have_color == MagickFalse)
7854         mng_info->write_png_colortype = 5;
7855
7856       else
7857         mng_info->write_png_colortype = 7;
7858
7859       if (colortype != 0 &&
7860          mng_info->write_png_colortype != (ssize_t) colortype)
7861         ping_need_colortype_warning=MagickTrue;
7862
7863     }
7864
7865   /* See if cheap transparency is possible.  It is only possible
7866    * when there is a single transparent color, no semitransparent
7867    * color, and no opaque color that has the same RGB components
7868    * as the transparent color.  We only need this information if
7869    * we are writing a PNG with colortype 0 or 2, and we have not
7870    * excluded the tRNS chunk.
7871    */
7872   if (number_transparent == 1 &&
7873       mng_info->write_png_colortype < 4)
7874     {
7875        ping_have_cheap_transparency = MagickTrue;
7876
7877        if (number_semitransparent != 0)
7878          ping_have_cheap_transparency = MagickFalse;
7879
7880        else if (image_colors == 0 || image_colors > 256 ||
7881            image->colormap == NULL)
7882          {
7883            ExceptionInfo
7884              *exception;
7885
7886            register const PixelPacket
7887              *q;
7888
7889            exception=(&image->exception);
7890
7891            for (y=0; y < (ssize_t) image->rows; y++)
7892            {
7893              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
7894
7895              if (q == (PixelPacket *) NULL)
7896                break;
7897
7898              for (x=0; x < (ssize_t) image->columns; x++)
7899              {
7900                  if (q->opacity != TransparentOpacity &&
7901                      (unsigned short) q->red == ping_trans_color.red &&
7902                      (unsigned short) q->green == ping_trans_color.green &&
7903                      (unsigned short) q->blue == ping_trans_color.blue)
7904                    {
7905                      ping_have_cheap_transparency = MagickFalse;
7906                      break;
7907                    }
7908
7909                  q++;
7910              }
7911     
7912              if (ping_have_cheap_transparency == MagickFalse)
7913                 break;
7914            }
7915          }
7916        else
7917          {
7918             /* Assuming that image->colormap[0] is the one transparent color
7919              * and that all others are opaque.
7920              */
7921             if (image_colors > 1)
7922               for (i=1; i<image_colors; i++)
7923                 if (image->colormap[i].red == image->colormap[0].red &&
7924                     image->colormap[i].green == image->colormap[0].green &&
7925                     image->colormap[i].blue == image->colormap[0].blue)
7926                   {
7927                      ping_have_cheap_transparency = MagickFalse;
7928                      break;
7929                   }
7930          }
7931        
7932        if (logging != MagickFalse)
7933          {
7934            if (ping_have_cheap_transparency == MagickFalse)
7935              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7936                  "   Cheap transparency is not possible.");
7937
7938            else
7939              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7940                  "   Cheap transparency is possible.");
7941          }
7942      }
7943   else
7944     ping_have_cheap_transparency = MagickFalse;
7945
7946   image_depth=image->depth;
7947
7948   quantum_info = (QuantumInfo *) NULL;
7949   number_colors=0;
7950   image_colors=(int) image->colors;
7951   image_matte=image->matte;
7952
7953   mng_info->IsPalette=image->storage_class == PseudoClass &&
7954     image_colors <= 256 && image->colormap != NULL;
7955
7956   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
7957      (image->colors == 0 || image->colormap == NULL))
7958     {
7959       image_info=DestroyImageInfo(image_info);
7960       image=DestroyImage(image);
7961       (void) ThrowMagickException(&IMimage->exception,
7962           GetMagickModule(),CoderError,
7963           "Cannot write PNG8 or color-type 3; colormap is NULL",
7964           "`%s'",IMimage->filename);
7965 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7966       UnlockSemaphoreInfo(ping_semaphore);
7967 #endif
7968       return(MagickFalse);
7969     }
7970
7971   /*
7972     Allocate the PNG structures
7973   */
7974 #ifdef PNG_USER_MEM_SUPPORTED
7975   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
7976     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
7977     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
7978
7979 #else
7980   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
7981     MagickPNGErrorHandler,MagickPNGWarningHandler);
7982
7983 #endif
7984   if (ping == (png_struct *) NULL)
7985     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7986
7987   ping_info=png_create_info_struct(ping);
7988
7989   if (ping_info == (png_info *) NULL)
7990     {
7991       png_destroy_write_struct(&ping,(png_info **) NULL);
7992       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7993     }
7994
7995   png_set_write_fn(ping,image,png_put_data,png_flush_data);
7996   ping_pixels=(unsigned char *) NULL;
7997
7998   if (setjmp(png_jmpbuf(ping)))
7999     {
8000       /*
8001         PNG write failed.
8002       */
8003 #ifdef PNG_DEBUG
8004      if (image_info->verbose)
8005         (void) printf("PNG write has failed.\n");
8006 #endif
8007       png_destroy_write_struct(&ping,&ping_info);
8008 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8009       UnlockSemaphoreInfo(ping_semaphore);
8010 #endif
8011       if (ping_have_blob != MagickFalse)
8012           (void) CloseBlob(image);
8013       image_info=DestroyImageInfo(image_info);
8014       image=DestroyImage(image);
8015       return(MagickFalse);
8016     }
8017   /*
8018     Prepare PNG for writing.
8019   */
8020 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8021   if (mng_info->write_mng)
8022      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8023
8024 #else
8025 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8026   if (mng_info->write_mng)
8027      png_permit_empty_plte(ping,MagickTrue);
8028
8029 # endif
8030 #endif
8031
8032   x=0;
8033
8034   ping_width=(png_uint_32) image->columns;
8035   ping_height=(png_uint_32) image->rows;
8036
8037   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8038      image_depth=8;
8039
8040   if (mng_info->write_png_depth != 0)
8041      image_depth=mng_info->write_png_depth;
8042
8043   /* Adjust requested depth to next higher valid depth if necessary */
8044   if (image_depth > 8)
8045      image_depth=16;
8046
8047   if ((image_depth > 4) && (image_depth < 8))
8048      image_depth=8;
8049
8050   if (image_depth == 3)
8051      image_depth=4;
8052
8053   if (logging != MagickFalse)
8054     {
8055      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8056         "    width=%.20g",(double) ping_width);
8057      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8058         "    height=%.20g",(double) ping_height);
8059      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8060         "    image_matte=%.20g",(double) image->matte);
8061      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8062         "    image->depth=%.20g",(double) image->depth);
8063      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8064         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
8065     }
8066
8067   save_image_depth=image_depth;
8068   ping_bit_depth=(png_byte) save_image_depth;
8069
8070
8071 #if defined(PNG_pHYs_SUPPORTED)
8072   if (ping_exclude_pHYs == MagickFalse)
8073   {
8074   if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8075       (!mng_info->write_mng || !mng_info->equal_physs))
8076     {
8077       if (logging != MagickFalse)
8078         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8079             "    Setting up pHYs chunk");
8080
8081       if (image->units == PixelsPerInchResolution)
8082         {
8083           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8084           ping_pHYs_x_resolution=
8085              (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8086           ping_pHYs_y_resolution=
8087              (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8088         }
8089
8090       else if (image->units == PixelsPerCentimeterResolution)
8091         {
8092           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8093           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8094           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
8095         }
8096
8097       else
8098         {
8099           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8100           ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8101           ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
8102         }
8103
8104       if (logging != MagickFalse)
8105         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8106           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8107           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8108           (int) ping_pHYs_unit_type);
8109        ping_have_pHYs = MagickTrue;
8110     }
8111   }
8112 #endif
8113
8114   if (ping_exclude_bKGD == MagickFalse)
8115   {
8116   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
8117     {
8118        unsigned int
8119          mask;
8120
8121        mask=0xffff;
8122        if (ping_bit_depth == 8)
8123           mask=0x00ff;
8124
8125        if (ping_bit_depth == 4)
8126           mask=0x000f;
8127
8128        if (ping_bit_depth == 2)
8129           mask=0x0003;
8130
8131        if (ping_bit_depth == 1)
8132           mask=0x0001;
8133
8134        ping_background.red=(png_uint_16)
8135          (ScaleQuantumToShort(image->background_color.red) & mask);
8136
8137        ping_background.green=(png_uint_16)
8138          (ScaleQuantumToShort(image->background_color.green) & mask);
8139
8140        ping_background.blue=(png_uint_16)
8141          (ScaleQuantumToShort(image->background_color.blue) & mask);
8142     }
8143
8144   if (logging != MagickFalse)
8145     {
8146       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8147           "    Setting up bKGD chunk (1)");
8148
8149       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8150           "    ping_bit_depth=%d",ping_bit_depth);
8151     }
8152
8153   ping_have_bKGD = MagickTrue;
8154   }
8155
8156   /*
8157     Select the color type.
8158   */
8159   matte=image_matte;
8160   old_bit_depth=0;
8161
8162   if (mng_info->IsPalette && mng_info->write_png8)
8163     {
8164
8165       /* To do: make this a function cause it's used twice, except
8166          for reducing the sample depth from 8. */
8167
8168       number_colors=image_colors;
8169
8170       ping_have_tRNS=MagickFalse;
8171
8172       /*
8173         Set image palette.
8174       */
8175       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8176
8177       if (logging != MagickFalse)
8178         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8179             "  Setting up PLTE chunk with %d colors (%d)",
8180             number_colors, image_colors);
8181
8182       for (i=0; i < (ssize_t) number_colors; i++)
8183       {
8184         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8185         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8186         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8187         if (logging != MagickFalse)
8188           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8189 #if MAGICKCORE_QUANTUM_DEPTH == 8
8190             "    %3ld (%3d,%3d,%3d)",
8191 #else
8192             "    %5ld (%5d,%5d,%5d)",
8193 #endif
8194             (long) i,palette[i].red,palette[i].green,palette[i].blue);
8195
8196       }
8197
8198       ping_have_PLTE=MagickTrue;
8199       image_depth=ping_bit_depth;
8200       ping_num_trans=0;
8201
8202       if (matte != MagickFalse)
8203       {
8204           /*
8205             Identify which colormap entry is transparent.
8206           */
8207           assert(number_colors <= 256);
8208           assert(image->colormap != NULL);
8209
8210           for (i=0; i < (ssize_t) number_transparent; i++)
8211              ping_trans_alpha[i]=0;
8212
8213
8214           ping_num_trans=(unsigned short) (number_transparent +
8215              number_semitransparent);
8216
8217           if (ping_num_trans == 0)
8218              ping_have_tRNS=MagickFalse;
8219
8220           else
8221              ping_have_tRNS=MagickTrue;
8222       }
8223
8224       if (ping_exclude_bKGD == MagickFalse)
8225       {
8226        /*
8227         * Identify which colormap entry is the background color.
8228         */
8229
8230         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8231           if (IsPNGColorEqual(ping_background,image->colormap[i]))
8232             break;
8233
8234         ping_background.index=(png_byte) i;
8235       }
8236     } /* end of write_png8 */
8237
8238   else if (mng_info->write_png24)
8239     {
8240       image_matte=MagickFalse;
8241       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8242     }
8243
8244   else if (mng_info->write_png32)
8245     {
8246       image_matte=MagickTrue;
8247       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8248     }
8249
8250   else /* mng_info->write_pngNN not specified */
8251     {
8252       image_depth=ping_bit_depth;
8253
8254       if (mng_info->write_png_colortype != 0)
8255         {
8256           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
8257
8258           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8259               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8260             image_matte=MagickTrue;
8261
8262           else
8263             image_matte=MagickFalse;
8264
8265           if (logging != MagickFalse)
8266              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8267              "   PNG colortype %d was specified:",(int) ping_color_type);
8268         }
8269
8270       else /* write_png_colortype not specified */
8271         {
8272           if (logging != MagickFalse)
8273              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8274              "  Selecting PNG colortype:");
8275
8276           ping_color_type=(png_byte) ((matte != MagickFalse)?
8277             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
8278
8279           if (image_info->type == TrueColorType)
8280             {
8281               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8282               image_matte=MagickFalse;
8283             }
8284
8285           if (image_info->type == TrueColorMatteType)
8286             {
8287               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
8288               image_matte=MagickTrue;
8289             }
8290
8291           if (image_info->type == PaletteType ||
8292               image_info->type == PaletteMatteType)
8293             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8294
8295           if (mng_info->write_png_colortype == 0 &&
8296              (image_info->type == UndefinedType ||
8297              image_info->type == OptimizeType))
8298             {
8299               if (ping_have_color == MagickFalse)
8300                 {
8301                   if (image_matte == MagickFalse)
8302                     {
8303                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
8304                       image_matte=MagickFalse;
8305                     }
8306
8307                   else
8308                     {
8309                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
8310                       image_matte=MagickTrue;
8311                     }
8312                 }
8313               else
8314                 {
8315                   if (image_matte == MagickFalse)
8316                     {
8317                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
8318                       image_matte=MagickFalse;
8319                     }
8320
8321                   else
8322                     {
8323                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
8324                       image_matte=MagickTrue;
8325                     }
8326                  }
8327             }
8328
8329         }
8330
8331       if (logging != MagickFalse)
8332          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8333          "    Selected PNG colortype=%d",ping_color_type);
8334
8335       if (ping_bit_depth < 8)
8336         {
8337           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
8338               ping_color_type == PNG_COLOR_TYPE_RGB ||
8339               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
8340             ping_bit_depth=8;
8341         }
8342
8343       old_bit_depth=ping_bit_depth;
8344
8345       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8346         {
8347           if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
8348              ping_bit_depth=1;
8349         }
8350
8351       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
8352         {
8353            size_t one = 1;
8354            ping_bit_depth=1;
8355
8356            if (image->colors == 0)
8357            {
8358               /* DO SOMETHING */
8359               (void) ThrowMagickException(&image->exception,
8360                  GetMagickModule(),CoderError,
8361                 "image has 0 colors", "`%s'","");
8362            }
8363
8364            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
8365              ping_bit_depth <<= 1;
8366         }
8367
8368       if (logging != MagickFalse)
8369          {
8370            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8371             "    Number of colors: %.20g",(double) image_colors);
8372
8373            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8374             "    Tentative PNG bit depth: %d",ping_bit_depth);
8375          }
8376
8377       if (ping_bit_depth < (int) mng_info->write_png_depth)
8378          ping_bit_depth = mng_info->write_png_depth;
8379     }
8380
8381   image_depth=ping_bit_depth;
8382
8383   if (logging != MagickFalse)
8384     {
8385       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8386         "    Tentative PNG color type: %.20g",(double) ping_color_type);
8387
8388       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8389         "    image_info->type: %.20g",(double) image_info->type);
8390
8391       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8392         "    image_depth: %.20g",(double) image_depth);
8393
8394       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8395
8396         "    image->depth: %.20g",(double) image->depth);
8397
8398       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8399         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
8400     }
8401
8402   if (matte != MagickFalse)
8403     {
8404       if (mng_info->IsPalette)
8405         {
8406           if (mng_info->write_png_colortype == 0)
8407             {
8408               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8409
8410               if (ping_have_color != MagickFalse)
8411                  ping_color_type=PNG_COLOR_TYPE_RGBA;
8412             }
8413
8414           /*
8415            * Determine if there is any transparent color.
8416           */
8417           if (number_transparent + number_semitransparent == 0)
8418             {
8419               /*
8420                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
8421               */
8422
8423               image_matte=MagickFalse;
8424
8425               if (mng_info->write_png_colortype == 0)
8426                 ping_color_type&=0x03;
8427             }
8428
8429           else
8430             {
8431               unsigned int
8432                 mask; 
8433
8434               mask=0xffff;
8435
8436               if (ping_bit_depth == 8)
8437                  mask=0x00ff;
8438
8439               if (ping_bit_depth == 4)
8440                  mask=0x000f;
8441
8442               if (ping_bit_depth == 2)
8443                  mask=0x0003;
8444
8445               if (ping_bit_depth == 1)
8446                  mask=0x0001;
8447
8448               ping_trans_color.red=(png_uint_16)
8449                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
8450
8451               ping_trans_color.green=(png_uint_16)
8452                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
8453
8454               ping_trans_color.blue=(png_uint_16)
8455                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
8456
8457               ping_trans_color.gray=(png_uint_16)
8458                 (ScaleQuantumToShort(PixelIntensityToQuantum(
8459                    image->colormap)) & mask);
8460
8461               ping_trans_color.index=(png_byte) 0;
8462
8463               ping_have_tRNS=MagickTrue;
8464             }
8465
8466           if (ping_have_tRNS != MagickFalse)
8467             {
8468               /*
8469                * Determine if there is one and only one transparent color
8470                * and if so if it is fully transparent.
8471                */
8472               if (ping_have_cheap_transparency == MagickFalse)
8473                 ping_have_tRNS=MagickFalse;
8474             }
8475
8476           if (ping_have_tRNS != MagickFalse)
8477             {
8478               if (mng_info->write_png_colortype == 0)
8479                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
8480
8481               if (image_depth == 8)
8482                 {
8483                   ping_trans_color.red&=0xff;
8484                   ping_trans_color.green&=0xff;
8485                   ping_trans_color.blue&=0xff;
8486                   ping_trans_color.gray&=0xff;
8487                 }
8488             }
8489         }
8490       else
8491         {
8492           if (image_depth == 8)
8493             {
8494               ping_trans_color.red&=0xff;
8495               ping_trans_color.green&=0xff;
8496               ping_trans_color.blue&=0xff;
8497               ping_trans_color.gray&=0xff;
8498             }
8499         }
8500     }
8501
8502     matte=image_matte;
8503
8504     if (ping_have_tRNS != MagickFalse)
8505       image_matte=MagickFalse;
8506
8507     if ((mng_info->IsPalette) &&
8508         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
8509         ping_have_color == MagickFalse &&
8510         (image_matte == MagickFalse || image_depth >= 8))
8511       {
8512         size_t one=1;
8513
8514         if (image_matte != MagickFalse)
8515           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
8516
8517         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
8518           {
8519             ping_color_type=PNG_COLOR_TYPE_GRAY;
8520
8521             if (save_image_depth == 16 && image_depth == 8)
8522               {
8523                 if (logging != MagickFalse)
8524                   {
8525                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8526                         "  Scaling ping_trans_color (0)");
8527                   }
8528                     ping_trans_color.gray*=0x0101;
8529               }
8530           }
8531
8532         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
8533           image_depth=MAGICKCORE_QUANTUM_DEPTH;
8534
8535         if (image_colors == 0 || image_colors-1 > MaxColormapSize)
8536           image_colors=(int) (one << image_depth);
8537
8538         if (image_depth > 8)
8539           ping_bit_depth=16;
8540
8541         else
8542           {
8543             ping_bit_depth=8;
8544             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8545               {
8546                 if(!mng_info->write_png_depth)
8547                   {
8548                     ping_bit_depth=1;
8549
8550                     while ((int) (one << ping_bit_depth)
8551                         < (ssize_t) image_colors)
8552                       ping_bit_depth <<= 1;
8553                   }
8554               }
8555
8556             else if (ping_color_type ==
8557                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
8558                 mng_info->IsPalette)
8559               {
8560               /* Check if grayscale is reducible */
8561
8562                 int
8563                   depth_4_ok=MagickTrue,
8564                   depth_2_ok=MagickTrue,
8565                   depth_1_ok=MagickTrue;
8566
8567                 for (i=0; i < (ssize_t) image_colors; i++)
8568                 {
8569                    unsigned char
8570                      intensity;
8571
8572                    intensity=ScaleQuantumToChar(image->colormap[i].red);
8573
8574                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8575                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8576                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8577                      depth_2_ok=depth_1_ok=MagickFalse;
8578                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
8579                      depth_1_ok=MagickFalse;
8580                 }
8581
8582                 if (depth_1_ok && mng_info->write_png_depth <= 1)
8583                   ping_bit_depth=1;
8584
8585                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
8586                   ping_bit_depth=2;
8587
8588                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
8589                   ping_bit_depth=4;
8590               }
8591           }
8592
8593           image_depth=ping_bit_depth;
8594       }
8595
8596     else
8597
8598       if (mng_info->IsPalette)
8599       {
8600         number_colors=image_colors;
8601
8602         if (image_depth <= 8)
8603           {
8604             /*
8605               Set image palette.
8606             */
8607             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8608
8609             if (mng_info->have_write_global_plte && matte == MagickFalse)
8610               {
8611                 png_set_PLTE(ping,ping_info,NULL,0);
8612
8613                 if (logging != MagickFalse)
8614                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8615                     "  Setting up empty PLTE chunk");
8616               }
8617
8618             else
8619               {
8620                 for (i=0; i < (ssize_t) number_colors; i++)
8621                 {
8622                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8623                   palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8624                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8625                 }
8626
8627                 if (logging != MagickFalse)
8628                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8629                     "  Setting up PLTE chunk with %d colors",
8630                     number_colors);
8631
8632                 ping_have_PLTE=MagickTrue;
8633               }
8634
8635             /* color_type is PNG_COLOR_TYPE_PALETTE */
8636             if (mng_info->write_png_depth == 0)
8637               {
8638                 size_t
8639                   one;
8640
8641                 ping_bit_depth=1;
8642                 one=1;
8643
8644                 while ((one << ping_bit_depth) < number_colors)
8645                   ping_bit_depth <<= 1;
8646               }
8647
8648             ping_num_trans=0;
8649
8650             if (matte != MagickFalse)
8651               {
8652                 /*
8653                  * Set up trans_colors array.
8654                  */
8655                 assert(number_colors <= 256);
8656
8657                 ping_num_trans=(unsigned short) (number_transparent +
8658                   number_semitransparent);
8659
8660                 if (ping_num_trans == 0)
8661                   ping_have_tRNS=MagickFalse;
8662
8663                 else
8664                   {
8665                     if (logging != MagickFalse)
8666                       {
8667                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8668                           "  Scaling ping_trans_color (1)");
8669                       }
8670                     ping_have_tRNS=MagickTrue;
8671
8672                     for (i=0; i < ping_num_trans; i++)
8673                     {
8674                        ping_trans_alpha[i]= (png_byte) (255-
8675                           ScaleQuantumToChar(image->colormap[i].opacity));
8676                     }
8677                   }
8678               }
8679           }
8680       }
8681
8682     else
8683       {
8684
8685         if (image_depth < 8)
8686           image_depth=8;
8687
8688         if ((save_image_depth == 16) && (image_depth == 8))
8689           {
8690             if (logging != MagickFalse)
8691               {
8692                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8693                   "    Scaling ping_trans_color from (%d,%d,%d)",
8694                   (int) ping_trans_color.red,
8695                   (int) ping_trans_color.green,
8696                   (int) ping_trans_color.blue);
8697               }
8698
8699             ping_trans_color.red*=0x0101;
8700             ping_trans_color.green*=0x0101;
8701             ping_trans_color.blue*=0x0101;
8702             ping_trans_color.gray*=0x0101;
8703
8704             if (logging != MagickFalse)
8705               {
8706                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8707                   "    to (%d,%d,%d)",
8708                   (int) ping_trans_color.red,
8709                   (int) ping_trans_color.green,
8710                   (int) ping_trans_color.blue);
8711               }
8712           }
8713       }
8714
8715     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
8716          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
8717
8718     /*
8719       Adjust background and transparency samples in sub-8-bit grayscale files.
8720     */
8721     if (ping_bit_depth < 8 && ping_color_type ==
8722         PNG_COLOR_TYPE_GRAY)
8723       {
8724          png_uint_16
8725            maxval;
8726
8727          size_t
8728            one=1;
8729
8730          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
8731
8732          if (ping_exclude_bKGD == MagickFalse)
8733          {
8734
8735          ping_background.gray=(png_uint_16)
8736            (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8737
8738          if (logging != MagickFalse)
8739            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8740              "  Setting up bKGD chunk (2)");
8741
8742          ping_have_bKGD = MagickTrue;
8743          }
8744
8745          ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8746            ping_trans_color.gray));
8747       }
8748
8749   if (ping_exclude_bKGD == MagickFalse)
8750   {
8751     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8752       {
8753         /*
8754            Identify which colormap entry is the background color.
8755         */
8756
8757         number_colors=image_colors;
8758
8759         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8760           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
8761             break;
8762
8763         ping_background.index=(png_byte) i;
8764
8765         if (logging != MagickFalse)
8766           {
8767             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8768               "  Setting up bKGD chunk with index=%d",(int) i);
8769           }
8770
8771         if (i < (ssize_t) number_colors)
8772           {
8773             ping_have_bKGD = MagickTrue;
8774
8775             if (logging != MagickFalse)
8776               {
8777                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8778                   "     background   =(%d,%d,%d)",
8779                         (int) ping_background.red,
8780                         (int) ping_background.green,
8781                         (int) ping_background.blue);
8782               }
8783           }
8784
8785         else  /* Can't happen */
8786           {
8787             if (logging != MagickFalse)
8788               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8789                   "      No room in PLTE to add bKGD color");
8790             ping_have_bKGD = MagickFalse;
8791           }
8792       }
8793   }
8794
8795   if (logging != MagickFalse)
8796     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8797       "    PNG color type: %d",ping_color_type);
8798   /*
8799     Initialize compression level and filtering.
8800   */
8801   if (logging != MagickFalse)
8802     {
8803       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8804         "  Setting up deflate compression");
8805
8806       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8807         "    Compression buffer size: 32768");
8808     }
8809
8810   png_set_compression_buffer_size(ping,32768L);
8811
8812   if (logging != MagickFalse)
8813     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8814       "    Compression mem level: 9");
8815
8816   png_set_compression_mem_level(ping, 9);
8817
8818   quality=image->quality == UndefinedCompressionQuality ? 75UL :
8819      image->quality;
8820
8821   if (quality > 9)
8822     {
8823       int
8824         level;
8825
8826       level=(int) MagickMin((ssize_t) quality/10,9);
8827
8828       if (logging != MagickFalse)
8829         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8830           "    Compression level: %d",level);
8831
8832       png_set_compression_level(ping,level);
8833     }
8834
8835   else
8836     {
8837       if (logging != MagickFalse)
8838         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8839           "    Compression strategy: Z_HUFFMAN_ONLY");
8840
8841       png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
8842     }
8843
8844   if (logging != MagickFalse)
8845     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8846       "  Setting up filtering");
8847
8848 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
8849   /* This became available in libpng-1.0.9.  Output must be a MNG. */
8850   if (mng_info->write_mng && ((quality % 10) == 7))
8851     {
8852       if (logging != MagickFalse)
8853         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8854           "    Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
8855
8856       ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
8857     }
8858
8859   else
8860     if (logging != MagickFalse)
8861       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8862         "    Filter_type: 0");
8863 #endif
8864
8865   {
8866     int
8867       base_filter;
8868
8869     if ((quality % 10) > 5)
8870       base_filter=PNG_ALL_FILTERS;
8871
8872     else
8873       if ((quality % 10) != 5)
8874         base_filter=(int) quality % 10;
8875
8876       else
8877         if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
8878             ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
8879             (quality < 50))
8880           base_filter=PNG_NO_FILTERS;
8881
8882         else
8883           base_filter=PNG_ALL_FILTERS;
8884
8885     if (logging != MagickFalse)
8886       {
8887         if (base_filter == PNG_ALL_FILTERS)
8888           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8889             "    Base filter method: ADAPTIVE");
8890         else
8891           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8892             "    Base filter method: NONE");
8893       }
8894
8895     png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
8896   }
8897
8898   if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
8899      (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
8900     {
8901       ResetImageProfileIterator(image);
8902       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8903       {
8904         profile=GetImageProfile(image,name);
8905
8906         if (profile != (StringInfo *) NULL)
8907           {
8908 #ifdef PNG_WRITE_iCCP_SUPPORTED
8909             if ((LocaleCompare(name,"ICC") == 0) ||
8910                 (LocaleCompare(name,"ICM") == 0))
8911              {
8912
8913                if (ping_exclude_iCCP == MagickFalse)
8914                  {
8915                        png_set_iCCP(ping,ping_info,(const png_charp) name,0,
8916 #if (PNG_LIBPNG_VER < 10500)
8917                          (png_charp) GetStringInfoDatum(profile),
8918 #else
8919                          (png_const_bytep) GetStringInfoDatum(profile),
8920 #endif
8921                          (png_uint_32) GetStringInfoLength(profile));
8922                  }
8923              }
8924
8925             else
8926 #endif
8927               if (ping_exclude_zCCP == MagickFalse)
8928                 {
8929                   Magick_png_write_raw_profile(image_info,ping,ping_info,
8930                     (unsigned char *) name,(unsigned char *) name,
8931                     GetStringInfoDatum(profile),
8932                     (png_uint_32) GetStringInfoLength(profile));
8933                 }
8934           }
8935
8936           if (logging != MagickFalse)
8937             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8938               "  Setting up text chunk with %s profile",name);
8939
8940         name=GetNextImageProfile(image);
8941       }
8942   }
8943
8944 #if defined(PNG_WRITE_sRGB_SUPPORTED)
8945   if ((mng_info->have_write_global_srgb == 0) &&
8946       ((image->rendering_intent != UndefinedIntent) ||
8947       (image->colorspace == sRGBColorspace)))
8948     {
8949       if (ping_exclude_sRGB == MagickFalse)
8950         {
8951           /*
8952             Note image rendering intent.
8953           */
8954           if (logging != MagickFalse)
8955             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8956                 "  Setting up sRGB chunk");
8957
8958           (void) png_set_sRGB(ping,ping_info,(
8959             Magick_RenderingIntent_to_PNG_RenderingIntent(
8960               image->rendering_intent)));
8961
8962           if (ping_exclude_gAMA == MagickFalse)
8963             png_set_gAMA(ping,ping_info,0.45455);
8964         }
8965     }
8966
8967   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
8968 #endif
8969     {
8970       if (ping_exclude_gAMA == MagickFalse &&
8971           (ping_exclude_sRGB == MagickFalse ||
8972           (image->gamma < .45 || image->gamma > .46)))
8973       {
8974       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
8975         {
8976           /*
8977             Note image gamma.
8978             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8979           */
8980           if (logging != MagickFalse)
8981             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8982               "  Setting up gAMA chunk");
8983
8984           png_set_gAMA(ping,ping_info,image->gamma);
8985         }
8986       }
8987
8988       if (ping_exclude_cHRM == MagickFalse)
8989         {
8990           if ((mng_info->have_write_global_chrm == 0) &&
8991               (image->chromaticity.red_primary.x != 0.0))
8992             {
8993               /*
8994                 Note image chromaticity.
8995                 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8996               */
8997                PrimaryInfo
8998                  bp,
8999                  gp,
9000                  rp,
9001                  wp;
9002
9003                wp=image->chromaticity.white_point;
9004                rp=image->chromaticity.red_primary;
9005                gp=image->chromaticity.green_primary;
9006                bp=image->chromaticity.blue_primary;
9007
9008                if (logging != MagickFalse)
9009                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9010                    "  Setting up cHRM chunk");
9011
9012                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9013                    bp.x,bp.y);
9014            }
9015         }
9016     }
9017
9018   ping_interlace_method=image_info->interlace != NoInterlace;
9019
9020   if (mng_info->write_mng)
9021     png_set_sig_bytes(ping,8);
9022
9023   /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9024
9025   if (mng_info->write_png_colortype != 0)
9026     {
9027      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
9028        if (ping_have_color != MagickFalse)
9029          {
9030            ping_color_type = PNG_COLOR_TYPE_RGB;
9031
9032            if (ping_bit_depth < 8)
9033              ping_bit_depth=8;
9034          }
9035
9036      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
9037        if (ping_have_color != MagickFalse)
9038          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
9039     }
9040
9041   if (ping_need_colortype_warning != MagickFalse ||
9042      ((mng_info->write_png_depth &&
9043      (int) mng_info->write_png_depth != ping_bit_depth) ||
9044      (mng_info->write_png_colortype &&
9045      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
9046       mng_info->write_png_colortype != 7 &&
9047       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
9048     {
9049       if (logging != MagickFalse)
9050         {
9051           if (ping_need_colortype_warning != MagickFalse)
9052             {
9053               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9054                  "  Image has transparency but tRNS chunk was excluded");
9055             }
9056
9057           if (mng_info->write_png_depth)
9058             {
9059               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9060                   "  Defined PNG:bit-depth=%u, Computed depth=%u",
9061                   mng_info->write_png_depth,
9062                   ping_bit_depth);
9063             }
9064
9065           if (mng_info->write_png_colortype)
9066             {
9067               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9068                   "  Defined PNG:color-type=%u, Computed color type=%u",
9069                   mng_info->write_png_colortype-1,
9070                   ping_color_type);
9071             }
9072         }
9073
9074       png_warning(ping,
9075         "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9076     }
9077
9078   if (image_matte != MagickFalse && image->matte == MagickFalse)
9079     {
9080       /* Add an opaque matte channel */
9081       image->matte = MagickTrue;
9082       (void) SetImageOpacity(image,0);
9083
9084       if (logging != MagickFalse)
9085         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9086           "  Added an opaque matte channel");
9087     }
9088
9089   if (number_transparent != 0 || number_semitransparent != 0)
9090     {
9091       if (ping_color_type < 4)
9092         {
9093            ping_have_tRNS=MagickTrue;
9094            if (logging != MagickFalse)
9095              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9096                "  Setting ping_have_tRNS=MagickTrue.");
9097         }
9098     }
9099
9100   if (logging != MagickFalse)
9101     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9102       "  Writing PNG header chunks");
9103
9104   png_set_IHDR(ping,ping_info,ping_width,ping_height,
9105                ping_bit_depth,ping_color_type,
9106                ping_interlace_method,ping_compression_method,
9107                ping_filter_method);
9108
9109   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9110     {
9111       png_set_PLTE(ping,ping_info,palette,number_colors);
9112
9113       if (logging != MagickFalse)
9114         {
9115           for (i=0; i< (ssize_t) number_colors; i++)
9116           {
9117             if (i < ping_num_trans)
9118               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9119                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9120                       (int) i,
9121                       (int) palette[i].red,
9122                       (int) palette[i].green,
9123                       (int) palette[i].blue,
9124                       (int) i,
9125                       (int) ping_trans_alpha[i]);
9126              else
9127               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9128                 "     PLTE[%d] = (%d,%d,%d)",
9129                       (int) i,
9130                       (int) palette[i].red,
9131                       (int) palette[i].green,
9132                       (int) palette[i].blue);
9133            }
9134          }
9135     }
9136
9137   if (ping_exclude_bKGD == MagickFalse)
9138     {
9139       if (ping_have_bKGD != MagickFalse)
9140           png_set_bKGD(ping,ping_info,&ping_background);
9141     }
9142
9143   if (ping_exclude_pHYs == MagickFalse)
9144     {
9145       if (ping_have_pHYs != MagickFalse)
9146         {
9147           png_set_pHYs(ping,ping_info,
9148              ping_pHYs_x_resolution,
9149              ping_pHYs_y_resolution,
9150              ping_pHYs_unit_type);
9151
9152           if (logging)
9153             {
9154               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9155                    "    Setting up pHYs chunk");
9156               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9157                    "      x_resolution=%lu",
9158                    (unsigned long) ping_pHYs_x_resolution);
9159               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9160                    "      y_resolution=%lu",
9161                    (unsigned long) ping_pHYs_y_resolution);
9162               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9163                    "      unit_type=%lu",
9164                    (unsigned long) ping_pHYs_unit_type);
9165             }
9166         }
9167     }
9168
9169 #if defined(PNG_oFFs_SUPPORTED)
9170   if (ping_exclude_oFFs == MagickFalse)
9171     {
9172       if (image->page.x || image->page.y)
9173         {
9174            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9175               (png_int_32) image->page.y, 0);
9176
9177            if (logging != MagickFalse)
9178              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9179                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
9180                  (int) image->page.x, (int) image->page.y);
9181         }
9182     }
9183 #endif
9184
9185   if (mng_info->need_blob != MagickFalse)
9186   {
9187     if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9188        MagickFalse)
9189        png_error(ping,"WriteBlob Failed");
9190
9191      ping_have_blob=MagickTrue;
9192   }
9193
9194   png_write_info_before_PLTE(ping, ping_info);
9195
9196   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
9197     {
9198       if (logging != MagickFalse)
9199         {
9200           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9201               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
9202         }
9203
9204       if (ping_color_type == 3)
9205          (void) png_set_tRNS(ping, ping_info,
9206                 ping_trans_alpha,
9207                 ping_num_trans,
9208                 NULL);
9209
9210       else
9211         {
9212            (void) png_set_tRNS(ping, ping_info,
9213                   NULL,
9214                   0,
9215                   &ping_trans_color);
9216
9217            if (logging != MagickFalse)
9218              {
9219                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9220                  "     tRNS color   =(%d,%d,%d)",
9221                        (int) ping_trans_color.red,
9222                        (int) ping_trans_color.green,
9223                        (int) ping_trans_color.blue);
9224              }
9225          }
9226     }
9227
9228   /* write any png-chunk-b profiles */
9229   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
9230
9231   png_write_info(ping,ping_info);
9232
9233   /* write any PNG-chunk-m profiles */
9234   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
9235
9236   if (ping_exclude_vpAg == MagickFalse)
9237     {
9238       if ((image->page.width != 0 && image->page.width != image->columns) ||
9239           (image->page.height != 0 && image->page.height != image->rows))
9240         {
9241           unsigned char
9242             chunk[14];
9243
9244           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
9245           PNGType(chunk,mng_vpAg);
9246           LogPNGChunk(logging,mng_vpAg,9L);
9247           PNGLong(chunk+4,(png_uint_32) image->page.width);
9248           PNGLong(chunk+8,(png_uint_32) image->page.height);
9249           chunk[12]=0;   /* unit = pixels */
9250           (void) WriteBlob(image,13,chunk);
9251           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9252         }
9253     }
9254
9255 #if (PNG_LIBPNG_VER == 10206)
9256     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
9257 #define PNG_HAVE_IDAT               0x04
9258     ping->mode |= PNG_HAVE_IDAT;
9259 #undef PNG_HAVE_IDAT
9260 #endif
9261
9262   png_set_packing(ping);
9263   /*
9264     Allocate memory.
9265   */
9266   rowbytes=image->columns;
9267   if (image_depth > 8)
9268     rowbytes*=2;
9269   switch (ping_color_type)
9270     {
9271       case PNG_COLOR_TYPE_RGB:
9272         rowbytes*=3;
9273         break;
9274
9275       case PNG_COLOR_TYPE_GRAY_ALPHA:
9276         rowbytes*=2;
9277         break;
9278
9279       case PNG_COLOR_TYPE_RGBA:
9280         rowbytes*=4;
9281         break;
9282
9283       default:
9284         break;
9285     }
9286
9287   if (logging != MagickFalse)
9288     {
9289       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9290         "  Writing PNG image data");
9291
9292       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9293         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
9294     }
9295   ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
9296     sizeof(*ping_pixels));
9297
9298   if (ping_pixels == (unsigned char *) NULL)
9299     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9300
9301   /*
9302     Initialize image scanlines.
9303   */
9304   if (setjmp(png_jmpbuf(ping)))
9305     {
9306       /*
9307         PNG write failed.
9308       */
9309 #ifdef PNG_DEBUG
9310      if (image_info->verbose)
9311         (void) printf("PNG write has failed.\n");
9312 #endif
9313       png_destroy_write_struct(&ping,&ping_info);
9314       if (quantum_info != (QuantumInfo *) NULL)
9315         quantum_info=DestroyQuantumInfo(quantum_info);
9316       if (ping_pixels != (unsigned char *) NULL)
9317         ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9318 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9319       UnlockSemaphoreInfo(ping_semaphore);
9320 #endif
9321       if (ping_have_blob != MagickFalse)
9322           (void) CloseBlob(image);
9323       image_info=DestroyImageInfo(image_info);
9324       image=DestroyImage(image);
9325       return(MagickFalse);
9326     }
9327   quantum_info=AcquireQuantumInfo(image_info,image);
9328   if (quantum_info == (QuantumInfo *) NULL)
9329     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9330   quantum_info->format=UndefinedQuantumFormat;
9331   quantum_info->depth=image_depth;
9332   num_passes=png_set_interlace_handling(ping);
9333
9334   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9335        !mng_info->write_png32) &&
9336        (mng_info->IsPalette ||
9337        (image_info->type == BilevelType)) &&
9338        image_matte == MagickFalse &&
9339        ping_have_non_bw == MagickFalse)
9340     {
9341       /* Palette, Bilevel, or Opaque Monochrome */
9342       register const PixelPacket
9343         *p;
9344
9345       quantum_info->depth=8;
9346       for (pass=0; pass < num_passes; pass++)
9347       {
9348         /*
9349           Convert PseudoClass image to a PNG monochrome image.
9350         */
9351         for (y=0; y < (ssize_t) image->rows; y++)
9352         {
9353           if (logging != MagickFalse && y == 0)
9354              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9355                  "    Writing row of pixels (0)");
9356
9357           p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9358
9359           if (p == (const PixelPacket *) NULL)
9360             break;
9361
9362           if (mng_info->IsPalette)
9363             {
9364               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9365                 quantum_info,GrayQuantum,ping_pixels,&image->exception);
9366               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
9367                   mng_info->write_png_depth &&
9368                   mng_info->write_png_depth != old_bit_depth)
9369                 {
9370                   /* Undo pixel scaling */
9371                   for (i=0; i < (ssize_t) image->columns; i++)
9372                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
9373                      >> (8-old_bit_depth));
9374                 }
9375             }
9376
9377           else
9378             {
9379               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9380                 quantum_info,RedQuantum,ping_pixels,&image->exception);
9381             }
9382
9383           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
9384             for (i=0; i < (ssize_t) image->columns; i++)
9385                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
9386                       255 : 0);
9387
9388           if (logging != MagickFalse && y == 0)
9389             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9390                 "    Writing row of pixels (1)");
9391
9392           png_write_row(ping,ping_pixels);
9393         }
9394         if (image->previous == (Image *) NULL)
9395           {
9396             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9397             if (status == MagickFalse)
9398               break;
9399           }
9400       }
9401     }
9402
9403   else   /* Not Palette, Bilevel, or Opaque Monochrome */
9404     {
9405       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
9406          !mng_info->write_png32) &&
9407          (image_matte != MagickFalse ||
9408          (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
9409          (mng_info->IsPalette) && ping_have_color == MagickFalse)
9410         {
9411           register const PixelPacket
9412             *p;
9413
9414           for (pass=0; pass < num_passes; pass++)
9415           {
9416
9417           for (y=0; y < (ssize_t) image->rows; y++)
9418           {
9419             p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
9420
9421             if (p == (const PixelPacket *) NULL)
9422               break;
9423
9424             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9425               {
9426                 if (mng_info->IsPalette)
9427                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9428                     quantum_info,GrayQuantum,ping_pixels,&image->exception);
9429
9430                 else
9431                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9432                     quantum_info,RedQuantum,ping_pixels,&image->exception);
9433
9434                 if (logging != MagickFalse && y == 0)
9435                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9436                        "    Writing GRAY PNG pixels (2)");
9437               }
9438
9439             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
9440               {
9441                 if (logging != MagickFalse && y == 0)
9442                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9443                          "    Writing GRAY_ALPHA PNG pixels (2)");
9444
9445                 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9446                   quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
9447               }
9448
9449             if (logging != MagickFalse && y == 0)
9450               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9451                   "    Writing row of pixels (2)");
9452
9453             png_write_row(ping,ping_pixels);
9454           }
9455
9456           if (image->previous == (Image *) NULL)
9457             {
9458               status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9459               if (status == MagickFalse)
9460                 break;
9461             }
9462           }
9463         }
9464
9465       else
9466         {
9467           register const PixelPacket
9468             *p;
9469
9470           for (pass=0; pass < num_passes; pass++)
9471           {
9472             if ((image_depth > 8) || (mng_info->write_png24 ||
9473                 mng_info->write_png32 ||
9474                 (!mng_info->write_png8 && !mng_info->IsPalette)))
9475             {
9476               for (y=0; y < (ssize_t) image->rows; y++)
9477               {
9478                 p=GetVirtualPixels(image,0,y,image->columns,1,
9479                    &image->exception);
9480
9481                 if (p == (const PixelPacket *) NULL)
9482                   break;
9483
9484                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9485                   {
9486                     if (image->storage_class == DirectClass)
9487                       (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9488                         quantum_info,RedQuantum,ping_pixels,&image->exception);
9489
9490                     else
9491                       (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9492                         quantum_info,GrayQuantum,ping_pixels,&image->exception);
9493                   }
9494
9495                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9496                   {
9497                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9498                       quantum_info,GrayAlphaQuantum,ping_pixels,
9499                       &image->exception);
9500
9501                     if (logging != MagickFalse && y == 0)
9502                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9503                            "    Writing GRAY_ALPHA PNG pixels (3)");
9504                   }
9505
9506                 else if (image_matte != MagickFalse)
9507                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9508                     quantum_info,RGBAQuantum,ping_pixels,&image->exception);
9509
9510                 else
9511                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9512                     quantum_info,RGBQuantum,ping_pixels,&image->exception);
9513
9514                 if (logging != MagickFalse && y == 0)
9515                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9516                       "    Writing row of pixels (3)");
9517
9518                 png_write_row(ping,ping_pixels);
9519               }
9520             }
9521
9522           else
9523             /* not ((image_depth > 8) || (mng_info->write_png24 ||
9524                 mng_info->write_png32 ||
9525                 (!mng_info->write_png8 && !mng_info->IsPalette))) */
9526             {
9527               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
9528                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
9529                 {
9530                   if (logging != MagickFalse)
9531                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9532                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
9533
9534                   quantum_info->depth=8;
9535                   image_depth=8;
9536                 }
9537
9538               for (y=0; y < (ssize_t) image->rows; y++)
9539               {
9540                 if (logging != MagickFalse && y == 0)
9541                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9542                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
9543
9544                 p=GetVirtualPixels(image,0,y,image->columns,1,
9545                    &image->exception);
9546
9547                 if (p == (const PixelPacket *) NULL)
9548                   break;
9549
9550                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9551                   {
9552                     quantum_info->depth=image->depth;
9553
9554                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9555                        quantum_info,GrayQuantum,ping_pixels,&image->exception);
9556                   }
9557
9558                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9559                   {
9560                     if (logging != MagickFalse && y == 0)
9561                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9562                            "  Writing GRAY_ALPHA PNG pixels (4)");
9563
9564                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9565                          quantum_info,GrayAlphaQuantum,ping_pixels,
9566                          &image->exception);
9567                   }
9568
9569                 else
9570                   {
9571                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9572                       quantum_info,IndexQuantum,ping_pixels,&image->exception);
9573
9574                     if (logging != MagickFalse && y <= 2)
9575                     {
9576                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9577                           "  Writing row of non-gray pixels (4)");
9578
9579                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9580                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
9581                           (int)ping_pixels[0],(int)ping_pixels[1]);
9582                     }
9583                   }
9584                 png_write_row(ping,ping_pixels);
9585               }
9586             }
9587
9588             if (image->previous == (Image *) NULL)
9589               {
9590                 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9591                 if (status == MagickFalse)
9592                   break;
9593               }
9594           }
9595         }
9596     }
9597
9598   if (quantum_info != (QuantumInfo *) NULL)
9599     quantum_info=DestroyQuantumInfo(quantum_info);
9600
9601   if (logging != MagickFalse)
9602     {
9603       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9604         "  Wrote PNG image data");
9605
9606       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9607         "    Width: %.20g",(double) ping_width);
9608
9609       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9610         "    Height: %.20g",(double) ping_height);
9611
9612       if (mng_info->write_png_depth)
9613         {
9614           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9615             "    Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9616         }
9617
9618       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9619         "    PNG bit-depth written: %d",ping_bit_depth);
9620
9621       if (mng_info->write_png_colortype)
9622         {
9623           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9624             "    Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9625         }
9626
9627       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9628         "    PNG color-type written: %d",ping_color_type);
9629
9630       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631         "    PNG Interlace method: %d",ping_interlace_method);
9632     }
9633   /*
9634     Generate text chunks.
9635   */
9636   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
9637   {
9638     ResetImagePropertyIterator(image);
9639     property=GetNextImageProperty(image);
9640     while (property != (const char *) NULL)
9641     {
9642       png_textp
9643         text;
9644
9645       value=GetImageProperty(image,property);
9646       if (ping_exclude_pHYs != MagickFalse       ||
9647           LocaleCompare(property,"density") != 0 ||
9648           LocaleCompare(property,"units") != 0)
9649         {
9650         if (value != (const char *) NULL)
9651           {
9652             text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9653             text[0].key=(char *) property;
9654             text[0].text=(char *) value;
9655             text[0].text_length=strlen(value);
9656
9657             if (ping_exclude_tEXt != MagickFalse)
9658                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9659
9660             else if (ping_exclude_zTXt != MagickFalse)
9661                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9662
9663             else
9664             {
9665                text[0].compression=image_info->compression == NoCompression ||
9666                  (image_info->compression == UndefinedCompression &&
9667                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9668                  PNG_TEXT_COMPRESSION_zTXt ;
9669             }
9670
9671             if (logging != MagickFalse)
9672               {
9673                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9674                   "  Setting up text chunk");
9675
9676                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9677                   "    keyword: %s",text[0].key);
9678               }
9679
9680             png_set_text(ping,ping_info,text,1);
9681             png_free(ping,text);
9682           }
9683         }
9684       property=GetNextImageProperty(image);
9685     }
9686   }
9687
9688   /* write any PNG-chunk-e profiles */
9689   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
9690
9691   if (logging != MagickFalse)
9692     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9693       "  Writing PNG end info");
9694
9695   png_write_end(ping,ping_info);
9696
9697   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9698     {
9699       if (mng_info->page.x || mng_info->page.y ||
9700           (ping_width != mng_info->page.width) ||
9701           (ping_height != mng_info->page.height))
9702         {
9703           unsigned char
9704             chunk[32];
9705
9706           /*
9707             Write FRAM 4 with clipping boundaries followed by FRAM 1.
9708           */
9709           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
9710           PNGType(chunk,mng_FRAM);
9711           LogPNGChunk(logging,mng_FRAM,27L);
9712           chunk[4]=4;
9713           chunk[5]=0;  /* frame name separator (no name) */
9714           chunk[6]=1;  /* flag for changing delay, for next frame only */
9715           chunk[7]=0;  /* flag for changing frame timeout */
9716           chunk[8]=1;  /* flag for changing frame clipping for next frame */
9717           chunk[9]=0;  /* flag for changing frame sync_id */
9718           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9719           chunk[14]=0; /* clipping boundaries delta type */
9720           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9721           PNGLong(chunk+19,
9722              (png_uint_32) (mng_info->page.x + ping_width));
9723           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9724           PNGLong(chunk+27,
9725              (png_uint_32) (mng_info->page.y + ping_height));
9726           (void) WriteBlob(image,31,chunk);
9727           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9728           mng_info->old_framing_mode=4;
9729           mng_info->framing_mode=1;
9730         }
9731
9732       else
9733         mng_info->framing_mode=3;
9734     }
9735   if (mng_info->write_mng && !mng_info->need_fram &&
9736       ((int) image->dispose == 3))
9737      (void) ThrowMagickException(&image->exception,GetMagickModule(),
9738        CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
9739        "`%s'",image->filename);
9740
9741   /*
9742     Free PNG resources.
9743   */
9744
9745   png_destroy_write_struct(&ping,&ping_info);
9746
9747   ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9748
9749 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9750   UnlockSemaphoreInfo(ping_semaphore);
9751 #endif
9752
9753   if (ping_have_blob != MagickFalse)
9754      (void) CloseBlob(image);
9755
9756   image_info=DestroyImageInfo(image_info);
9757   image=DestroyImage(image);
9758
9759   /* Store bit depth actually written */
9760   s[0]=(char) ping_bit_depth;
9761   s[1]='\0';
9762
9763   (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9764
9765   if (logging != MagickFalse)
9766     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9767       "  exit WriteOnePNGImage()");
9768
9769   return(MagickTrue);
9770 /*  End write one PNG image */
9771 }
9772
9773 /*
9774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9775 %                                                                             %
9776 %                                                                             %
9777 %                                                                             %
9778 %   W r i t e P N G I m a g e                                                 %
9779 %                                                                             %
9780 %                                                                             %
9781 %                                                                             %
9782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9783 %
9784 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
9785 %  Multiple-image Network Graphics (MNG) image file.
9786 %
9787 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
9788 %
9789 %  The format of the WritePNGImage method is:
9790 %
9791 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9792 %
9793 %  A description of each parameter follows:
9794 %
9795 %    o image_info: the image info.
9796 %
9797 %    o image:  The image.
9798 %
9799 %  Returns MagickTrue on success, MagickFalse on failure.
9800 %
9801 %  Communicating with the PNG encoder:
9802 %
9803 %  While the datastream written is always in PNG format and normally would
9804 %  be given the "png" file extension, this method also writes the following
9805 %  pseudo-formats which are subsets of PNG:
9806 %
9807 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
9808 %               a depth greater than 8, the depth is reduced. If transparency
9809 %               is present, the tRNS chunk must only have values 0 and 255
9810 %               (i.e., transparency is binary: fully opaque or fully
9811 %               transparent).  If other values are present they will be
9812 %               50%-thresholded to binary transparency.  If more than 256
9813 %               colors are present, they will be quantized to the 3-3-2
9814 %               palette.  If you want better quantization or dithering of
9815 %               the colors or alpha, you need to do it before calling the
9816 %               PNG encoder. The pixels contain 8-bit indices even if
9817 %               they could be represented with 1, 2, or 4 bits.  Grayscale
9818 %               images will be written as indexed PNG files even though the
9819 %               PNG grayscale type might be slightly more efficient.  Please
9820 %               note that writing to the PNG8 format may result in loss
9821 %               of color and alpha data.
9822 %
9823 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
9824 %               chunk can be present to convey binary transparency by naming
9825 %               one of the colors as transparent.  The only loss incurred
9826 %               is reduction of sample depth to 8.  If the image has more
9827 %               than one transparent color, has semitransparent pixels, or
9828 %               has an opaque pixel with the same RGB components as the
9829 %               transparent color, an image is not written.
9830 %
9831 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
9832 %               transparency is permitted, i.e., the alpha sample for
9833 %               each pixel can have any value from 0 to 255. The alpha
9834 %               channel is present even if the image is fully opaque.
9835 %               The only loss in data is the reduction of the sample depth
9836 %               to 8.
9837 %
9838 %    o -define: For more precise control of the PNG output, you can use the
9839 %               Image options "png:bit-depth" and "png:color-type".  These
9840 %               can be set from the commandline with "-define" and also
9841 %               from the application programming interfaces.  The options
9842 %               are case-independent and are converted to lowercase before
9843 %               being passed to this encoder.
9844 %
9845 %               png:color-type can be 0, 2, 3, 4, or 6.
9846 %
9847 %               When png:color-type is 0 (Grayscale), png:bit-depth can
9848 %               be 1, 2, 4, 8, or 16.
9849 %
9850 %               When png:color-type is 2 (RGB), png:bit-depth can
9851 %               be 8 or 16.
9852 %
9853 %               When png:color-type is 3 (Indexed), png:bit-depth can
9854 %               be 1, 2, 4, or 8.  This refers to the number of bits
9855 %               used to store the index.  The color samples always have
9856 %               bit-depth 8 in indexed PNG files.
9857 %
9858 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
9859 %               png:bit-depth can be 8 or 16.
9860 %
9861 %  If the image cannot be written without loss with the requested bit-depth
9862 %  and color-type, a PNG file will not be written, and the encoder will
9863 %  return MagickFalse.
9864 %
9865 %  Since image encoders should not be responsible for the "heavy lifting",
9866 %  the user should make sure that ImageMagick has already reduced the
9867 %  image depth and number of colors and limit transparency to binary
9868 %  transparency prior to attempting to write the image with depth, color,
9869 %   or transparency limitations.
9870 %
9871 %  TODO: Enforce the previous paragraph.
9872 %
9873 %  Note that another definition, "png:bit-depth-written" exists, but it
9874 %  is not intended for external use.  It is only used internally by the
9875 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
9876 %
9877 %  It is possible to request that the PNG encoder write previously-formatted
9878 %  ancillary chunks in the output PNG file, using the "-profile" commandline
9879 %  option as shown below or by setting the profile via a programming
9880 %  interface:
9881 %
9882 %     -profile PNG-chunk-x:<file>
9883 %
9884 %  where x is a location flag and <file> is a file containing the chunk
9885 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
9886 %  This encoder will compute the chunk length and CRC, so those must not
9887 %  be included in the file.
9888 %
9889 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
9890 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
9891 %  of the same type, then add a short unique string after the "x" to prevent
9892 %  subsequent profiles from overwriting the preceding ones, e.g.,
9893 %
9894 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
9895 %
9896 %  As of version 6.6.6 the following optimizations are always done:
9897 %
9898 %   o  32-bit depth is reduced to 16.
9899 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
9900 %      high byte and low byte are identical.
9901 %   o  Palette is sorted to remove unused entries and to put a
9902 %      transparent color first, if BUILD_PNG_PALETTE is defined.
9903 %   o  Opaque matte channel is removed when writing an indexed PNG.
9904 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
9905 %      this can be done without loss and a larger bit depth N was not
9906 %      requested via the "-define PNG:bit-depth=N" option.
9907 %   o  If matte channel is present but only one transparent color is
9908 %      present, RGB+tRNS is written instead of RGBA
9909 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
9910 %      was requested when converting an opaque image).
9911 %
9912 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9913 */
9914 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
9915   Image *image)
9916 {
9917   MagickBooleanType
9918     excluding,
9919     logging,
9920     have_mng_structure,
9921     status;
9922
9923   MngInfo
9924     *mng_info;
9925
9926   const char
9927     *value;
9928
9929   int
9930     i,
9931     source;
9932
9933   /*
9934     Open image file.
9935   */
9936   assert(image_info != (const ImageInfo *) NULL);
9937   assert(image_info->signature == MagickSignature);
9938   assert(image != (Image *) NULL);
9939   assert(image->signature == MagickSignature);
9940   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
9941   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
9942   /*
9943     Allocate a MngInfo structure.
9944   */
9945   have_mng_structure=MagickFalse;
9946   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
9947
9948   if (mng_info == (MngInfo *) NULL)
9949     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9950
9951   /*
9952     Initialize members of the MngInfo structure.
9953   */
9954   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
9955   mng_info->image=image;
9956   mng_info->equal_backgrounds=MagickTrue;
9957   have_mng_structure=MagickTrue;
9958
9959   /* See if user has requested a specific PNG subformat */
9960
9961   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
9962   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
9963   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
9964
9965   if (mng_info->write_png8)
9966     {
9967       mng_info->write_png_colortype = /* 3 */ 4;
9968       mng_info->write_png_depth = 8;
9969       image->depth = 8;
9970     }
9971
9972   if (mng_info->write_png24)
9973     {
9974       mng_info->write_png_colortype = /* 2 */ 3;
9975       mng_info->write_png_depth = 8;
9976       image->depth = 8;
9977
9978       if (image->matte == MagickTrue)
9979         (void) SetImageType(image,TrueColorMatteType);
9980
9981       else
9982         (void) SetImageType(image,TrueColorType);
9983
9984       (void) SyncImage(image);
9985     }
9986
9987   if (mng_info->write_png32)
9988     {
9989       mng_info->write_png_colortype = /* 6 */  7;
9990       mng_info->write_png_depth = 8;
9991       image->depth = 8;
9992
9993       if (image->matte == MagickTrue)
9994         (void) SetImageType(image,TrueColorMatteType);
9995
9996       else
9997         (void) SetImageType(image,TrueColorType);
9998
9999       (void) SyncImage(image);
10000     }
10001
10002   value=GetImageOption(image_info,"png:bit-depth");
10003
10004   if (value != (char *) NULL)
10005     {
10006       if (LocaleCompare(value,"1") == 0)
10007         mng_info->write_png_depth = 1;
10008
10009       else if (LocaleCompare(value,"2") == 0)
10010         mng_info->write_png_depth = 2;
10011
10012       else if (LocaleCompare(value,"4") == 0)
10013         mng_info->write_png_depth = 4;
10014
10015       else if (LocaleCompare(value,"8") == 0)
10016         mng_info->write_png_depth = 8;
10017
10018       else if (LocaleCompare(value,"16") == 0)
10019         mng_info->write_png_depth = 16;
10020
10021       else
10022         (void) ThrowMagickException(&image->exception,
10023              GetMagickModule(),CoderWarning,
10024              "ignoring invalid defined png:bit-depth",
10025              "=%s",value);
10026
10027       if (logging != MagickFalse)
10028         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10029           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
10030     }
10031
10032   value=GetImageOption(image_info,"png:color-type");
10033
10034   if (value != (char *) NULL)
10035     {
10036       /* We must store colortype+1 because 0 is a valid colortype */
10037       if (LocaleCompare(value,"0") == 0)
10038         mng_info->write_png_colortype = 1;
10039
10040       else if (LocaleCompare(value,"2") == 0)
10041         mng_info->write_png_colortype = 3;
10042
10043       else if (LocaleCompare(value,"3") == 0)
10044         mng_info->write_png_colortype = 4;
10045
10046       else if (LocaleCompare(value,"4") == 0)
10047         mng_info->write_png_colortype = 5;
10048
10049       else if (LocaleCompare(value,"6") == 0)
10050         mng_info->write_png_colortype = 7;
10051
10052       else
10053         (void) ThrowMagickException(&image->exception,
10054              GetMagickModule(),CoderWarning,
10055              "ignoring invalid defined png:color-type",
10056              "=%s",value);
10057
10058       if (logging != MagickFalse)
10059         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10060           "  png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
10061     }
10062
10063   /* Check for chunks to be excluded:
10064    *
10065    * The default is to not exclude any known chunks except for any
10066    * listed in the "unused_chunks" array, above.
10067    *
10068    * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10069    * define (in the image properties or in the image artifacts)
10070    * or via a mng_info member.  For convenience, in addition
10071    * to or instead of a comma-separated list of chunks, the
10072    * "exclude-chunk" string can be simply "all" or "none".
10073    *
10074    * The exclude-chunk define takes priority over the mng_info.
10075    *
10076    * A "PNG:include-chunk" define takes  priority over both the
10077    * mng_info and the "PNG:exclude-chunk" define.  Like the
10078    * "exclude-chunk" string, it can define "all" or "none" as
10079    * well as a comma-separated list.  Chunks that are unknown to
10080    * ImageMagick are always excluded, regardless of their "copy-safe"
10081    * status according to the PNG specification, and even if they
10082    * appear in the "include-chunk" list.
10083    *
10084    * Finally, all chunks listed in the "unused_chunks" array are
10085    * automatically excluded, regardless of the other instructions
10086    * or lack thereof.
10087    *
10088    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10089    * will not be written and the gAMA chunk will only be written if it
10090    * is not between .45 and .46, or approximately (1.0/2.2).
10091    *
10092    * If you exclude tRNS and the image has transparency, the colortype
10093    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10094    *
10095    * The -strip option causes StripImage() to set the png:include-chunk
10096    * artifact to "none,gama".
10097    */
10098
10099   mng_info->ping_exclude_bKGD=MagickFalse;
10100   mng_info->ping_exclude_cHRM=MagickFalse;
10101   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10102   mng_info->ping_exclude_gAMA=MagickFalse;
10103   mng_info->ping_exclude_cHRM=MagickFalse;
10104   mng_info->ping_exclude_iCCP=MagickFalse;
10105   /* mng_info->ping_exclude_iTXt=MagickFalse; */
10106   mng_info->ping_exclude_oFFs=MagickFalse;
10107   mng_info->ping_exclude_pHYs=MagickFalse;
10108   mng_info->ping_exclude_sRGB=MagickFalse;
10109   mng_info->ping_exclude_tEXt=MagickFalse;
10110   mng_info->ping_exclude_tRNS=MagickFalse;
10111   mng_info->ping_exclude_vpAg=MagickFalse;
10112   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10113   mng_info->ping_exclude_zTXt=MagickFalse;
10114
10115   excluding=MagickFalse;
10116
10117   for (source=0; source<1; source++)
10118   {
10119     if (source==0)
10120       {
10121        value=GetImageArtifact(image,"png:exclude-chunk");
10122
10123        if (value == NULL)
10124          value=GetImageArtifact(image,"png:exclude-chunks");
10125       }
10126     else
10127       {
10128        value=GetImageOption(image_info,"png:exclude-chunk");
10129
10130        if (value == NULL)
10131          value=GetImageOption(image_info,"png:exclude-chunks");
10132       }
10133
10134     if (value != NULL)
10135     {
10136
10137     size_t
10138       last;
10139
10140     excluding=MagickTrue;
10141
10142     if (logging != MagickFalse)
10143       {
10144         if (source == 0)
10145            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10146               "  png:exclude-chunk=%s found in image artifacts.\n", value);
10147         else
10148            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10149               "  png:exclude-chunk=%s found in image properties.\n", value);
10150       }
10151
10152     last=strlen(value);
10153
10154     for (i=0; i<(int) last; i+=5)
10155     {
10156
10157       if (LocaleNCompare(value+i,"all",3) == 0)
10158       {
10159         mng_info->ping_exclude_bKGD=MagickTrue;
10160         mng_info->ping_exclude_cHRM=MagickTrue;
10161         mng_info->ping_exclude_EXIF=MagickTrue;
10162         mng_info->ping_exclude_gAMA=MagickTrue;
10163         mng_info->ping_exclude_iCCP=MagickTrue;
10164         /* mng_info->ping_exclude_iTXt=MagickTrue; */
10165         mng_info->ping_exclude_oFFs=MagickTrue;
10166         mng_info->ping_exclude_pHYs=MagickTrue;
10167         mng_info->ping_exclude_sRGB=MagickTrue;
10168         mng_info->ping_exclude_tEXt=MagickTrue;
10169         mng_info->ping_exclude_tRNS=MagickTrue;
10170         mng_info->ping_exclude_vpAg=MagickTrue;
10171         mng_info->ping_exclude_zCCP=MagickTrue;
10172         mng_info->ping_exclude_zTXt=MagickTrue;
10173         i--;
10174       }
10175
10176       if (LocaleNCompare(value+i,"none",4) == 0)
10177       {
10178         mng_info->ping_exclude_bKGD=MagickFalse;
10179         mng_info->ping_exclude_cHRM=MagickFalse;
10180         mng_info->ping_exclude_EXIF=MagickFalse;
10181         mng_info->ping_exclude_gAMA=MagickFalse;
10182         mng_info->ping_exclude_iCCP=MagickFalse;
10183         /* mng_info->ping_exclude_iTXt=MagickFalse; */
10184         mng_info->ping_exclude_oFFs=MagickFalse;
10185         mng_info->ping_exclude_pHYs=MagickFalse;
10186         mng_info->ping_exclude_sRGB=MagickFalse;
10187         mng_info->ping_exclude_tEXt=MagickFalse;
10188         mng_info->ping_exclude_tRNS=MagickFalse;
10189         mng_info->ping_exclude_vpAg=MagickFalse;
10190         mng_info->ping_exclude_zCCP=MagickFalse;
10191         mng_info->ping_exclude_zTXt=MagickFalse;
10192       }
10193
10194       if (LocaleNCompare(value+i,"bkgd",4) == 0)
10195         mng_info->ping_exclude_bKGD=MagickTrue;
10196
10197       if (LocaleNCompare(value+i,"chrm",4) == 0)
10198         mng_info->ping_exclude_cHRM=MagickTrue;
10199
10200       if (LocaleNCompare(value+i,"exif",4) == 0)
10201         mng_info->ping_exclude_EXIF=MagickTrue;
10202
10203       if (LocaleNCompare(value+i,"gama",4) == 0)
10204         mng_info->ping_exclude_gAMA=MagickTrue;
10205
10206       if (LocaleNCompare(value+i,"iccp",4) == 0)
10207         mng_info->ping_exclude_iCCP=MagickTrue;
10208
10209     /*
10210       if (LocaleNCompare(value+i,"itxt",4) == 0)
10211         mng_info->ping_exclude_iTXt=MagickTrue;
10212      */
10213
10214       if (LocaleNCompare(value+i,"gama",4) == 0)
10215         mng_info->ping_exclude_gAMA=MagickTrue;
10216
10217       if (LocaleNCompare(value+i,"offs",4) == 0)
10218         mng_info->ping_exclude_oFFs=MagickTrue;
10219
10220       if (LocaleNCompare(value+i,"phys",4) == 0)
10221         mng_info->ping_exclude_pHYs=MagickTrue;
10222
10223       if (LocaleNCompare(value+i,"srgb",4) == 0)
10224         mng_info->ping_exclude_sRGB=MagickTrue;
10225
10226       if (LocaleNCompare(value+i,"text",4) == 0)
10227         mng_info->ping_exclude_tEXt=MagickTrue;
10228
10229       if (LocaleNCompare(value+i,"trns",4) == 0)
10230         mng_info->ping_exclude_tRNS=MagickTrue;
10231
10232       if (LocaleNCompare(value+i,"vpag",4) == 0)
10233         mng_info->ping_exclude_vpAg=MagickTrue;
10234
10235       if (LocaleNCompare(value+i,"zccp",4) == 0)
10236         mng_info->ping_exclude_zCCP=MagickTrue;
10237
10238       if (LocaleNCompare(value+i,"ztxt",4) == 0)
10239         mng_info->ping_exclude_zTXt=MagickTrue;
10240
10241       }
10242     }
10243   }
10244
10245   for (source=0; source<1; source++)
10246   {
10247     if (source==0)
10248       {
10249        value=GetImageArtifact(image,"png:include-chunk");
10250
10251        if (value == NULL)
10252          value=GetImageArtifact(image,"png:include-chunks");
10253       }
10254     else
10255       {
10256        value=GetImageOption(image_info,"png:include-chunk");
10257
10258        if (value == NULL)
10259          value=GetImageOption(image_info,"png:include-chunks");
10260       }
10261
10262     if (value != NULL)
10263     {
10264     size_t
10265       last;
10266
10267     excluding=MagickTrue;
10268
10269     if (logging != MagickFalse)
10270       {
10271         if (source == 0)
10272            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10273               "  png:include-chunk=%s found in image artifacts.\n", value);
10274         else
10275            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10276               "  png:include-chunk=%s found in image properties.\n", value);
10277       }
10278
10279     last=strlen(value);
10280
10281     for (i=0; i<(int) last; i+=5)
10282       {
10283       if (LocaleNCompare(value+i,"all",3) == 0)
10284         {
10285           mng_info->ping_exclude_bKGD=MagickFalse;
10286           mng_info->ping_exclude_cHRM=MagickFalse;
10287           mng_info->ping_exclude_EXIF=MagickFalse;
10288           mng_info->ping_exclude_gAMA=MagickFalse;
10289           mng_info->ping_exclude_iCCP=MagickFalse;
10290           /* mng_info->ping_exclude_iTXt=MagickFalse; */
10291           mng_info->ping_exclude_oFFs=MagickFalse;
10292           mng_info->ping_exclude_pHYs=MagickFalse;
10293           mng_info->ping_exclude_sRGB=MagickFalse;
10294           mng_info->ping_exclude_tEXt=MagickFalse;
10295           mng_info->ping_exclude_tRNS=MagickFalse;
10296           mng_info->ping_exclude_vpAg=MagickFalse;
10297           mng_info->ping_exclude_zCCP=MagickFalse;
10298           mng_info->ping_exclude_zTXt=MagickFalse;
10299           i--;
10300         }
10301
10302       if (LocaleNCompare(value+i,"none",4) == 0)
10303         {
10304           mng_info->ping_exclude_bKGD=MagickTrue;
10305           mng_info->ping_exclude_cHRM=MagickTrue;
10306           mng_info->ping_exclude_EXIF=MagickTrue;
10307           mng_info->ping_exclude_gAMA=MagickTrue;
10308           mng_info->ping_exclude_iCCP=MagickTrue;
10309           /* mng_info->ping_exclude_iTXt=MagickTrue; */
10310           mng_info->ping_exclude_oFFs=MagickTrue;
10311           mng_info->ping_exclude_pHYs=MagickTrue;
10312           mng_info->ping_exclude_sRGB=MagickTrue;
10313           mng_info->ping_exclude_tEXt=MagickTrue;
10314           mng_info->ping_exclude_tRNS=MagickTrue;
10315           mng_info->ping_exclude_vpAg=MagickTrue;
10316           mng_info->ping_exclude_zCCP=MagickTrue;
10317           mng_info->ping_exclude_zTXt=MagickTrue;
10318         }
10319
10320       if (LocaleNCompare(value+i,"bkgd",4) == 0)
10321         mng_info->ping_exclude_bKGD=MagickFalse;
10322
10323       if (LocaleNCompare(value+i,"chrm",4) == 0)
10324         mng_info->ping_exclude_cHRM=MagickFalse;
10325
10326       if (LocaleNCompare(value+i,"exif",4) == 0)
10327         mng_info->ping_exclude_EXIF=MagickFalse;
10328
10329       if (LocaleNCompare(value+i,"gama",4) == 0)
10330         mng_info->ping_exclude_gAMA=MagickFalse;
10331
10332       if (LocaleNCompare(value+i,"iccp",4) == 0)
10333         mng_info->ping_exclude_iCCP=MagickFalse;
10334
10335     /*
10336       if (LocaleNCompare(value+i,"itxt",4) == 0)
10337         mng_info->ping_exclude_iTXt=MagickFalse;
10338      */
10339
10340       if (LocaleNCompare(value+i,"gama",4) == 0)
10341         mng_info->ping_exclude_gAMA=MagickFalse;
10342
10343       if (LocaleNCompare(value+i,"offs",4) == 0)
10344         mng_info->ping_exclude_oFFs=MagickFalse;
10345
10346       if (LocaleNCompare(value+i,"phys",4) == 0)
10347         mng_info->ping_exclude_pHYs=MagickFalse;
10348
10349       if (LocaleNCompare(value+i,"srgb",4) == 0)
10350         mng_info->ping_exclude_sRGB=MagickFalse;
10351
10352       if (LocaleNCompare(value+i,"text",4) == 0)
10353         mng_info->ping_exclude_tEXt=MagickFalse;
10354
10355       if (LocaleNCompare(value+i,"trns",4) == 0)
10356         mng_info->ping_exclude_tRNS=MagickFalse;
10357
10358       if (LocaleNCompare(value+i,"vpag",4) == 0)
10359         mng_info->ping_exclude_vpAg=MagickFalse;
10360
10361       if (LocaleNCompare(value+i,"zccp",4) == 0)
10362         mng_info->ping_exclude_zCCP=MagickFalse;
10363
10364       if (LocaleNCompare(value+i,"ztxt",4) == 0)
10365         mng_info->ping_exclude_zTXt=MagickFalse;
10366
10367       }
10368     }
10369   }
10370
10371   if (excluding != MagickFalse && logging != MagickFalse)
10372   {
10373     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10374       "  Chunks to be excluded from the output PNG:");
10375     if (mng_info->ping_exclude_bKGD != MagickFalse)
10376       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10377           "    bKGD");
10378     if (mng_info->ping_exclude_cHRM != MagickFalse)
10379       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10380           "    cHRM");
10381     if (mng_info->ping_exclude_EXIF != MagickFalse)
10382       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10383           "    EXIF");
10384     if (mng_info->ping_exclude_gAMA != MagickFalse)
10385       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10386           "    gAMA");
10387     if (mng_info->ping_exclude_iCCP != MagickFalse)
10388       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10389           "    iCCP");
10390 /*
10391     if (mng_info->ping_exclude_iTXt != MagickFalse)
10392       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10393           "    iTXt");
10394 */
10395     if (mng_info->ping_exclude_oFFs != MagickFalse)
10396       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10397           "    oFFs");
10398     if (mng_info->ping_exclude_pHYs != MagickFalse)
10399       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10400           "    pHYs");
10401     if (mng_info->ping_exclude_sRGB != MagickFalse)
10402       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10403           "    sRGB");
10404     if (mng_info->ping_exclude_tEXt != MagickFalse)
10405       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10406           "    tEXt");
10407     if (mng_info->ping_exclude_tRNS != MagickFalse)
10408       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10409           "    tRNS");
10410     if (mng_info->ping_exclude_vpAg != MagickFalse)
10411       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10412           "    vpAg");
10413     if (mng_info->ping_exclude_zCCP != MagickFalse)
10414       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10415           "    zCCP");
10416     if (mng_info->ping_exclude_zTXt != MagickFalse)
10417       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10418           "    zTXt");
10419   }
10420
10421   mng_info->need_blob = MagickTrue;
10422
10423   status=WriteOnePNGImage(mng_info,image_info,image);
10424
10425   MngInfoFreeStruct(mng_info,&have_mng_structure);
10426
10427   if (logging != MagickFalse)
10428     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
10429
10430   return(status);
10431 }
10432
10433 #if defined(JNG_SUPPORTED)
10434
10435 /* Write one JNG image */
10436 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
10437    const ImageInfo *image_info,Image *image)
10438 {
10439   Image
10440     *jpeg_image;
10441
10442   ImageInfo
10443     *jpeg_image_info;
10444
10445   MagickBooleanType
10446     logging,
10447     status;
10448
10449   size_t
10450     length;
10451
10452   unsigned char
10453     *blob,
10454     chunk[80],
10455     *p;
10456
10457   unsigned int
10458     jng_alpha_compression_method,
10459     jng_alpha_sample_depth,
10460     jng_color_type,
10461     transparent;
10462
10463   size_t
10464     jng_quality;
10465
10466   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
10467     "  Enter WriteOneJNGImage()");
10468
10469   blob=(unsigned char *) NULL;
10470   jpeg_image=(Image *) NULL;
10471   jpeg_image_info=(ImageInfo *) NULL;
10472
10473   status=MagickTrue;
10474   transparent=image_info->type==GrayscaleMatteType ||
10475      image_info->type==TrueColorMatteType;
10476   jng_color_type=10;
10477   jng_alpha_sample_depth=0;
10478   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
10479   jng_alpha_compression_method=0;
10480
10481   if (image->matte != MagickFalse)
10482     {
10483       /* if any pixels are transparent */
10484       transparent=MagickTrue;
10485       if (image_info->compression==JPEGCompression)
10486         jng_alpha_compression_method=8;
10487     }
10488
10489   if (transparent)
10490     {
10491       jng_color_type=14;
10492
10493       /* Create JPEG blob, image, and image_info */
10494       if (logging != MagickFalse)
10495         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10496           "  Creating jpeg_image_info for opacity.");
10497
10498       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10499
10500       if (jpeg_image_info == (ImageInfo *) NULL)
10501         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10502
10503       if (logging != MagickFalse)
10504         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10505           "  Creating jpeg_image.");
10506
10507       jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10508
10509       if (jpeg_image == (Image *) NULL)
10510         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10511
10512       (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10513       status=SeparateImageChannel(jpeg_image,OpacityChannel);
10514       status=NegateImage(jpeg_image,MagickFalse);
10515       jpeg_image->matte=MagickFalse;
10516
10517       if (jng_quality >= 1000)
10518         jpeg_image_info->quality=jng_quality/1000;
10519
10520       else
10521         jpeg_image_info->quality=jng_quality;
10522
10523       jpeg_image_info->type=GrayscaleType;
10524       (void) SetImageType(jpeg_image,GrayscaleType);
10525       (void) AcquireUniqueFilename(jpeg_image->filename);
10526       (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
10527         "%s",jpeg_image->filename);
10528     }
10529
10530   /* To do: check bit depth of PNG alpha channel */
10531
10532   /* Check if image is grayscale. */
10533   if (image_info->type != TrueColorMatteType && image_info->type !=
10534     TrueColorType && ImageIsGray(image))
10535     jng_color_type-=2;
10536
10537   if (transparent)
10538     {
10539       if (jng_alpha_compression_method==0)
10540         {
10541           const char
10542             *value;
10543
10544           /* Encode opacity as a grayscale PNG blob */
10545           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10546             &image->exception);
10547           if (logging != MagickFalse)
10548             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10549               "  Creating PNG blob.");
10550           length=0;
10551
10552           (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
10553           (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
10554           jpeg_image_info->interlace=NoInterlace;
10555
10556           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10557             &image->exception);
10558
10559           /* Retrieve sample depth used */
10560           value=GetImageProperty(jpeg_image,"png:bit-depth-written");
10561           if (value != (char *) NULL)
10562             jng_alpha_sample_depth= (unsigned int) value[0];
10563         }
10564       else
10565         {
10566           /* Encode opacity as a grayscale JPEG blob */
10567
10568           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10569             &image->exception);
10570
10571           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10572           (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10573           jpeg_image_info->interlace=NoInterlace;
10574           if (logging != MagickFalse)
10575             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10576               "  Creating blob.");
10577           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
10578            &image->exception);
10579           jng_alpha_sample_depth=8;
10580
10581           if (logging != MagickFalse)
10582             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10583               "  Successfully read jpeg_image into a blob, length=%.20g.",
10584               (double) length);
10585
10586         }
10587       /* Destroy JPEG image and image_info */
10588       jpeg_image=DestroyImage(jpeg_image);
10589       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10590       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10591     }
10592
10593   /* Write JHDR chunk */
10594   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
10595   PNGType(chunk,mng_JHDR);
10596   LogPNGChunk(logging,mng_JHDR,16L);
10597   PNGLong(chunk+4,(png_uint_32) image->columns);
10598   PNGLong(chunk+8,(png_uint_32) image->rows);
10599   chunk[12]=jng_color_type;
10600   chunk[13]=8;  /* sample depth */
10601   chunk[14]=8; /*jng_image_compression_method */
10602   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10603   chunk[16]=jng_alpha_sample_depth;
10604   chunk[17]=jng_alpha_compression_method;
10605   chunk[18]=0; /*jng_alpha_filter_method */
10606   chunk[19]=0; /*jng_alpha_interlace_method */
10607   (void) WriteBlob(image,20,chunk);
10608   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10609   if (logging != MagickFalse)
10610     {
10611       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10612         "    JNG width:%15lu",(unsigned long) image->columns);
10613
10614       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10615         "    JNG height:%14lu",(unsigned long) image->rows);
10616
10617       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10618         "    JNG color type:%10d",jng_color_type);
10619
10620       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10621         "    JNG sample depth:%8d",8);
10622
10623       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10624         "    JNG compression:%9d",8);
10625
10626       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10627         "    JNG interlace:%11d",0);
10628
10629       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10630         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
10631
10632       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10633         "    JNG alpha compression:%3d",jng_alpha_compression_method);
10634
10635       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10636         "    JNG alpha filter:%8d",0);
10637
10638       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10639         "    JNG alpha interlace:%5d",0);
10640     }
10641
10642   /* Write any JNG-chunk-b profiles */
10643   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
10644
10645   /*
10646      Write leading ancillary chunks
10647   */
10648
10649   if (transparent)
10650   {
10651     /*
10652       Write JNG bKGD chunk
10653     */
10654
10655     unsigned char
10656       blue,
10657       green,
10658       red;
10659
10660     ssize_t
10661       num_bytes;
10662
10663     if (jng_color_type == 8 || jng_color_type == 12)
10664       num_bytes=6L;
10665     else
10666       num_bytes=10L;
10667     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
10668     PNGType(chunk,mng_bKGD);
10669     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
10670     red=ScaleQuantumToChar(image->background_color.red);
10671     green=ScaleQuantumToChar(image->background_color.green);
10672     blue=ScaleQuantumToChar(image->background_color.blue);
10673     *(chunk+4)=0;
10674     *(chunk+5)=red;
10675     *(chunk+6)=0;
10676     *(chunk+7)=green;
10677     *(chunk+8)=0;
10678     *(chunk+9)=blue;
10679     (void) WriteBlob(image,(size_t) num_bytes,chunk);
10680     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10681   }
10682
10683   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10684     {
10685       /*
10686         Write JNG sRGB chunk
10687       */
10688       (void) WriteBlobMSBULong(image,1L);
10689       PNGType(chunk,mng_sRGB);
10690       LogPNGChunk(logging,mng_sRGB,1L);
10691
10692       if (image->rendering_intent != UndefinedIntent)
10693         chunk[4]=(unsigned char)
10694           Magick_RenderingIntent_to_PNG_RenderingIntent(
10695           (image->rendering_intent));
10696
10697       else
10698         chunk[4]=(unsigned char)
10699           Magick_RenderingIntent_to_PNG_RenderingIntent(
10700           (PerceptualIntent));
10701
10702       (void) WriteBlob(image,5,chunk);
10703       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10704     }
10705   else
10706     {
10707       if (image->gamma != 0.0)
10708         {
10709           /*
10710              Write JNG gAMA chunk
10711           */
10712           (void) WriteBlobMSBULong(image,4L);
10713           PNGType(chunk,mng_gAMA);
10714           LogPNGChunk(logging,mng_gAMA,4L);
10715           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10716           (void) WriteBlob(image,8,chunk);
10717           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10718         }
10719
10720       if ((mng_info->equal_chrms == MagickFalse) &&
10721           (image->chromaticity.red_primary.x != 0.0))
10722         {
10723           PrimaryInfo
10724             primary;
10725
10726           /*
10727              Write JNG cHRM chunk
10728           */
10729           (void) WriteBlobMSBULong(image,32L);
10730           PNGType(chunk,mng_cHRM);
10731           LogPNGChunk(logging,mng_cHRM,32L);
10732           primary=image->chromaticity.white_point;
10733           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10734           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
10735           primary=image->chromaticity.red_primary;
10736           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10737           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
10738           primary=image->chromaticity.green_primary;
10739           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10740           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
10741           primary=image->chromaticity.blue_primary;
10742           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10743           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
10744           (void) WriteBlob(image,36,chunk);
10745           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10746         }
10747     }
10748
10749   if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10750     {
10751       /*
10752          Write JNG pHYs chunk
10753       */
10754       (void) WriteBlobMSBULong(image,9L);
10755       PNGType(chunk,mng_pHYs);
10756       LogPNGChunk(logging,mng_pHYs,9L);
10757       if (image->units == PixelsPerInchResolution)
10758         {
10759           PNGLong(chunk+4,(png_uint_32)
10760             (image->x_resolution*100.0/2.54+0.5));
10761
10762           PNGLong(chunk+8,(png_uint_32)
10763             (image->y_resolution*100.0/2.54+0.5));
10764
10765           chunk[12]=1;
10766         }
10767
10768       else
10769         {
10770           if (image->units == PixelsPerCentimeterResolution)
10771             {
10772               PNGLong(chunk+4,(png_uint_32)
10773                 (image->x_resolution*100.0+0.5));
10774
10775               PNGLong(chunk+8,(png_uint_32)
10776                 (image->y_resolution*100.0+0.5));
10777
10778               chunk[12]=1;
10779             }
10780
10781           else
10782             {
10783               PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10784               PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
10785               chunk[12]=0;
10786             }
10787         }
10788       (void) WriteBlob(image,13,chunk);
10789       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10790     }
10791
10792   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10793     {
10794       /*
10795          Write JNG oFFs chunk
10796       */
10797       (void) WriteBlobMSBULong(image,9L);
10798       PNGType(chunk,mng_oFFs);
10799       LogPNGChunk(logging,mng_oFFs,9L);
10800       PNGsLong(chunk+4,(ssize_t) (image->page.x));
10801       PNGsLong(chunk+8,(ssize_t) (image->page.y));
10802       chunk[12]=0;
10803       (void) WriteBlob(image,13,chunk);
10804       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10805     }
10806   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10807     {
10808        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
10809        PNGType(chunk,mng_vpAg);
10810        LogPNGChunk(logging,mng_vpAg,9L);
10811        PNGLong(chunk+4,(png_uint_32) image->page.width);
10812        PNGLong(chunk+8,(png_uint_32) image->page.height);
10813        chunk[12]=0;   /* unit = pixels */
10814        (void) WriteBlob(image,13,chunk);
10815        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10816     }
10817
10818
10819   if (transparent)
10820     {
10821       if (jng_alpha_compression_method==0)
10822         {
10823           register ssize_t
10824             i;
10825
10826           ssize_t
10827             len;
10828
10829           /* Write IDAT chunk header */
10830           if (logging != MagickFalse)
10831             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10832               "  Write IDAT chunks from blob, length=%.20g.",(double)
10833               length);
10834
10835           /* Copy IDAT chunks */
10836           len=0;
10837           p=blob+8;
10838           for (i=8; i<(ssize_t) length; i+=len+12)
10839           {
10840             len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
10841             p+=4;
10842
10843             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
10844               {
10845                 /* Found an IDAT chunk. */
10846                 (void) WriteBlobMSBULong(image,(size_t) len);
10847                 LogPNGChunk(logging,mng_IDAT,(size_t) len);
10848                 (void) WriteBlob(image,(size_t) len+4,p);
10849                 (void) WriteBlobMSBULong(image,
10850                     crc32(0,p,(uInt) len+4));
10851               }
10852
10853             else
10854               {
10855                 if (logging != MagickFalse)
10856                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10857                     "    Skipping %c%c%c%c chunk, length=%.20g.",
10858                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
10859               }
10860             p+=(8+len);
10861           }
10862         }
10863       else
10864         {
10865           /* Write JDAA chunk header */
10866           if (logging != MagickFalse)
10867             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10868               "  Write JDAA chunk, length=%.20g.",(double) length);
10869           (void) WriteBlobMSBULong(image,(size_t) length);
10870           PNGType(chunk,mng_JDAA);
10871           LogPNGChunk(logging,mng_JDAA,length);
10872           /* Write JDAT chunk(s) data */
10873           (void) WriteBlob(image,4,chunk);
10874           (void) WriteBlob(image,length,blob);
10875           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
10876              (uInt) length));
10877         }
10878       blob=(unsigned char *) RelinquishMagickMemory(blob);
10879     }
10880
10881   /* Encode image as a JPEG blob */
10882   if (logging != MagickFalse)
10883     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10884       "  Creating jpeg_image_info.");
10885   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10886   if (jpeg_image_info == (ImageInfo *) NULL)
10887     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10888
10889   if (logging != MagickFalse)
10890     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10891       "  Creating jpeg_image.");
10892
10893   jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10894   if (jpeg_image == (Image *) NULL)
10895     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10896   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10897
10898   (void) AcquireUniqueFilename(jpeg_image->filename);
10899   (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
10900     jpeg_image->filename);
10901
10902   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10903     &image->exception);
10904
10905   if (logging != MagickFalse)
10906     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10907       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
10908       (double) jpeg_image->rows);
10909
10910   if (jng_color_type == 8 || jng_color_type == 12)
10911     jpeg_image_info->type=GrayscaleType;
10912
10913   jpeg_image_info->quality=jng_quality % 1000;
10914   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10915   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10916
10917   if (logging != MagickFalse)
10918     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10919       "  Creating blob.");
10920
10921   blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
10922
10923   if (logging != MagickFalse)
10924     {
10925       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10926         "  Successfully read jpeg_image into a blob, length=%.20g.",
10927         (double) length);
10928
10929       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10930         "  Write JDAT chunk, length=%.20g.",(double) length);
10931     }
10932
10933   /* Write JDAT chunk(s) */
10934   (void) WriteBlobMSBULong(image,(size_t) length);
10935   PNGType(chunk,mng_JDAT);
10936   LogPNGChunk(logging,mng_JDAT,length);
10937   (void) WriteBlob(image,4,chunk);
10938   (void) WriteBlob(image,length,blob);
10939   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
10940
10941   jpeg_image=DestroyImage(jpeg_image);
10942   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10943   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10944   blob=(unsigned char *) RelinquishMagickMemory(blob);
10945
10946   /* Write any JNG-chunk-e profiles */
10947   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
10948
10949   /* Write IEND chunk */
10950   (void) WriteBlobMSBULong(image,0L);
10951   PNGType(chunk,mng_IEND);
10952   LogPNGChunk(logging,mng_IEND,0);
10953   (void) WriteBlob(image,4,chunk);
10954   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
10955
10956   if (logging != MagickFalse)
10957     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10958       "  exit WriteOneJNGImage()");
10959
10960   return(status);
10961 }
10962
10963
10964 /*
10965 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10966 %                                                                             %
10967 %                                                                             %
10968 %                                                                             %
10969 %   W r i t e J N G I m a g e                                                 %
10970 %                                                                             %
10971 %                                                                             %
10972 %                                                                             %
10973 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10974 %
10975 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
10976 %
10977 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
10978 %
10979 %  The format of the WriteJNGImage method is:
10980 %
10981 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10982 %
10983 %  A description of each parameter follows:
10984 %
10985 %    o image_info: the image info.
10986 %
10987 %    o image:  The image.
10988 %
10989 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10990 */
10991 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10992 {
10993   MagickBooleanType
10994     have_mng_structure,
10995     logging,
10996     status;
10997
10998   MngInfo
10999     *mng_info;
11000
11001   /*
11002     Open image file.
11003   */
11004   assert(image_info != (const ImageInfo *) NULL);
11005   assert(image_info->signature == MagickSignature);
11006   assert(image != (Image *) NULL);
11007   assert(image->signature == MagickSignature);
11008   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11009   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
11010   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11011   if (status == MagickFalse)
11012     return(status);
11013
11014   /*
11015     Allocate a MngInfo structure.
11016   */
11017   have_mng_structure=MagickFalse;
11018   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11019   if (mng_info == (MngInfo *) NULL)
11020     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11021   /*
11022     Initialize members of the MngInfo structure.
11023   */
11024   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11025   mng_info->image=image;
11026   have_mng_structure=MagickTrue;
11027
11028   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11029
11030   status=WriteOneJNGImage(mng_info,image_info,image);
11031   (void) CloseBlob(image);
11032
11033   (void) CatchImageException(image);
11034   MngInfoFreeStruct(mng_info,&have_mng_structure);
11035   if (logging != MagickFalse)
11036     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11037   return(status);
11038 }
11039 #endif
11040
11041
11042
11043 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11044 {
11045   const char
11046     *option;
11047
11048   Image
11049     *next_image;
11050
11051   MagickBooleanType
11052     have_mng_structure,
11053     status;
11054
11055   volatile MagickBooleanType
11056     logging;
11057
11058   MngInfo
11059     *mng_info;
11060
11061   int
11062     image_count,
11063     need_iterations,
11064     need_matte;
11065
11066   volatile int
11067 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11068     defined(PNG_MNG_FEATURES_SUPPORTED)
11069     need_local_plte,
11070 #endif
11071     all_images_are_gray,
11072     need_defi,
11073     use_global_plte;
11074
11075   register ssize_t
11076     i;
11077
11078   unsigned char
11079     chunk[800];
11080
11081   volatile unsigned int
11082     write_jng,
11083     write_mng;
11084
11085   volatile size_t
11086     scene;
11087
11088   size_t
11089     final_delay=0,
11090     initial_delay;
11091
11092 #if (PNG_LIBPNG_VER < 10200)
11093     if (image_info->verbose)
11094       printf("Your PNG library (libpng-%s) is rather old.\n",
11095          PNG_LIBPNG_VER_STRING);
11096 #endif
11097
11098   /*
11099     Open image file.
11100   */
11101   assert(image_info != (const ImageInfo *) NULL);
11102   assert(image_info->signature == MagickSignature);
11103   assert(image != (Image *) NULL);
11104   assert(image->signature == MagickSignature);
11105   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11106   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
11107   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11108   if (status == MagickFalse)
11109     return(status);
11110
11111   /*
11112     Allocate a MngInfo structure.
11113   */
11114   have_mng_structure=MagickFalse;
11115   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11116   if (mng_info == (MngInfo *) NULL)
11117     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11118   /*
11119     Initialize members of the MngInfo structure.
11120   */
11121   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11122   mng_info->image=image;
11123   have_mng_structure=MagickTrue;
11124   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11125
11126   /*
11127    * See if user has requested a specific PNG subformat to be used
11128    * for all of the PNGs in the MNG being written, e.g.,
11129    *
11130    *    convert *.png png8:animation.mng
11131    *
11132    * To do: check -define png:bit_depth and png:color_type as well,
11133    * or perhaps use mng:bit_depth and mng:color_type instead for
11134    * global settings.
11135    */
11136
11137   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11138   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11139   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11140
11141   write_jng=MagickFalse;
11142   if (image_info->compression == JPEGCompression)
11143     write_jng=MagickTrue;
11144
11145   mng_info->adjoin=image_info->adjoin &&
11146     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11147
11148   if (logging != MagickFalse)
11149     {
11150       /* Log some info about the input */
11151       Image
11152         *p;
11153
11154       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11155         "  Checking input image(s)");
11156
11157       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11158         "    Image_info depth: %.20g",(double) image_info->depth);
11159
11160       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11161         "    Type: %d",image_info->type);
11162
11163       scene=0;
11164       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
11165       {
11166         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11167           "    Scene: %.20g",(double) scene++);
11168
11169         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11170           "      Image depth: %.20g",(double) p->depth);
11171
11172         if (p->matte)
11173           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11174             "      Matte: True");
11175
11176         else
11177           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11178             "      Matte: False");
11179
11180         if (p->storage_class == PseudoClass)
11181           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11182             "      Storage class: PseudoClass");
11183
11184         else
11185           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11186             "      Storage class: DirectClass");
11187
11188         if (p->colors)
11189           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11190             "      Number of colors: %.20g",(double) p->colors);
11191
11192         else
11193           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11194             "      Number of colors: unspecified");
11195
11196         if (mng_info->adjoin == MagickFalse)
11197           break;
11198       }
11199     }
11200
11201   use_global_plte=MagickFalse;
11202   all_images_are_gray=MagickFalse;
11203 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11204   need_local_plte=MagickTrue;
11205 #endif
11206   need_defi=MagickFalse;
11207   need_matte=MagickFalse;
11208   mng_info->framing_mode=1;
11209   mng_info->old_framing_mode=1;
11210
11211   if (write_mng)
11212       if (image_info->page != (char *) NULL)
11213         {
11214           /*
11215             Determine image bounding box.
11216           */
11217           SetGeometry(image,&mng_info->page);
11218           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
11219             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
11220         }
11221   if (write_mng)
11222     {
11223       unsigned int
11224         need_geom;
11225
11226       unsigned short
11227         red,
11228         green,
11229         blue;
11230
11231       mng_info->page=image->page;
11232       need_geom=MagickTrue;
11233       if (mng_info->page.width || mng_info->page.height)
11234          need_geom=MagickFalse;
11235       /*
11236         Check all the scenes.
11237       */
11238       initial_delay=image->delay;
11239       need_iterations=MagickFalse;
11240       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
11241       mng_info->equal_physs=MagickTrue,
11242       mng_info->equal_gammas=MagickTrue;
11243       mng_info->equal_srgbs=MagickTrue;
11244       mng_info->equal_backgrounds=MagickTrue;
11245       image_count=0;
11246 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11247     defined(PNG_MNG_FEATURES_SUPPORTED)
11248       all_images_are_gray=MagickTrue;
11249       mng_info->equal_palettes=MagickFalse;
11250       need_local_plte=MagickFalse;
11251 #endif
11252       for (next_image=image; next_image != (Image *) NULL; )
11253       {
11254         if (need_geom)
11255           {
11256             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
11257               mng_info->page.width=next_image->columns+next_image->page.x;
11258
11259             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
11260               mng_info->page.height=next_image->rows+next_image->page.y;
11261           }
11262
11263         if (next_image->page.x || next_image->page.y)
11264           need_defi=MagickTrue;
11265
11266         if (next_image->matte)
11267           need_matte=MagickTrue;
11268
11269         if ((int) next_image->dispose >= BackgroundDispose)
11270           if (next_image->matte || next_image->page.x || next_image->page.y ||
11271               ((next_image->columns < mng_info->page.width) &&
11272                (next_image->rows < mng_info->page.height)))
11273             mng_info->need_fram=MagickTrue;
11274
11275         if (next_image->iterations)
11276           need_iterations=MagickTrue;
11277
11278         final_delay=next_image->delay;
11279
11280         if (final_delay != initial_delay || final_delay > 1UL*
11281            next_image->ticks_per_second)
11282           mng_info->need_fram=1;
11283
11284 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11285     defined(PNG_MNG_FEATURES_SUPPORTED)
11286         /*
11287           check for global palette possibility.
11288         */
11289         if (image->matte != MagickFalse)
11290            need_local_plte=MagickTrue;
11291
11292         if (need_local_plte == 0)
11293           {
11294             if (ImageIsGray(image) == MagickFalse)
11295               all_images_are_gray=MagickFalse;
11296             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
11297             if (use_global_plte == 0)
11298               use_global_plte=mng_info->equal_palettes;
11299             need_local_plte=!mng_info->equal_palettes;
11300           }
11301 #endif
11302         if (GetNextImageInList(next_image) != (Image *) NULL)
11303           {
11304             if (next_image->background_color.red !=
11305                 next_image->next->background_color.red ||
11306                 next_image->background_color.green !=
11307                 next_image->next->background_color.green ||
11308                 next_image->background_color.blue !=
11309                 next_image->next->background_color.blue)
11310               mng_info->equal_backgrounds=MagickFalse;
11311
11312             if (next_image->gamma != next_image->next->gamma)
11313               mng_info->equal_gammas=MagickFalse;
11314
11315             if (next_image->rendering_intent !=
11316                 next_image->next->rendering_intent)
11317               mng_info->equal_srgbs=MagickFalse;
11318
11319             if ((next_image->units != next_image->next->units) ||
11320                 (next_image->x_resolution != next_image->next->x_resolution) ||
11321                 (next_image->y_resolution != next_image->next->y_resolution))
11322               mng_info->equal_physs=MagickFalse;
11323
11324             if (mng_info->equal_chrms)
11325               {
11326                 if (next_image->chromaticity.red_primary.x !=
11327                     next_image->next->chromaticity.red_primary.x ||
11328                     next_image->chromaticity.red_primary.y !=
11329                     next_image->next->chromaticity.red_primary.y ||
11330                     next_image->chromaticity.green_primary.x !=
11331                     next_image->next->chromaticity.green_primary.x ||
11332                     next_image->chromaticity.green_primary.y !=
11333                     next_image->next->chromaticity.green_primary.y ||
11334                     next_image->chromaticity.blue_primary.x !=
11335                     next_image->next->chromaticity.blue_primary.x ||
11336                     next_image->chromaticity.blue_primary.y !=
11337                     next_image->next->chromaticity.blue_primary.y ||
11338                     next_image->chromaticity.white_point.x !=
11339                     next_image->next->chromaticity.white_point.x ||
11340                     next_image->chromaticity.white_point.y !=
11341                     next_image->next->chromaticity.white_point.y)
11342                   mng_info->equal_chrms=MagickFalse;
11343               }
11344           }
11345         image_count++;
11346         next_image=GetNextImageInList(next_image);
11347       }
11348       if (image_count < 2)
11349         {
11350           mng_info->equal_backgrounds=MagickFalse;
11351           mng_info->equal_chrms=MagickFalse;
11352           mng_info->equal_gammas=MagickFalse;
11353           mng_info->equal_srgbs=MagickFalse;
11354           mng_info->equal_physs=MagickFalse;
11355           use_global_plte=MagickFalse;
11356 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11357           need_local_plte=MagickTrue;
11358 #endif
11359           need_iterations=MagickFalse;
11360         }
11361
11362      if (mng_info->need_fram == MagickFalse)
11363        {
11364          /*
11365            Only certain framing rates 100/n are exactly representable without
11366            the FRAM chunk but we'll allow some slop in VLC files
11367          */
11368          if (final_delay == 0)
11369            {
11370              if (need_iterations != MagickFalse)
11371                {
11372                  /*
11373                    It's probably a GIF with loop; don't run it *too* fast.
11374                  */
11375                  if (mng_info->adjoin)
11376                    {
11377                      final_delay=10;
11378                      (void) ThrowMagickException(&image->exception,
11379                         GetMagickModule(),CoderWarning,
11380                        "input has zero delay between all frames; assuming",
11381                        " 10 cs `%s'","");
11382                    }
11383                }
11384              else
11385                mng_info->ticks_per_second=0;
11386            }
11387          if (final_delay != 0)
11388            mng_info->ticks_per_second=(png_uint_32)
11389               (image->ticks_per_second/final_delay);
11390          if (final_delay > 50)
11391            mng_info->ticks_per_second=2;
11392
11393          if (final_delay > 75)
11394            mng_info->ticks_per_second=1;
11395
11396          if (final_delay > 125)
11397            mng_info->need_fram=MagickTrue;
11398
11399          if (need_defi && final_delay > 2 && (final_delay != 4) &&
11400             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
11401             (final_delay != 25) && (final_delay != 50) && (final_delay !=
11402                1UL*image->ticks_per_second))
11403            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
11404        }
11405
11406      if (mng_info->need_fram != MagickFalse)
11407         mng_info->ticks_per_second=1UL*image->ticks_per_second;
11408      /*
11409         If pseudocolor, we should also check to see if all the
11410         palettes are identical and write a global PLTE if they are.
11411         ../glennrp Feb 99.
11412      */
11413      /*
11414         Write the MNG version 1.0 signature and MHDR chunk.
11415      */
11416      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
11417      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
11418      PNGType(chunk,mng_MHDR);
11419      LogPNGChunk(logging,mng_MHDR,28L);
11420      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
11421      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
11422      PNGLong(chunk+12,mng_info->ticks_per_second);
11423      PNGLong(chunk+16,0L);  /* layer count=unknown */
11424      PNGLong(chunk+20,0L);  /* frame count=unknown */
11425      PNGLong(chunk+24,0L);  /* play time=unknown   */
11426      if (write_jng)
11427        {
11428          if (need_matte)
11429            {
11430              if (need_defi || mng_info->need_fram || use_global_plte)
11431                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
11432
11433              else
11434                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
11435            }
11436
11437          else
11438            {
11439              if (need_defi || mng_info->need_fram || use_global_plte)
11440                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
11441
11442              else
11443                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
11444            }
11445        }
11446
11447      else
11448        {
11449          if (need_matte)
11450            {
11451              if (need_defi || mng_info->need_fram || use_global_plte)
11452                PNGLong(chunk+28,11L);    /* simplicity=LC */
11453
11454              else
11455                PNGLong(chunk+28,9L);    /* simplicity=VLC */
11456            }
11457
11458          else
11459            {
11460              if (need_defi || mng_info->need_fram || use_global_plte)
11461                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
11462
11463              else
11464                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
11465            }
11466        }
11467      (void) WriteBlob(image,32,chunk);
11468      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
11469      option=GetImageOption(image_info,"mng:need-cacheoff");
11470      if (option != (const char *) NULL)
11471        {
11472          size_t
11473            length;
11474
11475          /*
11476            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
11477          */
11478          PNGType(chunk,mng_nEED);
11479          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
11480          (void) WriteBlobMSBULong(image,(size_t) length);
11481          LogPNGChunk(logging,mng_nEED,(size_t) length);
11482          length+=4;
11483          (void) WriteBlob(image,length,chunk);
11484          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
11485        }
11486      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
11487          (GetNextImageInList(image) != (Image *) NULL) &&
11488          (image->iterations != 1))
11489        {
11490          /*
11491            Write MNG TERM chunk
11492          */
11493          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
11494          PNGType(chunk,mng_TERM);
11495          LogPNGChunk(logging,mng_TERM,10L);
11496          chunk[4]=3;  /* repeat animation */
11497          chunk[5]=0;  /* show last frame when done */
11498          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
11499             final_delay/MagickMax(image->ticks_per_second,1)));
11500
11501          if (image->iterations == 0)
11502            PNGLong(chunk+10,PNG_UINT_31_MAX);
11503
11504          else
11505            PNGLong(chunk+10,(png_uint_32) image->iterations);
11506
11507          if (logging != MagickFalse)
11508            {
11509              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11510                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
11511               final_delay/MagickMax(image->ticks_per_second,1)));
11512
11513              if (image->iterations == 0)
11514                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11515                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
11516
11517              else
11518                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11519                  "     Image iterations: %.20g",(double) image->iterations);
11520            }
11521          (void) WriteBlob(image,14,chunk);
11522          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11523        }
11524      /*
11525        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11526      */
11527      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
11528           mng_info->equal_srgbs)
11529        {
11530          /*
11531            Write MNG sRGB chunk
11532          */
11533          (void) WriteBlobMSBULong(image,1L);
11534          PNGType(chunk,mng_sRGB);
11535          LogPNGChunk(logging,mng_sRGB,1L);
11536
11537          if (image->rendering_intent != UndefinedIntent)
11538            chunk[4]=(unsigned char)
11539              Magick_RenderingIntent_to_PNG_RenderingIntent(
11540              (image->rendering_intent));
11541
11542          else
11543            chunk[4]=(unsigned char)
11544              Magick_RenderingIntent_to_PNG_RenderingIntent(
11545                (PerceptualIntent));
11546
11547          (void) WriteBlob(image,5,chunk);
11548          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11549          mng_info->have_write_global_srgb=MagickTrue;
11550        }
11551
11552      else
11553        {
11554          if (image->gamma && mng_info->equal_gammas)
11555            {
11556              /*
11557                 Write MNG gAMA chunk
11558              */
11559              (void) WriteBlobMSBULong(image,4L);
11560              PNGType(chunk,mng_gAMA);
11561              LogPNGChunk(logging,mng_gAMA,4L);
11562              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11563              (void) WriteBlob(image,8,chunk);
11564              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11565              mng_info->have_write_global_gama=MagickTrue;
11566            }
11567          if (mng_info->equal_chrms)
11568            {
11569              PrimaryInfo
11570                primary;
11571
11572              /*
11573                 Write MNG cHRM chunk
11574              */
11575              (void) WriteBlobMSBULong(image,32L);
11576              PNGType(chunk,mng_cHRM);
11577              LogPNGChunk(logging,mng_cHRM,32L);
11578              primary=image->chromaticity.white_point;
11579              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11580              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11581              primary=image->chromaticity.red_primary;
11582              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11583              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11584              primary=image->chromaticity.green_primary;
11585              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11586              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11587              primary=image->chromaticity.blue_primary;
11588              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11589              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11590              (void) WriteBlob(image,36,chunk);
11591              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11592              mng_info->have_write_global_chrm=MagickTrue;
11593            }
11594        }
11595      if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11596        {
11597          /*
11598             Write MNG pHYs chunk
11599          */
11600          (void) WriteBlobMSBULong(image,9L);
11601          PNGType(chunk,mng_pHYs);
11602          LogPNGChunk(logging,mng_pHYs,9L);
11603
11604          if (image->units == PixelsPerInchResolution)
11605            {
11606              PNGLong(chunk+4,(png_uint_32)
11607                (image->x_resolution*100.0/2.54+0.5));
11608
11609              PNGLong(chunk+8,(png_uint_32)
11610                (image->y_resolution*100.0/2.54+0.5));
11611
11612              chunk[12]=1;
11613            }
11614
11615          else
11616            {
11617              if (image->units == PixelsPerCentimeterResolution)
11618                {
11619                  PNGLong(chunk+4,(png_uint_32)
11620                    (image->x_resolution*100.0+0.5));
11621
11622                  PNGLong(chunk+8,(png_uint_32)
11623                    (image->y_resolution*100.0+0.5));
11624
11625                  chunk[12]=1;
11626                }
11627
11628              else
11629                {
11630                  PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11631                  PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11632                  chunk[12]=0;
11633                }
11634            }
11635          (void) WriteBlob(image,13,chunk);
11636          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11637        }
11638      /*
11639        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11640        or does not cover the entire frame.
11641      */
11642      if (write_mng && (image->matte || image->page.x > 0 ||
11643          image->page.y > 0 || (image->page.width &&
11644          (image->page.width+image->page.x < mng_info->page.width))
11645          || (image->page.height && (image->page.height+image->page.y
11646          < mng_info->page.height))))
11647        {
11648          (void) WriteBlobMSBULong(image,6L);
11649          PNGType(chunk,mng_BACK);
11650          LogPNGChunk(logging,mng_BACK,6L);
11651          red=ScaleQuantumToShort(image->background_color.red);
11652          green=ScaleQuantumToShort(image->background_color.green);
11653          blue=ScaleQuantumToShort(image->background_color.blue);
11654          PNGShort(chunk+4,red);
11655          PNGShort(chunk+6,green);
11656          PNGShort(chunk+8,blue);
11657          (void) WriteBlob(image,10,chunk);
11658          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11659          if (mng_info->equal_backgrounds)
11660            {
11661              (void) WriteBlobMSBULong(image,6L);
11662              PNGType(chunk,mng_bKGD);
11663              LogPNGChunk(logging,mng_bKGD,6L);
11664              (void) WriteBlob(image,10,chunk);
11665              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11666            }
11667        }
11668
11669 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11670      if ((need_local_plte == MagickFalse) &&
11671          (image->storage_class == PseudoClass) &&
11672          (all_images_are_gray == MagickFalse))
11673        {
11674          size_t
11675            data_length;
11676
11677          /*
11678            Write MNG PLTE chunk
11679          */
11680          data_length=3*image->colors;
11681          (void) WriteBlobMSBULong(image,data_length);
11682          PNGType(chunk,mng_PLTE);
11683          LogPNGChunk(logging,mng_PLTE,data_length);
11684
11685          for (i=0; i < (ssize_t) image->colors; i++)
11686          {
11687            chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11688            chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11689            chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11690          }
11691
11692          (void) WriteBlob(image,data_length+4,chunk);
11693          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11694          mng_info->have_write_global_plte=MagickTrue;
11695        }
11696 #endif
11697     }
11698   scene=0;
11699   mng_info->delay=0;
11700 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11701     defined(PNG_MNG_FEATURES_SUPPORTED)
11702   mng_info->equal_palettes=MagickFalse;
11703 #endif
11704   do
11705   {
11706     if (mng_info->adjoin)
11707     {
11708 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11709     defined(PNG_MNG_FEATURES_SUPPORTED)
11710     /*
11711       If we aren't using a global palette for the entire MNG, check to
11712       see if we can use one for two or more consecutive images.
11713     */
11714     if (need_local_plte && use_global_plte && !all_images_are_gray)
11715       {
11716         if (mng_info->IsPalette)
11717           {
11718             /*
11719               When equal_palettes is true, this image has the same palette
11720               as the previous PseudoClass image
11721             */
11722             mng_info->have_write_global_plte=mng_info->equal_palettes;
11723             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11724             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11725               {
11726                 /*
11727                   Write MNG PLTE chunk
11728                 */
11729                 size_t
11730                   data_length;
11731
11732                 data_length=3*image->colors;
11733                 (void) WriteBlobMSBULong(image,data_length);
11734                 PNGType(chunk,mng_PLTE);
11735                 LogPNGChunk(logging,mng_PLTE,data_length);
11736
11737                 for (i=0; i < (ssize_t) image->colors; i++)
11738                 {
11739                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11740                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11741                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11742                 }
11743
11744                 (void) WriteBlob(image,data_length+4,chunk);
11745                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11746                    (uInt) (data_length+4)));
11747                 mng_info->have_write_global_plte=MagickTrue;
11748               }
11749           }
11750         else
11751           mng_info->have_write_global_plte=MagickFalse;
11752       }
11753 #endif
11754     if (need_defi)
11755       {
11756         ssize_t
11757           previous_x,
11758           previous_y;
11759
11760         if (scene)
11761           {
11762             previous_x=mng_info->page.x;
11763             previous_y=mng_info->page.y;
11764           }
11765         else
11766           {
11767             previous_x=0;
11768             previous_y=0;
11769           }
11770         mng_info->page=image->page;
11771         if ((mng_info->page.x !=  previous_x) ||
11772             (mng_info->page.y != previous_y))
11773           {
11774              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
11775              PNGType(chunk,mng_DEFI);
11776              LogPNGChunk(logging,mng_DEFI,12L);
11777              chunk[4]=0; /* object 0 MSB */
11778              chunk[5]=0; /* object 0 LSB */
11779              chunk[6]=0; /* visible  */
11780              chunk[7]=0; /* abstract */
11781              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11782              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11783              (void) WriteBlob(image,16,chunk);
11784              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11785           }
11786       }
11787     }
11788
11789    mng_info->write_mng=write_mng;
11790
11791    if ((int) image->dispose >= 3)
11792      mng_info->framing_mode=3;
11793
11794    if (mng_info->need_fram && mng_info->adjoin &&
11795        ((image->delay != mng_info->delay) ||
11796         (mng_info->framing_mode != mng_info->old_framing_mode)))
11797      {
11798        if (image->delay == mng_info->delay)
11799          {
11800            /*
11801              Write a MNG FRAM chunk with the new framing mode.
11802            */
11803            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
11804            PNGType(chunk,mng_FRAM);
11805            LogPNGChunk(logging,mng_FRAM,1L);
11806            chunk[4]=(unsigned char) mng_info->framing_mode;
11807            (void) WriteBlob(image,5,chunk);
11808            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11809          }
11810        else
11811          {
11812            /*
11813              Write a MNG FRAM chunk with the delay.
11814            */
11815            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
11816            PNGType(chunk,mng_FRAM);
11817            LogPNGChunk(logging,mng_FRAM,10L);
11818            chunk[4]=(unsigned char) mng_info->framing_mode;
11819            chunk[5]=0;  /* frame name separator (no name) */
11820            chunk[6]=2;  /* flag for changing default delay */
11821            chunk[7]=0;  /* flag for changing frame timeout */
11822            chunk[8]=0;  /* flag for changing frame clipping */
11823            chunk[9]=0;  /* flag for changing frame sync_id */
11824            PNGLong(chunk+10,(png_uint_32)
11825              ((mng_info->ticks_per_second*
11826              image->delay)/MagickMax(image->ticks_per_second,1)));
11827            (void) WriteBlob(image,14,chunk);
11828            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11829            mng_info->delay=(png_uint_32) image->delay;
11830          }
11831        mng_info->old_framing_mode=mng_info->framing_mode;
11832      }
11833
11834 #if defined(JNG_SUPPORTED)
11835    if (image_info->compression == JPEGCompression)
11836      {
11837        ImageInfo
11838          *write_info;
11839
11840        if (logging != MagickFalse)
11841          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11842            "  Writing JNG object.");
11843        /* To do: specify the desired alpha compression method. */
11844        write_info=CloneImageInfo(image_info);
11845        write_info->compression=UndefinedCompression;
11846        status=WriteOneJNGImage(mng_info,write_info,image);
11847        write_info=DestroyImageInfo(write_info);
11848      }
11849    else
11850 #endif
11851      {
11852        if (logging != MagickFalse)
11853          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11854            "  Writing PNG object.");
11855
11856        mng_info->need_blob = MagickFalse;
11857
11858        /* We don't want any ancillary chunks written */
11859        mng_info->ping_exclude_bKGD=MagickTrue;
11860        mng_info->ping_exclude_cHRM=MagickTrue;
11861        mng_info->ping_exclude_EXIF=MagickTrue;
11862        mng_info->ping_exclude_gAMA=MagickTrue;
11863        mng_info->ping_exclude_cHRM=MagickTrue;
11864        mng_info->ping_exclude_iCCP=MagickTrue;
11865        /* mng_info->ping_exclude_iTXt=MagickTrue; */
11866        mng_info->ping_exclude_oFFs=MagickTrue;
11867        mng_info->ping_exclude_pHYs=MagickTrue;
11868        mng_info->ping_exclude_sRGB=MagickTrue;
11869        mng_info->ping_exclude_tEXt=MagickTrue;
11870        mng_info->ping_exclude_tRNS=MagickTrue;
11871        mng_info->ping_exclude_vpAg=MagickTrue;
11872        mng_info->ping_exclude_zCCP=MagickTrue;
11873        mng_info->ping_exclude_zTXt=MagickTrue;
11874
11875        status=WriteOnePNGImage(mng_info,image_info,image);
11876      }
11877
11878     if (status == MagickFalse)
11879       {
11880         MngInfoFreeStruct(mng_info,&have_mng_structure);
11881         (void) CloseBlob(image);
11882         return(MagickFalse);
11883       }
11884     (void) CatchImageException(image);
11885     if (GetNextImageInList(image) == (Image *) NULL)
11886       break;
11887     image=SyncNextImageInList(image);
11888     status=SetImageProgress(image,SaveImagesTag,scene++,
11889       GetImageListLength(image));
11890
11891     if (status == MagickFalse)
11892       break;
11893
11894   } while (mng_info->adjoin);
11895
11896   if (write_mng)
11897     {
11898       while (GetPreviousImageInList(image) != (Image *) NULL)
11899         image=GetPreviousImageInList(image);
11900       /*
11901         Write the MEND chunk.
11902       */
11903       (void) WriteBlobMSBULong(image,0x00000000L);
11904       PNGType(chunk,mng_MEND);
11905       LogPNGChunk(logging,mng_MEND,0L);
11906       (void) WriteBlob(image,4,chunk);
11907       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11908     }
11909   /*
11910     Relinquish resources.
11911   */
11912   (void) CloseBlob(image);
11913   MngInfoFreeStruct(mng_info,&have_mng_structure);
11914
11915   if (logging != MagickFalse)
11916     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
11917
11918   return(MagickTrue);
11919 }
11920 #else /* PNG_LIBPNG_VER > 10011 */
11921
11922 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
11923 {
11924   image=image;
11925   printf("Your PNG library is too old: You have libpng-%s\n",
11926      PNG_LIBPNG_VER_STRING);
11927
11928   ThrowBinaryException(CoderError,"PNG library is too old",
11929      image_info->filename);
11930 }
11931
11932 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11933 {
11934   return(WritePNGImage(image_info,image));
11935 }
11936 #endif /* PNG_LIBPNG_VER > 10011 */
11937 #endif