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