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