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