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