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