]> granicus.if.org Git - imagemagick/blob - coders/png.c
Added some logging to coders/png.c
[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             else if (ping_color_type ==
8028                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
8029                 mng_info->IsPalette)
8030               {
8031
8032               /* Check if grayscale is reducible */
8033                 int
8034                   depth_4_ok=MagickTrue,
8035                   depth_2_ok=MagickTrue,
8036                   depth_1_ok=MagickTrue;
8037
8038                 for (i=0; i < (ssize_t) image_colors; i++)
8039                 {
8040                    unsigned char
8041                      intensity;
8042
8043                    intensity=ScaleQuantumToChar(image->colormap[i].red);
8044
8045                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
8046                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
8047
8048                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
8049                      depth_2_ok=depth_1_ok=MagickFalse;
8050
8051                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
8052                      depth_1_ok=MagickFalse;
8053                 }
8054
8055                 if (depth_1_ok && mng_info->write_png_depth <= 1)
8056                   ping_bit_depth=1;
8057
8058                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
8059                   ping_bit_depth=2;
8060
8061                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
8062                   ping_bit_depth=4;
8063               }
8064           }
8065
8066           image_depth=ping_bit_depth;
8067       }
8068
8069     else
8070
8071       if (mng_info->IsPalette)
8072       {
8073         number_colors=image_colors;
8074
8075         if (image_depth <= 8)
8076           {
8077             /*
8078               Set image palette.
8079             */
8080             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8081
8082             if (mng_info->have_write_global_plte && matte == MagickFalse)
8083               {
8084                 png_set_PLTE(ping,ping_info,NULL,0);
8085
8086                 if (logging != MagickFalse)
8087                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8088                     "  Setting up empty PLTE chunk");
8089               }
8090
8091             else
8092               {
8093                 for (i=0; i < (ssize_t) number_colors; i++)
8094                 {
8095                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8096                   palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8097                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8098                 }
8099
8100                 if (logging != MagickFalse)
8101                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8102                     "  Setting up PLTE chunk with %d colors",
8103                     number_colors);
8104
8105                 ping_have_PLTE=MagickTrue;
8106               }
8107
8108             /* color_type is PNG_COLOR_TYPE_PALETTE */
8109             if (mng_info->write_png_depth == 0)
8110               {
8111                 size_t
8112                   one;
8113
8114                 ping_bit_depth=1;
8115                 one=1;
8116
8117                 while ((one << ping_bit_depth) < number_colors)
8118                   ping_bit_depth <<= 1;
8119               }
8120
8121             ping_num_trans=0;
8122
8123             if (matte != MagickFalse)
8124               {
8125                 /*
8126                  * Set up trans_colors array.
8127                  */
8128                 assert(number_colors <= 256);
8129
8130                 ping_num_trans=(unsigned short) (number_transparent +
8131                   number_semitransparent);
8132
8133                 if (ping_num_trans == 0)
8134                   ping_have_tRNS=MagickFalse;
8135
8136                 else
8137                   {
8138                     if (logging != MagickFalse)
8139                       {
8140                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8141                           "  Scaling ping_trans_color (1)");
8142                       }
8143                     ping_have_tRNS=MagickTrue;
8144
8145                     for (i=0; i < ping_num_trans; i++)
8146                     {
8147                        ping_trans_alpha[i]= (png_byte) (255-
8148                           ScaleQuantumToChar(image->colormap[i].opacity));
8149                     }
8150                   }
8151               }
8152           }
8153       }
8154
8155     else
8156       {
8157
8158         if (image_depth < 8)
8159           image_depth=8;
8160
8161         if ((save_image_depth == 16) && (image_depth == 8))
8162           {
8163             if (logging != MagickFalse)
8164               {
8165                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8166                   "    Scaling ping_trans_color from (%d,%d,%d)",
8167                   (int) ping_trans_color.red,
8168                   (int) ping_trans_color.green,
8169                   (int) ping_trans_color.blue);
8170               }
8171
8172             ping_trans_color.red*=0x0101;
8173             ping_trans_color.green*=0x0101;
8174             ping_trans_color.blue*=0x0101;
8175             ping_trans_color.gray*=0x0101;
8176
8177             if (logging != MagickFalse)
8178               {
8179                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8180                   "    to (%d,%d,%d)",
8181                   (int) ping_trans_color.red,
8182                   (int) ping_trans_color.green,
8183                   (int) ping_trans_color.blue);
8184               }
8185           }
8186       }
8187
8188     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
8189          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
8190
8191     /*
8192       Adjust background and transparency samples in sub-8-bit grayscale files.
8193     */
8194     if (ping_bit_depth < 8 && ping_color_type ==
8195         PNG_COLOR_TYPE_GRAY)
8196       {
8197          png_uint_16
8198            maxval;
8199
8200          size_t
8201            one=1;
8202
8203          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
8204
8205          if (ping_exclude_bKGD == MagickFalse)
8206          {
8207
8208          ping_background.gray=(png_uint_16)
8209            (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
8210
8211          if (logging != MagickFalse)
8212            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8213              "  Setting up bKGD chunk (2)");
8214
8215          ping_have_bKGD = MagickTrue;
8216          }
8217
8218          ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
8219            ping_trans_color.gray));
8220       }
8221
8222   if (ping_exclude_bKGD == MagickFalse)
8223   {
8224     if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
8225       {
8226         /*
8227            Identify which colormap entry is the background color.
8228         */
8229
8230         number_colors=image_colors;
8231
8232         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
8233           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
8234             break;
8235
8236         ping_background.index=(png_byte) i;
8237
8238         if (logging != MagickFalse)
8239           {
8240             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8241               "  Setting up bKGD chunk with index=%d",(int) i);
8242           }
8243
8244         if (i < (ssize_t) number_colors)
8245           {
8246             ping_have_bKGD = MagickTrue;
8247
8248             if (logging != MagickFalse)
8249               {
8250                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8251                   "     background   =(%d,%d,%d)",
8252                         (int) ping_background.red,
8253                         (int) ping_background.green,
8254                         (int) ping_background.blue);
8255               }
8256           }
8257
8258         else  /* Can't happen */
8259           {
8260             if (logging != MagickFalse)
8261               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8262                   "      No room in PLTE to add bKGD color");
8263             ping_have_bKGD = MagickFalse;
8264           }
8265       }
8266   }
8267
8268   if (logging != MagickFalse)
8269     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8270       "    PNG color type: %d",ping_color_type);
8271   /*
8272     Initialize compression level and filtering.
8273   */
8274   if (logging != MagickFalse)
8275     {
8276       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8277         "  Setting up deflate compression");
8278
8279       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8280         "    Compression buffer size: 32768");
8281     }
8282
8283   png_set_compression_buffer_size(ping,32768L);
8284
8285   if (logging != MagickFalse)
8286     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8287       "    Compression mem level: 9");
8288
8289   png_set_compression_mem_level(ping, 9);
8290
8291   quality=image->quality == UndefinedCompressionQuality ? 75UL :
8292      image->quality;
8293
8294   if (quality > 9)
8295     {
8296       int
8297         level;
8298
8299       level=(int) MagickMin((ssize_t) quality/10,9);
8300
8301       if (logging != MagickFalse)
8302         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8303           "    Compression level: %d",level);
8304
8305       png_set_compression_level(ping,level);
8306     }
8307
8308   else
8309     {
8310       if (logging != MagickFalse)
8311         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8312           "    Compression strategy: Z_HUFFMAN_ONLY");
8313
8314       png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
8315     }
8316
8317   if (logging != MagickFalse)
8318     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8319       "  Setting up filtering");
8320
8321 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
8322   /* This became available in libpng-1.0.9.  Output must be a MNG. */
8323   if (mng_info->write_mng && ((quality % 10) == 7))
8324     {
8325       if (logging != MagickFalse)
8326         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8327           "    Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
8328
8329       ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
8330     }
8331
8332   else
8333     if (logging != MagickFalse)
8334       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8335         "    Filter_type: 0");
8336 #endif
8337
8338   {
8339     int
8340       base_filter;
8341
8342     if ((quality % 10) > 5)
8343       base_filter=PNG_ALL_FILTERS;
8344
8345     else
8346       if ((quality % 10) != 5)
8347         base_filter=(int) quality % 10;
8348
8349       else
8350         if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
8351             ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
8352             (quality < 50))
8353           base_filter=PNG_NO_FILTERS;
8354
8355         else
8356           base_filter=PNG_ALL_FILTERS;
8357
8358     if (logging != MagickFalse)
8359       {
8360         if (base_filter == PNG_ALL_FILTERS)
8361           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8362             "    Base filter method: ADAPTIVE");
8363         else
8364           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8365             "    Base filter method: NONE");
8366       }
8367
8368     png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
8369   }
8370
8371   if (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse)
8372     {
8373       ResetImageProfileIterator(image);
8374       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8375       {
8376         profile=GetImageProfile(image,name);
8377
8378         if (profile != (StringInfo *) NULL)
8379           {
8380 #ifdef PNG_WRITE_iCCP_SUPPORTED
8381             if ((LocaleCompare(name,"ICC") == 0) ||
8382                 (LocaleCompare(name,"ICM") == 0))
8383              {
8384
8385                if (ping_exclude_iCCP == MagickFalse)
8386                  {
8387                        png_set_iCCP(ping,ping_info,(const png_charp) name,0,
8388 #if (PNG_LIBPNG_VER < 10500)
8389                          (png_charp) GetStringInfoDatum(profile),
8390 #else
8391                          (png_const_bytep) GetStringInfoDatum(profile),
8392 #endif
8393                          (png_uint_32) GetStringInfoLength(profile));
8394                  }
8395              }
8396
8397             else
8398 #endif
8399               if (ping_exclude_zCCP == MagickFalse)
8400                 {
8401                   Magick_png_write_raw_profile(image_info,ping,ping_info,
8402                     (unsigned char *) name,(unsigned char *) name,
8403                     GetStringInfoDatum(profile),
8404                     (png_uint_32) GetStringInfoLength(profile));
8405                 }
8406           }
8407
8408           if (logging != MagickFalse)
8409             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8410               "  Setting up text chunk with %s profile",name);
8411
8412         name=GetNextImageProfile(image);
8413       }
8414   }
8415
8416 #if defined(PNG_WRITE_sRGB_SUPPORTED)
8417   if ((mng_info->have_write_global_srgb == 0) &&
8418       ((image->rendering_intent != UndefinedIntent) ||
8419       (image->colorspace == sRGBColorspace)))
8420     {
8421       if (ping_exclude_sRGB == MagickFalse)
8422         {
8423           /*
8424             Note image rendering intent.
8425           */
8426           if (logging != MagickFalse)
8427             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8428                 "  Setting up sRGB chunk");
8429
8430           (void) png_set_sRGB(ping,ping_info,(
8431             Magick_RenderingIntent_to_PNG_RenderingIntent(
8432               image->rendering_intent)));
8433
8434           if (ping_exclude_gAMA == MagickFalse)
8435             png_set_gAMA(ping,ping_info,0.45455);
8436         }
8437     }
8438
8439   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
8440 #endif
8441     {
8442       if (ping_exclude_gAMA == MagickFalse &&
8443           (ping_exclude_sRGB == MagickFalse ||
8444           (image->gamma < .45 || image->gamma > .46)))
8445       {
8446       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
8447         {
8448           /*
8449             Note image gamma.
8450             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8451           */
8452           if (logging != MagickFalse)
8453             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8454               "  Setting up gAMA chunk");
8455
8456           png_set_gAMA(ping,ping_info,image->gamma);
8457         }
8458       }
8459
8460       if (ping_exclude_cHRM == MagickFalse)
8461         {
8462           if ((mng_info->have_write_global_chrm == 0) &&
8463               (image->chromaticity.red_primary.x != 0.0))
8464             {
8465               /*
8466                 Note image chromaticity.
8467                 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
8468               */
8469                PrimaryInfo
8470                  bp,
8471                  gp,
8472                  rp,
8473                  wp;
8474
8475                wp=image->chromaticity.white_point;
8476                rp=image->chromaticity.red_primary;
8477                gp=image->chromaticity.green_primary;
8478                bp=image->chromaticity.blue_primary;
8479
8480                if (logging != MagickFalse)
8481                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8482                    "  Setting up cHRM chunk");
8483
8484                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
8485                    bp.x,bp.y);
8486            }
8487         }
8488     }
8489
8490   ping_interlace_method=image_info->interlace != NoInterlace;
8491
8492   if (mng_info->write_mng)
8493     png_set_sig_bytes(ping,8);
8494
8495   /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
8496
8497   if (mng_info->write_png_colortype != 0)
8498     {
8499      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
8500        if (ImageIsGray(image) == MagickFalse)
8501          {
8502            ping_color_type = PNG_COLOR_TYPE_RGB;
8503
8504            if (ping_bit_depth < 8)
8505              ping_bit_depth=8;
8506          }
8507
8508      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
8509        if (ImageIsGray(image) == MagickFalse)
8510          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
8511     }
8512
8513   if (ping_need_colortype_warning != MagickFalse ||
8514      ((mng_info->write_png_depth &&
8515      (int) mng_info->write_png_depth != ping_bit_depth) ||
8516      (mng_info->write_png_colortype &&
8517      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
8518       mng_info->write_png_colortype != 7 &&
8519       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
8520     {
8521       if (logging != MagickFalse)
8522         {
8523           if (ping_need_colortype_warning != MagickFalse)
8524             {
8525               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8526                  "  Image has transparency but tRNS chunk was excluded");
8527             }
8528
8529           if (mng_info->write_png_depth)
8530             {
8531               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8532                   "  Defined PNG:bit-depth=%u, Computed depth=%u",
8533                   mng_info->write_png_depth,
8534                   ping_bit_depth);
8535             }
8536
8537           if (mng_info->write_png_colortype)
8538             {
8539               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8540                   "  Defined PNG:color-type=%u, Computed color type=%u",
8541                   mng_info->write_png_colortype-1,
8542                   ping_color_type);
8543             }
8544         }
8545
8546       png_warning(ping,
8547         "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
8548     }
8549
8550   if (image_matte != MagickFalse && image->matte == MagickFalse)
8551     {
8552       /* Add an opaque matte channel */
8553       image->matte = MagickTrue;
8554       (void) SetImageOpacity(image,0);
8555
8556       if (logging != MagickFalse)
8557         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8558           "  Added an opaque matte channel");
8559     }
8560
8561   if (number_transparent != 0 || number_semitransparent != 0)
8562     {
8563       if (ping_color_type < 4)
8564         {
8565            ping_have_tRNS=MagickTrue;
8566            if (logging != MagickFalse)
8567              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8568                "  Setting ping_have_tRNS=MagickTrue.");
8569         }
8570     }
8571
8572   if (logging != MagickFalse)
8573     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8574       "  Writing PNG header chunks");
8575
8576   png_set_IHDR(ping,ping_info,ping_width,ping_height,
8577                ping_bit_depth,ping_color_type,
8578                ping_interlace_method,ping_compression_method,
8579                ping_filter_method);
8580
8581   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
8582     {
8583       png_set_PLTE(ping,ping_info,palette,number_colors);
8584
8585       if (logging != MagickFalse)
8586         {
8587           for (i=0; i< (ssize_t) number_colors; i++)
8588           {
8589             if (i < ping_num_trans)
8590               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8591                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
8592                       (int) i,
8593                       (int) palette[i].red,
8594                       (int) palette[i].green,
8595                       (int) palette[i].blue,
8596                       (int) i,
8597                       (int) ping_trans_alpha[i]);
8598              else
8599               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8600                 "     PLTE[%d] = (%d,%d,%d)",
8601                       (int) i,
8602                       (int) palette[i].red,
8603                       (int) palette[i].green,
8604                       (int) palette[i].blue);
8605            }
8606          }
8607     }
8608
8609   if (ping_exclude_bKGD == MagickFalse)
8610     {
8611       if (ping_have_bKGD != MagickFalse)
8612           png_set_bKGD(ping,ping_info,&ping_background);
8613     }
8614
8615   if (ping_exclude_pHYs == MagickFalse)
8616     {
8617       if (ping_have_pHYs != MagickFalse)
8618         {
8619           png_set_pHYs(ping,ping_info,
8620              ping_pHYs_x_resolution,
8621              ping_pHYs_y_resolution,
8622              ping_pHYs_unit_type);
8623         }
8624     }
8625
8626 #if defined(PNG_oFFs_SUPPORTED)
8627   if (ping_exclude_oFFs == MagickFalse)
8628     {
8629       if (image->page.x || image->page.y)
8630         {
8631            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
8632               (png_int_32) image->page.y, 0);
8633
8634            if (logging != MagickFalse)
8635              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8636                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
8637                  (int) image->page.x, (int) image->page.y);
8638         }
8639     }
8640 #endif
8641
8642   png_write_info_before_PLTE(ping, ping_info);
8643
8644   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
8645     {
8646       if (logging != MagickFalse)
8647         {
8648           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8649               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
8650         }
8651
8652       if (ping_color_type == 3)
8653          (void) png_set_tRNS(ping, ping_info,
8654                 ping_trans_alpha,
8655                 ping_num_trans,
8656                 NULL);
8657
8658       else
8659         {
8660            (void) png_set_tRNS(ping, ping_info,
8661                   NULL,
8662                   0,
8663                   &ping_trans_color);
8664
8665            if (logging != MagickFalse)
8666              {
8667                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8668                  "     tRNS color   =(%d,%d,%d)",
8669                        (int) ping_trans_color.red,
8670                        (int) ping_trans_color.green,
8671                        (int) ping_trans_color.blue);
8672              }
8673          }
8674     }
8675
8676   /* write any png-chunk-b profiles */
8677   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
8678   png_write_info(ping,ping_info);
8679
8680   /* write any PNG-chunk-m profiles */
8681   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
8682
8683   if (ping_exclude_vpAg == MagickFalse)
8684     {
8685       if ((image->page.width != 0 && image->page.width != image->columns) ||
8686           (image->page.height != 0 && image->page.height != image->rows))
8687         {
8688           unsigned char
8689             chunk[14];
8690
8691           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
8692           PNGType(chunk,mng_vpAg);
8693           LogPNGChunk(logging,mng_vpAg,9L);
8694           PNGLong(chunk+4,(png_uint_32) image->page.width);
8695           PNGLong(chunk+8,(png_uint_32) image->page.height);
8696           chunk[12]=0;   /* unit = pixels */
8697           (void) WriteBlob(image,13,chunk);
8698           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8699         }
8700     }
8701
8702 #if (PNG_LIBPNG_VER == 10206)
8703     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
8704 #define PNG_HAVE_IDAT               0x04
8705     ping->mode |= PNG_HAVE_IDAT;
8706 #undef PNG_HAVE_IDAT
8707 #endif
8708
8709   png_set_packing(ping);
8710   /*
8711     Allocate memory.
8712   */
8713   rowbytes=image->columns;
8714   if (image_depth > 8)
8715     rowbytes*=2;
8716   switch (ping_color_type)
8717     {
8718       case PNG_COLOR_TYPE_RGB:
8719         rowbytes*=3;
8720         break;
8721
8722       case PNG_COLOR_TYPE_GRAY_ALPHA:
8723         rowbytes*=2;
8724         break;
8725
8726       case PNG_COLOR_TYPE_RGBA:
8727         rowbytes*=4;
8728         break;
8729
8730       default:
8731         break;
8732     }
8733
8734   if (logging != MagickFalse)
8735     {
8736       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8737         "  Writing PNG image data");
8738
8739       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8740         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
8741     }
8742   ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
8743     sizeof(*ping_pixels));
8744
8745   if (ping_pixels == (unsigned char *) NULL)
8746     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8747
8748   /*
8749     Initialize image scanlines.
8750   */
8751   if (setjmp(png_jmpbuf(ping)))
8752     {
8753       /*
8754         PNG write failed.
8755       */
8756 #ifdef PNG_DEBUG
8757      if (image_info->verbose)
8758         (void) printf("PNG write has failed.\n");
8759 #endif
8760       png_destroy_write_struct(&ping,&ping_info);
8761       if (quantum_info != (QuantumInfo *) NULL)
8762         quantum_info=DestroyQuantumInfo(quantum_info);
8763       if (ping_pixels != (unsigned char *) NULL)
8764         ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
8765 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8766       UnlockSemaphoreInfo(ping_semaphore);
8767 #endif
8768       if (mng_info->need_blob != MagickFalse)
8769           (void) CloseBlob(image);
8770       image_info=DestroyImageInfo(image_info);
8771       image=DestroyImage(image);
8772       return(MagickFalse);
8773     }
8774   quantum_info=AcquireQuantumInfo(image_info,image);
8775   if (quantum_info == (QuantumInfo *) NULL)
8776     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8777   quantum_info->format=UndefinedQuantumFormat;
8778   quantum_info->depth=image_depth;
8779   num_passes=png_set_interlace_handling(ping);
8780
8781   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
8782        !mng_info->write_png32) &&
8783        (mng_info->IsPalette ||
8784        (image_info->type == BilevelType)) &&
8785        image_matte == MagickFalse && ImageIsMonochrome(image))
8786     {
8787       /* Palette, Bilevel, or Opaque Monochrome */
8788       register const PixelPacket
8789         *p;
8790
8791       quantum_info->depth=8;
8792       for (pass=0; pass < num_passes; pass++)
8793       {
8794         /*
8795           Convert PseudoClass image to a PNG monochrome image.
8796         */
8797         for (y=0; y < (ssize_t) image->rows; y++)
8798         {
8799
8800           if (logging != MagickFalse)
8801              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8802                  "    Writing row of pixels (0)");
8803
8804           p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
8805
8806           if (p == (const PixelPacket *) NULL)
8807             break;
8808
8809           if (mng_info->IsPalette)
8810             {
8811               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8812                 quantum_info,GrayQuantum,ping_pixels,&image->exception);
8813               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
8814                   mng_info->write_png_depth &&
8815                   mng_info->write_png_depth != old_bit_depth)
8816                 {
8817                   /* Undo pixel scaling */
8818                   for (i=0; i < (ssize_t) image->columns; i++)
8819                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
8820                      >> (8-old_bit_depth));
8821                 }
8822             }
8823
8824           else
8825             {
8826               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8827                 quantum_info,RedQuantum,ping_pixels,&image->exception);
8828             }
8829
8830           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
8831             for (i=0; i < (ssize_t) image->columns; i++)
8832                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
8833                       255 : 0);
8834
8835           if (logging != MagickFalse && y == 0)
8836             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8837                 "    Writing row of pixels (1)");
8838
8839           png_write_row(ping,ping_pixels);
8840         }
8841         if (image->previous == (Image *) NULL)
8842           {
8843             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
8844             if (status == MagickFalse)
8845               break;
8846           }
8847       }
8848     }
8849
8850   else   /* Not Palette, Bilevel, or Opaque Monochrome */
8851     {
8852       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
8853          !mng_info->write_png32) &&
8854          (image_matte != MagickFalse ||
8855          (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
8856          (mng_info->IsPalette) && ImageIsGray(image))
8857         {
8858           register const PixelPacket
8859             *p;
8860
8861           for (pass=0; pass < num_passes; pass++)
8862           {
8863
8864           for (y=0; y < (ssize_t) image->rows; y++)
8865           {
8866             p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
8867
8868             if (p == (const PixelPacket *) NULL)
8869               break;
8870
8871             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8872               {
8873                 if (mng_info->IsPalette)
8874                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8875                     quantum_info,GrayQuantum,ping_pixels,&image->exception);
8876
8877                 else
8878                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8879                     quantum_info,RedQuantum,ping_pixels,&image->exception);
8880
8881                 if (logging != MagickFalse && y == 0)
8882                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8883                        "    Writing GRAY PNG pixels (2)");
8884               }
8885
8886             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
8887               {
8888                 if (logging != MagickFalse && y == 0)
8889                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8890                          "    Writing GRAY_ALPHA PNG pixels (2)");
8891
8892                 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8893                   quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
8894               }
8895
8896             if (logging != MagickFalse && y == 0)
8897               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8898                   "    Writing row of pixels (2)");
8899
8900             png_write_row(ping,ping_pixels);
8901           }
8902
8903           if (image->previous == (Image *) NULL)
8904             {
8905               status=SetImageProgress(image,LoadImageTag,pass,num_passes);
8906               if (status == MagickFalse)
8907                 break;
8908             }
8909           }
8910         }
8911
8912       else
8913         {
8914           register const PixelPacket
8915             *p;
8916
8917           for (pass=0; pass < num_passes; pass++)
8918           {
8919             if ((image_depth > 8) || (mng_info->write_png24 ||
8920                 mng_info->write_png32 ||
8921                 (!mng_info->write_png8 && !mng_info->IsPalette)))
8922             {
8923               for (y=0; y < (ssize_t) image->rows; y++)
8924               {
8925                 p=GetVirtualPixels(image,0,y,image->columns,1,
8926                    &image->exception);
8927
8928                 if (p == (const PixelPacket *) NULL)
8929                   break;
8930
8931                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8932                   {
8933                     if (image->storage_class == DirectClass)
8934                       (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8935                         quantum_info,RedQuantum,ping_pixels,&image->exception);
8936
8937                     else
8938                       (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8939                         quantum_info,GrayQuantum,ping_pixels,&image->exception);
8940                   }
8941
8942                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
8943                   {
8944                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8945                       quantum_info,GrayAlphaQuantum,ping_pixels,
8946                       &image->exception);
8947
8948                     if (logging != MagickFalse && y == 0)
8949                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8950                            "    Writing GRAY_ALPHA PNG pixels (3)");
8951                   }
8952
8953                 else if (image_matte != MagickFalse)
8954                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8955                     quantum_info,RGBAQuantum,ping_pixels,&image->exception);
8956
8957                 else
8958                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8959                     quantum_info,RGBQuantum,ping_pixels,&image->exception);
8960
8961                 if (logging != MagickFalse && y == 0)
8962                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8963                       "    Writing row of pixels (3)");
8964
8965                 png_write_row(ping,ping_pixels);
8966               }
8967             }
8968
8969           else
8970             /* not ((image_depth > 8) || (mng_info->write_png24 ||
8971                 mng_info->write_png32 ||
8972                 (!mng_info->write_png8 && !mng_info->IsPalette))) */
8973             {
8974               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
8975                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
8976                 {
8977                   if (logging != MagickFalse)
8978                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8979                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
8980
8981                   quantum_info->depth=8;
8982                   image_depth=8;
8983                 }
8984
8985               for (y=0; y < (ssize_t) image->rows; y++)
8986               {
8987                 if (logging != MagickFalse && y == 0)
8988                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8989                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
8990
8991                 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
8992
8993                 if (p == (const PixelPacket *) NULL)
8994                   break;
8995
8996                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
8997                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
8998                        quantum_info,GrayQuantum,ping_pixels,&image->exception);
8999
9000                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
9001                   {
9002                     if (logging != MagickFalse && y == 0)
9003                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9004                            "  Writing GRAY_ALPHA PNG pixels (4)");
9005
9006                     (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9007                          quantum_info,GrayAlphaQuantum,ping_pixels,
9008                          &image->exception);
9009                   }
9010
9011                 else
9012                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
9013                     quantum_info,IndexQuantum,ping_pixels,&image->exception);
9014
9015                   if (logging != MagickFalse && y <= 2)
9016                   {
9017                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9018                         "  Writing row of pixels (4)");
9019
9020                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9021                         "  ping_pixels[0]=%d,ping_pixels[1]=%d",
9022                         (int)ping_pixels[0],(int)ping_pixels[1]);
9023                   }
9024                 png_write_row(ping,ping_pixels);
9025               }
9026             }
9027
9028             if (image->previous == (Image *) NULL)
9029               {
9030                 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
9031                 if (status == MagickFalse)
9032                   break;
9033               }
9034           }
9035         }
9036     }
9037
9038   if (quantum_info != (QuantumInfo *) NULL)
9039     quantum_info=DestroyQuantumInfo(quantum_info);
9040
9041   if (logging != MagickFalse)
9042     {
9043       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9044         "  Wrote PNG image data");
9045
9046       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9047         "    Width: %.20g",(double) ping_width);
9048
9049       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9050         "    Height: %.20g",(double) ping_height);
9051
9052       if (mng_info->write_png_depth)
9053         {
9054           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9055             "    Defined PNG:bit-depth: %d",mng_info->write_png_depth);
9056         }
9057
9058       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9059         "    PNG bit-depth written: %d",ping_bit_depth);
9060
9061       if (mng_info->write_png_colortype)
9062         {
9063           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9064             "    Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
9065         }
9066
9067       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9068         "    PNG color-type written: %d",ping_color_type);
9069
9070       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9071         "    PNG Interlace method: %d",ping_interlace_method);
9072     }
9073   /*
9074     Generate text chunks.
9075   */
9076   if (ping_exclude_tEXt == MagickFalse && ping_exclude_zTXt == MagickFalse)
9077   {
9078     ResetImagePropertyIterator(image);
9079     property=GetNextImageProperty(image);
9080     while (property != (const char *) NULL)
9081     {
9082       png_textp
9083         text;
9084
9085       value=GetImageProperty(image,property);
9086       if (value != (const char *) NULL)
9087         {
9088           text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
9089           text[0].key=(char *) property;
9090           text[0].text=(char *) value;
9091           text[0].text_length=strlen(value);
9092
9093           if (ping_exclude_tEXt != MagickFalse)
9094              text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
9095
9096           else if (ping_exclude_zTXt != MagickFalse)
9097              text[0].compression=PNG_TEXT_COMPRESSION_NONE;
9098
9099           else
9100           {
9101              text[0].compression=image_info->compression == NoCompression ||
9102                (image_info->compression == UndefinedCompression &&
9103                text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
9104                PNG_TEXT_COMPRESSION_zTXt ;
9105           }
9106
9107           if (logging != MagickFalse)
9108             {
9109               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9110                 "  Setting up text chunk");
9111
9112               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9113                 "    keyword: %s",text[0].key);
9114             }
9115
9116           png_set_text(ping,ping_info,text,1);
9117           png_free(ping,text);
9118         }
9119       property=GetNextImageProperty(image);
9120     }
9121   }
9122
9123   /* write any PNG-chunk-e profiles */
9124   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
9125
9126   if (logging != MagickFalse)
9127     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9128       "  Writing PNG end info");
9129
9130   png_write_end(ping,ping_info);
9131
9132   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
9133     {
9134       if (mng_info->page.x || mng_info->page.y ||
9135           (ping_width != mng_info->page.width) ||
9136           (ping_height != mng_info->page.height))
9137         {
9138           unsigned char
9139             chunk[32];
9140
9141           /*
9142             Write FRAM 4 with clipping boundaries followed by FRAM 1.
9143           */
9144           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
9145           PNGType(chunk,mng_FRAM);
9146           LogPNGChunk(logging,mng_FRAM,27L);
9147           chunk[4]=4;
9148           chunk[5]=0;  /* frame name separator (no name) */
9149           chunk[6]=1;  /* flag for changing delay, for next frame only */
9150           chunk[7]=0;  /* flag for changing frame timeout */
9151           chunk[8]=1;  /* flag for changing frame clipping for next frame */
9152           chunk[9]=0;  /* flag for changing frame sync_id */
9153           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
9154           chunk[14]=0; /* clipping boundaries delta type */
9155           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
9156           PNGLong(chunk+19,
9157              (png_uint_32) (mng_info->page.x + ping_width));
9158           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
9159           PNGLong(chunk+27,
9160              (png_uint_32) (mng_info->page.y + ping_height));
9161           (void) WriteBlob(image,31,chunk);
9162           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
9163           mng_info->old_framing_mode=4;
9164           mng_info->framing_mode=1;
9165         }
9166
9167       else
9168         mng_info->framing_mode=3;
9169     }
9170   if (mng_info->write_mng && !mng_info->need_fram &&
9171       ((int) image->dispose == 3))
9172      (void) ThrowMagickException(&image->exception,GetMagickModule(),
9173        CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
9174        "`%s'",image->filename);
9175
9176   /*
9177     Free PNG resources.
9178   */
9179
9180   png_destroy_write_struct(&ping,&ping_info);
9181
9182   ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9183
9184 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
9185   UnlockSemaphoreInfo(ping_semaphore);
9186 #endif
9187
9188   if (mng_info->need_blob != MagickFalse)
9189      (void) CloseBlob(image);
9190
9191   image_info=DestroyImageInfo(image_info);
9192   image=DestroyImage(image);
9193
9194   /* Store bit depth actually written */
9195   s[0]=(char) ping_bit_depth;
9196   s[1]='\0';
9197
9198   (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
9199
9200   if (logging != MagickFalse)
9201     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9202       "  exit WriteOnePNGImage()");
9203
9204   return(MagickTrue);
9205 /*  End write one PNG image */
9206 }
9207
9208 /*
9209 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9210 %                                                                             %
9211 %                                                                             %
9212 %                                                                             %
9213 %   W r i t e P N G I m a g e                                                 %
9214 %                                                                             %
9215 %                                                                             %
9216 %                                                                             %
9217 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9218 %
9219 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
9220 %  Multiple-image Network Graphics (MNG) image file.
9221 %
9222 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
9223 %
9224 %  The format of the WritePNGImage method is:
9225 %
9226 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9227 %
9228 %  A description of each parameter follows:
9229 %
9230 %    o image_info: the image info.
9231 %
9232 %    o image:  The image.
9233 %
9234 %  Returns MagickTrue on success, MagickFalse on failure.
9235 %
9236 %  Communicating with the PNG encoder:
9237 %
9238 %  While the datastream written is always in PNG format and normally would
9239 %  be given the "png" file extension, this method also writes the following
9240 %  pseudo-formats which are subsets of PNG:
9241 %
9242 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If transparency
9243 %               is present, the tRNS chunk must only have values 0 and 255
9244 %               (i.e., transparency is binary: fully opaque or fully
9245 %               transparent).  The pixels contain 8-bit indices even if
9246 %               they could be represented with 1, 2, or 4 bits. Note: grayscale
9247 %               images will be written as indexed PNG files even though the
9248 %               PNG grayscale type might be slightly more efficient.
9249 %
9250 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
9251 %               chunk can be present to convey binary transparency by naming
9252 %               one of the colors as transparent.
9253 %
9254 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
9255 %               transparency is permitted, i.e., the alpha sample for
9256 %               each pixel can have any value from 0 to 255. The alpha
9257 %               channel is present even if the image is fully opaque.
9258 %
9259 %    o -define: For more precise control of the PNG output, you can use the
9260 %               Image options "png:bit-depth" and "png:color-type".  These
9261 %               can be set from the commandline with "-define" and also
9262 %               from the application programming interfaces.  The options
9263 %               are case-independent and are converted to lowercase before
9264 %               being passed to this encoder.
9265 %
9266 %               png:color-type can be 0, 2, 3, 4, or 6.
9267 %
9268 %               When png:color-type is 0 (Grayscale), png:bit-depth can
9269 %               be 1, 2, 4, 8, or 16.
9270 %
9271 %               When png:color-type is 2 (RGB), png:bit-depth can
9272 %               be 8 or 16.
9273 %
9274 %               When png:color-type is 3 (Indexed), png:bit-depth can
9275 %               be 1, 2, 4, or 8.  This refers to the number of bits
9276 %               used to store the index.  The color samples always have
9277 %               bit-depth 8 in indexed PNG files.
9278 %
9279 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
9280 %               png:bit-depth can be 8 or 16.
9281 %
9282 %  If the image cannot be written without loss in the requested PNG8, PNG24,
9283 %  or PNG32 format or with the requested bit-depth and color-type without loss,
9284 %  a PNG file will not be written, and the encoder will return MagickFalse.
9285 %  Since image encoders should not be responsible for the "heavy lifting",
9286 %  the user should make sure that ImageMagick has already reduced the
9287 %  image depth and number of colors and limit transparency to binary
9288 %  transparency prior to attempting to write the image in a format that
9289 %  is subject to depth, color, or transparency limitations.
9290 %
9291 %  TODO: Enforce the previous paragraph.
9292 %
9293 %  Note that another definition, "png:bit-depth-written" exists, but it
9294 %  is not intended for external use.  It is only used internally by the
9295 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
9296 %
9297 %  It is possible to request that the PNG encoder write previously-formatted
9298 %  ancillary chunks in the output PNG file, using the "-profile" commandline
9299 %  option as shown below or by setting the profile via a programming
9300 %  interface:
9301 %
9302 %     -profile PNG-chunk-x:<file>
9303 %
9304 %  where x is a location flag and <file> is a file containing the chunk
9305 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
9306 %  This encoder will compute the chunk length and CRC, so those must not
9307 %  be included in the file.
9308 %
9309 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
9310 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
9311 %  of the same type, then add a short unique string after the "x" to prevent
9312 %  subsequent profiles from overwriting the preceding ones, e.g.,
9313 %
9314 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
9315 %
9316 %  As of version 6.6.6 the following optimizations are always done:
9317 %
9318 %   o  32-bit depth is reduced to 16.
9319 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
9320 %      high byte and low byte are identical.
9321 %   o  Palette is sorted to remove unused entries and to put a
9322 %      transparent color first, if BUILD_PNG_PALETTE is defined.
9323 %   o  Opaque matte channel is removed when writing an indexed PNG.
9324 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
9325 %      this can be done without loss and a larger bit depth N was not
9326 %      requested via the "-define PNG:bit-depth=N" option.
9327 %   o  If matte channel is present but only one transparent color is
9328 %      present, RGB+tRNS is written instead of RGBA
9329 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
9330 %      was requested when converting an opaque image).
9331 %
9332 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9333 */
9334 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
9335   Image *image)
9336 {
9337   MagickBooleanType
9338     excluding,
9339     logging,
9340     have_mng_structure,
9341     status;
9342
9343   MngInfo
9344     *mng_info;
9345
9346   const char
9347     *value;
9348
9349   int
9350     i,
9351     source;
9352
9353   /*
9354     Open image file.
9355   */
9356   assert(image_info != (const ImageInfo *) NULL);
9357   assert(image_info->signature == MagickSignature);
9358   assert(image != (Image *) NULL);
9359   assert(image->signature == MagickSignature);
9360   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
9361   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WritePNGImage()");
9362   /*
9363     Allocate a MngInfo structure.
9364   */
9365   have_mng_structure=MagickFalse;
9366   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
9367
9368   if (mng_info == (MngInfo *) NULL)
9369     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9370
9371   /*
9372     Initialize members of the MngInfo structure.
9373   */
9374   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
9375   mng_info->image=image;
9376   mng_info->equal_backgrounds=MagickTrue;
9377   have_mng_structure=MagickTrue;
9378
9379   /* See if user has requested a specific PNG subformat */
9380
9381   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
9382   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
9383   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
9384
9385   if (mng_info->write_png8)
9386     {
9387       mng_info->write_png_colortype = /* 3 */ 4;
9388       mng_info->write_png_depth = 8;
9389       image->depth = 8;
9390     }
9391
9392   if (mng_info->write_png24)
9393     {
9394       mng_info->write_png_colortype = /* 2 */ 3;
9395       mng_info->write_png_depth = 8;
9396       image->depth = 8;
9397
9398       if (image->matte == MagickTrue)
9399         (void) SetImageType(image,TrueColorMatteType);
9400
9401       else
9402         (void) SetImageType(image,TrueColorType);
9403
9404       (void) SyncImage(image);
9405     }
9406
9407   if (mng_info->write_png32)
9408     {
9409       mng_info->write_png_colortype = /* 6 */  7;
9410       mng_info->write_png_depth = 8;
9411       image->depth = 8;
9412
9413       if (image->matte == MagickTrue)
9414         (void) SetImageType(image,TrueColorMatteType);
9415
9416       else
9417         (void) SetImageType(image,TrueColorType);
9418
9419       (void) SyncImage(image);
9420     }
9421
9422   value=GetImageOption(image_info,"png:bit-depth");
9423
9424   if (value != (char *) NULL)
9425     {
9426       if (LocaleCompare(value,"1") == 0)
9427         mng_info->write_png_depth = 1;
9428
9429       else if (LocaleCompare(value,"2") == 0)
9430         mng_info->write_png_depth = 2;
9431
9432       else if (LocaleCompare(value,"4") == 0)
9433         mng_info->write_png_depth = 4;
9434
9435       else if (LocaleCompare(value,"8") == 0)
9436         mng_info->write_png_depth = 8;
9437
9438       else if (LocaleCompare(value,"16") == 0)
9439         mng_info->write_png_depth = 16;
9440
9441       else
9442         (void) ThrowMagickException(&image->exception,
9443              GetMagickModule(),CoderWarning,
9444              "ignoring invalid defined png:bit-depth",
9445              "=%s",value);
9446
9447       if (logging != MagickFalse)
9448         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9449           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
9450     }
9451
9452   value=GetImageOption(image_info,"png:color-type");
9453
9454   if (value != (char *) NULL)
9455     {
9456       /* We must store colortype+1 because 0 is a valid colortype */
9457       if (LocaleCompare(value,"0") == 0)
9458         mng_info->write_png_colortype = 1;
9459
9460       else if (LocaleCompare(value,"2") == 0)
9461         mng_info->write_png_colortype = 3;
9462
9463       else if (LocaleCompare(value,"3") == 0)
9464         mng_info->write_png_colortype = 4;
9465
9466       else if (LocaleCompare(value,"4") == 0)
9467         mng_info->write_png_colortype = 5;
9468
9469       else if (LocaleCompare(value,"6") == 0)
9470         mng_info->write_png_colortype = 7;
9471
9472       else
9473         (void) ThrowMagickException(&image->exception,
9474              GetMagickModule(),CoderWarning,
9475              "ignoring invalid defined png:color-type",
9476              "=%s",value);
9477
9478       if (logging != MagickFalse)
9479         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9480           "  png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
9481     }
9482
9483   /* Check for chunks to be excluded:
9484    *
9485    * The default is to not exclude any known chunks except for any
9486    * listed in the "unused_chunks" array, above.
9487    *
9488    * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
9489    * define (in the image properties or in the image artifacts)
9490    * or via a mng_info member.  For convenience, in addition
9491    * to or instead of a comma-separated list of chunks, the
9492    * "exclude-chunk" string can be simply "all" or "none".
9493    *
9494    * The exclude-chunk define takes priority over the mng_info.
9495    *
9496    * A "PNG:include-chunk" define takes  priority over both the
9497    * mng_info and the "PNG:exclude-chunk" define.  Like the
9498    * "exclude-chunk" string, it can define "all" or "none" as
9499    * well as a comma-separated list.  Chunks that are unknown to
9500    * ImageMagick are always excluded, regardless of their "copy-safe"
9501    * status according to the PNG specification, and even if they
9502    * appear in the "include-chunk" list.
9503    *
9504    * Finally, all chunks listed in the "unused_chunks" array are
9505    * automatically excluded, regardless of the other instructions
9506    * or lack thereof.
9507    *
9508    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
9509    * will not be written and the gAMA chunk will only be written if it
9510    * is not between .45 and .46, or approximately (1.0/2.2).
9511    *
9512    * If you exclude tRNS and the image has transparency, the colortype
9513    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
9514    *
9515    * The -strip option causes StripImage() to set the png:include-chunk
9516    * artifact to "none,gama".
9517    */
9518
9519   mng_info->ping_exclude_bKGD=MagickFalse;
9520   mng_info->ping_exclude_cHRM=MagickFalse;
9521   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
9522   mng_info->ping_exclude_gAMA=MagickFalse;
9523   mng_info->ping_exclude_cHRM=MagickFalse;
9524   mng_info->ping_exclude_iCCP=MagickFalse;
9525   /* mng_info->ping_exclude_iTXt=MagickFalse; */
9526   mng_info->ping_exclude_oFFs=MagickFalse;
9527   mng_info->ping_exclude_pHYs=MagickFalse;
9528   mng_info->ping_exclude_sRGB=MagickFalse;
9529   mng_info->ping_exclude_tEXt=MagickFalse;
9530   mng_info->ping_exclude_tRNS=MagickFalse;
9531   mng_info->ping_exclude_vpAg=MagickFalse;
9532   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
9533   mng_info->ping_exclude_zTXt=MagickFalse;
9534
9535   excluding=MagickFalse;
9536
9537   for (source=0; source<1; source++)
9538   {
9539     if (source==0)
9540       {
9541        value=GetImageArtifact(image,"png:exclude-chunk");
9542
9543        if (value == NULL)
9544          value=GetImageArtifact(image,"png:exclude-chunks");
9545       }
9546     else
9547       {
9548        value=GetImageOption(image_info,"png:exclude-chunk");
9549
9550        if (value == NULL)
9551          value=GetImageOption(image_info,"png:exclude-chunks");
9552       }
9553
9554     if (value != NULL)
9555     {
9556
9557     size_t
9558       last;
9559
9560     excluding=MagickTrue;
9561
9562     if (logging != MagickFalse)
9563       {
9564         if (source == 0)
9565            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9566               "  png:exclude-chunk=%s found in image artifacts.\n", value);
9567         else
9568            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9569               "  png:exclude-chunk=%s found in image properties.\n", value);
9570       }
9571
9572     last=strlen(value);
9573
9574     for (i=0; i<(int) last; i+=5)
9575     {
9576
9577       if (LocaleNCompare(value+i,"all",3) == 0)
9578       {
9579         mng_info->ping_exclude_bKGD=MagickTrue;
9580         mng_info->ping_exclude_cHRM=MagickTrue;
9581         mng_info->ping_exclude_EXIF=MagickTrue;
9582         mng_info->ping_exclude_gAMA=MagickTrue;
9583         mng_info->ping_exclude_iCCP=MagickTrue;
9584         /* mng_info->ping_exclude_iTXt=MagickTrue; */
9585         mng_info->ping_exclude_oFFs=MagickTrue;
9586         mng_info->ping_exclude_pHYs=MagickTrue;
9587         mng_info->ping_exclude_sRGB=MagickTrue;
9588         mng_info->ping_exclude_tEXt=MagickTrue;
9589         mng_info->ping_exclude_tRNS=MagickTrue;
9590         mng_info->ping_exclude_vpAg=MagickTrue;
9591         mng_info->ping_exclude_zCCP=MagickTrue;
9592         mng_info->ping_exclude_zTXt=MagickTrue;
9593         i--;
9594       }
9595
9596       if (LocaleNCompare(value+i,"none",4) == 0)
9597       {
9598         mng_info->ping_exclude_bKGD=MagickFalse;
9599         mng_info->ping_exclude_cHRM=MagickFalse;
9600         mng_info->ping_exclude_EXIF=MagickFalse;
9601         mng_info->ping_exclude_gAMA=MagickFalse;
9602         mng_info->ping_exclude_iCCP=MagickFalse;
9603         /* mng_info->ping_exclude_iTXt=MagickFalse; */
9604         mng_info->ping_exclude_oFFs=MagickFalse;
9605         mng_info->ping_exclude_pHYs=MagickFalse;
9606         mng_info->ping_exclude_sRGB=MagickFalse;
9607         mng_info->ping_exclude_tEXt=MagickFalse;
9608         mng_info->ping_exclude_tRNS=MagickFalse;
9609         mng_info->ping_exclude_vpAg=MagickFalse;
9610         mng_info->ping_exclude_zCCP=MagickFalse;
9611         mng_info->ping_exclude_zTXt=MagickFalse;
9612       }
9613
9614       if (LocaleNCompare(value+i,"bkgd",4) == 0)
9615         mng_info->ping_exclude_bKGD=MagickTrue;
9616
9617       if (LocaleNCompare(value+i,"chrm",4) == 0)
9618         mng_info->ping_exclude_cHRM=MagickTrue;
9619
9620       if (LocaleNCompare(value+i,"exif",4) == 0)
9621         mng_info->ping_exclude_EXIF=MagickTrue;
9622
9623       if (LocaleNCompare(value+i,"gama",4) == 0)
9624         mng_info->ping_exclude_gAMA=MagickTrue;
9625
9626       if (LocaleNCompare(value+i,"iccp",4) == 0)
9627         mng_info->ping_exclude_iCCP=MagickTrue;
9628
9629     /*
9630       if (LocaleNCompare(value+i,"itxt",4) == 0)
9631         mng_info->ping_exclude_iTXt=MagickTrue;
9632      */
9633
9634       if (LocaleNCompare(value+i,"gama",4) == 0)
9635         mng_info->ping_exclude_gAMA=MagickTrue;
9636
9637       if (LocaleNCompare(value+i,"offs",4) == 0)
9638         mng_info->ping_exclude_oFFs=MagickTrue;
9639
9640       if (LocaleNCompare(value+i,"phys",4) == 0)
9641         mng_info->ping_exclude_pHYs=MagickTrue;
9642
9643       if (LocaleNCompare(value+i,"srgb",4) == 0)
9644         mng_info->ping_exclude_sRGB=MagickTrue;
9645
9646       if (LocaleNCompare(value+i,"text",4) == 0)
9647         mng_info->ping_exclude_tEXt=MagickTrue;
9648
9649       if (LocaleNCompare(value+i,"trns",4) == 0)
9650         mng_info->ping_exclude_tRNS=MagickTrue;
9651
9652       if (LocaleNCompare(value+i,"vpag",4) == 0)
9653         mng_info->ping_exclude_vpAg=MagickTrue;
9654
9655       if (LocaleNCompare(value+i,"zccp",4) == 0)
9656         mng_info->ping_exclude_zCCP=MagickTrue;
9657
9658       if (LocaleNCompare(value+i,"ztxt",4) == 0)
9659         mng_info->ping_exclude_zTXt=MagickTrue;
9660
9661       }
9662     }
9663   }
9664
9665   for (source=0; source<1; source++)
9666   {
9667     if (source==0)
9668       {
9669        value=GetImageArtifact(image,"png:include-chunk");
9670
9671        if (value == NULL)
9672          value=GetImageArtifact(image,"png:include-chunks");
9673       }
9674     else
9675       {
9676        value=GetImageOption(image_info,"png:include-chunk");
9677
9678        if (value == NULL)
9679          value=GetImageOption(image_info,"png:include-chunks");
9680       }
9681
9682     if (value != NULL)
9683     {
9684     size_t
9685       last;
9686
9687     excluding=MagickTrue;
9688
9689     if (logging != MagickFalse)
9690       {
9691         if (source == 0)
9692            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9693               "  png:include-chunk=%s found in image artifacts.\n", value);
9694         else
9695            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9696               "  png:include-chunk=%s found in image properties.\n", value);
9697       }
9698
9699     last=strlen(value);
9700
9701     for (i=0; i<(int) last; i+=5)
9702       {
9703       if (LocaleNCompare(value+i,"all",3) == 0)
9704         {
9705           mng_info->ping_exclude_bKGD=MagickFalse;
9706           mng_info->ping_exclude_cHRM=MagickFalse;
9707           mng_info->ping_exclude_EXIF=MagickFalse;
9708           mng_info->ping_exclude_gAMA=MagickFalse;
9709           mng_info->ping_exclude_iCCP=MagickFalse;
9710           /* mng_info->ping_exclude_iTXt=MagickFalse; */
9711           mng_info->ping_exclude_oFFs=MagickFalse;
9712           mng_info->ping_exclude_pHYs=MagickFalse;
9713           mng_info->ping_exclude_sRGB=MagickFalse;
9714           mng_info->ping_exclude_tEXt=MagickFalse;
9715           mng_info->ping_exclude_tRNS=MagickFalse;
9716           mng_info->ping_exclude_vpAg=MagickFalse;
9717           mng_info->ping_exclude_zCCP=MagickFalse;
9718           mng_info->ping_exclude_zTXt=MagickFalse;
9719           i--;
9720         }
9721
9722       if (LocaleNCompare(value+i,"none",4) == 0)
9723         {
9724           mng_info->ping_exclude_bKGD=MagickTrue;
9725           mng_info->ping_exclude_cHRM=MagickTrue;
9726           mng_info->ping_exclude_EXIF=MagickTrue;
9727           mng_info->ping_exclude_gAMA=MagickTrue;
9728           mng_info->ping_exclude_iCCP=MagickTrue;
9729           /* mng_info->ping_exclude_iTXt=MagickTrue; */
9730           mng_info->ping_exclude_oFFs=MagickTrue;
9731           mng_info->ping_exclude_pHYs=MagickTrue;
9732           mng_info->ping_exclude_sRGB=MagickTrue;
9733           mng_info->ping_exclude_tEXt=MagickTrue;
9734           mng_info->ping_exclude_tRNS=MagickTrue;
9735           mng_info->ping_exclude_vpAg=MagickTrue;
9736           mng_info->ping_exclude_zCCP=MagickTrue;
9737           mng_info->ping_exclude_zTXt=MagickTrue;
9738         }
9739
9740       if (LocaleNCompare(value+i,"bkgd",4) == 0)
9741         mng_info->ping_exclude_bKGD=MagickFalse;
9742
9743       if (LocaleNCompare(value+i,"chrm",4) == 0)
9744         mng_info->ping_exclude_cHRM=MagickFalse;
9745
9746       if (LocaleNCompare(value+i,"exif",4) == 0)
9747         mng_info->ping_exclude_EXIF=MagickFalse;
9748
9749       if (LocaleNCompare(value+i,"gama",4) == 0)
9750         mng_info->ping_exclude_gAMA=MagickFalse;
9751
9752       if (LocaleNCompare(value+i,"iccp",4) == 0)
9753         mng_info->ping_exclude_iCCP=MagickFalse;
9754
9755     /*
9756       if (LocaleNCompare(value+i,"itxt",4) == 0)
9757         mng_info->ping_exclude_iTXt=MagickFalse;
9758      */
9759
9760       if (LocaleNCompare(value+i,"gama",4) == 0)
9761         mng_info->ping_exclude_gAMA=MagickFalse;
9762
9763       if (LocaleNCompare(value+i,"offs",4) == 0)
9764         mng_info->ping_exclude_oFFs=MagickFalse;
9765
9766       if (LocaleNCompare(value+i,"phys",4) == 0)
9767         mng_info->ping_exclude_pHYs=MagickFalse;
9768
9769       if (LocaleNCompare(value+i,"srgb",4) == 0)
9770         mng_info->ping_exclude_sRGB=MagickFalse;
9771
9772       if (LocaleNCompare(value+i,"text",4) == 0)
9773         mng_info->ping_exclude_tEXt=MagickFalse;
9774
9775       if (LocaleNCompare(value+i,"trns",4) == 0)
9776         mng_info->ping_exclude_tRNS=MagickFalse;
9777
9778       if (LocaleNCompare(value+i,"vpag",4) == 0)
9779         mng_info->ping_exclude_vpAg=MagickFalse;
9780
9781       if (LocaleNCompare(value+i,"zccp",4) == 0)
9782         mng_info->ping_exclude_zCCP=MagickFalse;
9783
9784       if (LocaleNCompare(value+i,"ztxt",4) == 0)
9785         mng_info->ping_exclude_zTXt=MagickFalse;
9786
9787       }
9788     }
9789   }
9790
9791   if (excluding != MagickFalse && logging != MagickFalse)
9792   {
9793     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9794       "  Chunks to be excluded from the output PNG:");
9795     if (mng_info->ping_exclude_bKGD != MagickFalse)
9796       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9797           "    bKGD");
9798     if (mng_info->ping_exclude_cHRM != MagickFalse)
9799       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9800           "    cHRM");
9801     if (mng_info->ping_exclude_EXIF != MagickFalse)
9802       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9803           "    EXIF");
9804     if (mng_info->ping_exclude_gAMA != MagickFalse)
9805       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9806           "    gAMA");
9807     if (mng_info->ping_exclude_iCCP != MagickFalse)
9808       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9809           "    iCCP");
9810 /*
9811     if (mng_info->ping_exclude_iTXt != MagickFalse)
9812       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9813           "    iTXt");
9814 */
9815     if (mng_info->ping_exclude_oFFs != MagickFalse)
9816       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9817           "    oFFs");
9818     if (mng_info->ping_exclude_pHYs != MagickFalse)
9819       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9820           "    pHYs");
9821     if (mng_info->ping_exclude_sRGB != MagickFalse)
9822       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9823           "    sRGB");
9824     if (mng_info->ping_exclude_tEXt != MagickFalse)
9825       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9826           "    tEXt");
9827     if (mng_info->ping_exclude_tRNS != MagickFalse)
9828       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9829           "    tRNS");
9830     if (mng_info->ping_exclude_vpAg != MagickFalse)
9831       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9832           "    vpAg");
9833     if (mng_info->ping_exclude_zCCP != MagickFalse)
9834       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9835           "    zCCP");
9836     if (mng_info->ping_exclude_zTXt != MagickFalse)
9837       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9838           "    zTXt");
9839   }
9840
9841   mng_info->need_blob = MagickTrue;
9842
9843   status=WriteOnePNGImage(mng_info,image_info,image);
9844
9845   MngInfoFreeStruct(mng_info,&have_mng_structure);
9846
9847   if (logging != MagickFalse)
9848     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
9849
9850   return(status);
9851 }
9852
9853 #if defined(JNG_SUPPORTED)
9854
9855 /* Write one JNG image */
9856 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
9857    const ImageInfo *image_info,Image *image)
9858 {
9859   Image
9860     *jpeg_image;
9861
9862   ImageInfo
9863     *jpeg_image_info;
9864
9865   MagickBooleanType
9866     logging,
9867     status;
9868
9869   size_t
9870     length;
9871
9872   unsigned char
9873     *blob,
9874     chunk[80],
9875     *p;
9876
9877   unsigned int
9878     jng_alpha_compression_method,
9879     jng_alpha_sample_depth,
9880     jng_color_type,
9881     transparent;
9882
9883   size_t
9884     jng_quality;
9885
9886   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
9887     "  enter WriteOneJNGImage()");
9888
9889   blob=(unsigned char *) NULL;
9890   jpeg_image=(Image *) NULL;
9891   jpeg_image_info=(ImageInfo *) NULL;
9892
9893   status=MagickTrue;
9894   transparent=image_info->type==GrayscaleMatteType ||
9895      image_info->type==TrueColorMatteType;
9896   jng_color_type=10;
9897   jng_alpha_sample_depth=0;
9898   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
9899   jng_alpha_compression_method=0;
9900
9901   if (image->matte != MagickFalse)
9902     {
9903       /* if any pixels are transparent */
9904       transparent=MagickTrue;
9905       if (image_info->compression==JPEGCompression)
9906         jng_alpha_compression_method=8;
9907     }
9908
9909   if (transparent)
9910     {
9911       jng_color_type=14;
9912
9913       /* Create JPEG blob, image, and image_info */
9914       if (logging != MagickFalse)
9915         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9916           "  Creating jpeg_image_info for opacity.");
9917
9918       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
9919
9920       if (jpeg_image_info == (ImageInfo *) NULL)
9921         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9922
9923       if (logging != MagickFalse)
9924         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9925           "  Creating jpeg_image.");
9926
9927       jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
9928
9929       if (jpeg_image == (Image *) NULL)
9930         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9931
9932       (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
9933       status=SeparateImageChannel(jpeg_image,OpacityChannel);
9934       status=NegateImage(jpeg_image,MagickFalse);
9935       jpeg_image->matte=MagickFalse;
9936
9937       if (jng_quality >= 1000)
9938         jpeg_image_info->quality=jng_quality/1000;
9939
9940       else
9941         jpeg_image_info->quality=jng_quality;
9942
9943       jpeg_image_info->type=GrayscaleType;
9944       (void) SetImageType(jpeg_image,GrayscaleType);
9945       (void) AcquireUniqueFilename(jpeg_image->filename);
9946       (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
9947         "%s",jpeg_image->filename);
9948     }
9949
9950   /* To do: check bit depth of PNG alpha channel */
9951
9952   /* Check if image is grayscale. */
9953   if (image_info->type != TrueColorMatteType && image_info->type !=
9954     TrueColorType && ImageIsGray(image))
9955     jng_color_type-=2;
9956
9957   if (transparent)
9958     {
9959       if (jng_alpha_compression_method==0)
9960         {
9961           const char
9962             *value;
9963
9964           /* Encode opacity as a grayscale PNG blob */
9965           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
9966             &image->exception);
9967           if (logging != MagickFalse)
9968             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9969               "  Creating PNG blob.");
9970           length=0;
9971
9972           (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
9973           (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
9974           jpeg_image_info->interlace=NoInterlace;
9975
9976           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
9977             &image->exception);
9978
9979           /* Retrieve sample depth used */
9980           value=GetImageProperty(jpeg_image,"png:bit-depth-written");
9981           if (value != (char *) NULL)
9982             jng_alpha_sample_depth= (unsigned int) value[0];
9983         }
9984       else
9985         {
9986           /* Encode opacity as a grayscale JPEG blob */
9987
9988           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
9989             &image->exception);
9990
9991           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
9992           (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
9993           jpeg_image_info->interlace=NoInterlace;
9994           if (logging != MagickFalse)
9995             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9996               "  Creating blob.");
9997           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
9998            &image->exception);
9999           jng_alpha_sample_depth=8;
10000
10001           if (logging != MagickFalse)
10002             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10003               "  Successfully read jpeg_image into a blob, length=%.20g.",
10004               (double) length);
10005
10006         }
10007       /* Destroy JPEG image and image_info */
10008       jpeg_image=DestroyImage(jpeg_image);
10009       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10010       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10011     }
10012
10013   /* Write JHDR chunk */
10014   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
10015   PNGType(chunk,mng_JHDR);
10016   LogPNGChunk(logging,mng_JHDR,16L);
10017   PNGLong(chunk+4,(png_uint_32) image->columns);
10018   PNGLong(chunk+8,(png_uint_32) image->rows);
10019   chunk[12]=jng_color_type;
10020   chunk[13]=8;  /* sample depth */
10021   chunk[14]=8; /*jng_image_compression_method */
10022   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
10023   chunk[16]=jng_alpha_sample_depth;
10024   chunk[17]=jng_alpha_compression_method;
10025   chunk[18]=0; /*jng_alpha_filter_method */
10026   chunk[19]=0; /*jng_alpha_interlace_method */
10027   (void) WriteBlob(image,20,chunk);
10028   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
10029   if (logging != MagickFalse)
10030     {
10031       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10032         "    JNG width:%15lu",(unsigned long) image->columns);
10033
10034       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10035         "    JNG height:%14lu",(unsigned long) image->rows);
10036
10037       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10038         "    JNG color type:%10d",jng_color_type);
10039
10040       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10041         "    JNG sample depth:%8d",8);
10042
10043       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10044         "    JNG compression:%9d",8);
10045
10046       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10047         "    JNG interlace:%11d",0);
10048
10049       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10050         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
10051
10052       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10053         "    JNG alpha compression:%3d",jng_alpha_compression_method);
10054
10055       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10056         "    JNG alpha filter:%8d",0);
10057
10058       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10059         "    JNG alpha interlace:%5d",0);
10060     }
10061
10062   /* Write any JNG-chunk-b profiles */
10063   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
10064
10065   /*
10066      Write leading ancillary chunks
10067   */
10068
10069   if (transparent)
10070   {
10071     /*
10072       Write JNG bKGD chunk
10073     */
10074
10075     unsigned char
10076       blue,
10077       green,
10078       red;
10079
10080     ssize_t
10081       num_bytes;
10082
10083     if (jng_color_type == 8 || jng_color_type == 12)
10084       num_bytes=6L;
10085     else
10086       num_bytes=10L;
10087     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
10088     PNGType(chunk,mng_bKGD);
10089     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
10090     red=ScaleQuantumToChar(image->background_color.red);
10091     green=ScaleQuantumToChar(image->background_color.green);
10092     blue=ScaleQuantumToChar(image->background_color.blue);
10093     *(chunk+4)=0;
10094     *(chunk+5)=red;
10095     *(chunk+6)=0;
10096     *(chunk+7)=green;
10097     *(chunk+8)=0;
10098     *(chunk+9)=blue;
10099     (void) WriteBlob(image,(size_t) num_bytes,chunk);
10100     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
10101   }
10102
10103   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
10104     {
10105       /*
10106         Write JNG sRGB chunk
10107       */
10108       (void) WriteBlobMSBULong(image,1L);
10109       PNGType(chunk,mng_sRGB);
10110       LogPNGChunk(logging,mng_sRGB,1L);
10111
10112       if (image->rendering_intent != UndefinedIntent)
10113         chunk[4]=(unsigned char)
10114           Magick_RenderingIntent_to_PNG_RenderingIntent(
10115           (image->rendering_intent));
10116
10117       else
10118         chunk[4]=(unsigned char)
10119           Magick_RenderingIntent_to_PNG_RenderingIntent(
10120           (PerceptualIntent));
10121
10122       (void) WriteBlob(image,5,chunk);
10123       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10124     }
10125   else
10126     {
10127       if (image->gamma != 0.0)
10128         {
10129           /*
10130              Write JNG gAMA chunk
10131           */
10132           (void) WriteBlobMSBULong(image,4L);
10133           PNGType(chunk,mng_gAMA);
10134           LogPNGChunk(logging,mng_gAMA,4L);
10135           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10136           (void) WriteBlob(image,8,chunk);
10137           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10138         }
10139
10140       if ((mng_info->equal_chrms == MagickFalse) &&
10141           (image->chromaticity.red_primary.x != 0.0))
10142         {
10143           PrimaryInfo
10144             primary;
10145
10146           /*
10147              Write JNG cHRM chunk
10148           */
10149           (void) WriteBlobMSBULong(image,32L);
10150           PNGType(chunk,mng_cHRM);
10151           LogPNGChunk(logging,mng_cHRM,32L);
10152           primary=image->chromaticity.white_point;
10153           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
10154           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
10155           primary=image->chromaticity.red_primary;
10156           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
10157           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
10158           primary=image->chromaticity.green_primary;
10159           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
10160           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
10161           primary=image->chromaticity.blue_primary;
10162           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
10163           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
10164           (void) WriteBlob(image,36,chunk);
10165           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
10166         }
10167     }
10168
10169   if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
10170     {
10171       /*
10172          Write JNG pHYs chunk
10173       */
10174       (void) WriteBlobMSBULong(image,9L);
10175       PNGType(chunk,mng_pHYs);
10176       LogPNGChunk(logging,mng_pHYs,9L);
10177       if (image->units == PixelsPerInchResolution)
10178         {
10179           PNGLong(chunk+4,(png_uint_32)
10180             (image->x_resolution*100.0/2.54+0.5));
10181
10182           PNGLong(chunk+8,(png_uint_32)
10183             (image->y_resolution*100.0/2.54+0.5));
10184
10185           chunk[12]=1;
10186         }
10187
10188       else
10189         {
10190           if (image->units == PixelsPerCentimeterResolution)
10191             {
10192               PNGLong(chunk+4,(png_uint_32)
10193                 (image->x_resolution*100.0+0.5));
10194
10195               PNGLong(chunk+8,(png_uint_32)
10196                 (image->y_resolution*100.0+0.5));
10197
10198               chunk[12]=1;
10199             }
10200
10201           else
10202             {
10203               PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
10204               PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
10205               chunk[12]=0;
10206             }
10207         }
10208       (void) WriteBlob(image,13,chunk);
10209       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10210     }
10211
10212   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
10213     {
10214       /*
10215          Write JNG oFFs chunk
10216       */
10217       (void) WriteBlobMSBULong(image,9L);
10218       PNGType(chunk,mng_oFFs);
10219       LogPNGChunk(logging,mng_oFFs,9L);
10220       PNGsLong(chunk+4,(ssize_t) (image->page.x));
10221       PNGsLong(chunk+8,(ssize_t) (image->page.y));
10222       chunk[12]=0;
10223       (void) WriteBlob(image,13,chunk);
10224       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10225     }
10226   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
10227     {
10228        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
10229        PNGType(chunk,mng_vpAg);
10230        LogPNGChunk(logging,mng_vpAg,9L);
10231        PNGLong(chunk+4,(png_uint_32) image->page.width);
10232        PNGLong(chunk+8,(png_uint_32) image->page.height);
10233        chunk[12]=0;   /* unit = pixels */
10234        (void) WriteBlob(image,13,chunk);
10235        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10236     }
10237
10238
10239   if (transparent)
10240     {
10241       if (jng_alpha_compression_method==0)
10242         {
10243           register ssize_t
10244             i;
10245
10246           ssize_t
10247             len;
10248
10249           /* Write IDAT chunk header */
10250           if (logging != MagickFalse)
10251             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10252               "  Write IDAT chunks from blob, length=%.20g.",(double)
10253               length);
10254
10255           /* Copy IDAT chunks */
10256           len=0;
10257           p=blob+8;
10258           for (i=8; i<(ssize_t) length; i+=len+12)
10259           {
10260             len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
10261             p+=4;
10262
10263             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
10264               {
10265                 /* Found an IDAT chunk. */
10266                 (void) WriteBlobMSBULong(image,(size_t) len);
10267                 LogPNGChunk(logging,mng_IDAT,(size_t) len);
10268                 (void) WriteBlob(image,(size_t) len+4,p);
10269                 (void) WriteBlobMSBULong(image,
10270                     crc32(0,p,(uInt) len+4));
10271               }
10272
10273             else
10274               {
10275                 if (logging != MagickFalse)
10276                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10277                     "    Skipping %c%c%c%c chunk, length=%.20g.",
10278                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
10279               }
10280             p+=(8+len);
10281           }
10282         }
10283       else
10284         {
10285           /* Write JDAA chunk header */
10286           if (logging != MagickFalse)
10287             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10288               "  Write JDAA chunk, length=%.20g.",(double) length);
10289           (void) WriteBlobMSBULong(image,(size_t) length);
10290           PNGType(chunk,mng_JDAA);
10291           LogPNGChunk(logging,mng_JDAA,length);
10292           /* Write JDAT chunk(s) data */
10293           (void) WriteBlob(image,4,chunk);
10294           (void) WriteBlob(image,length,blob);
10295           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
10296              (uInt) length));
10297         }
10298       blob=(unsigned char *) RelinquishMagickMemory(blob);
10299     }
10300
10301   /* Encode image as a JPEG blob */
10302   if (logging != MagickFalse)
10303     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10304       "  Creating jpeg_image_info.");
10305   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
10306   if (jpeg_image_info == (ImageInfo *) NULL)
10307     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10308
10309   if (logging != MagickFalse)
10310     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10311       "  Creating jpeg_image.");
10312
10313   jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10314   if (jpeg_image == (Image *) NULL)
10315     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10316   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10317
10318   (void) AcquireUniqueFilename(jpeg_image->filename);
10319   (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
10320     jpeg_image->filename);
10321
10322   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
10323     &image->exception);
10324
10325   if (logging != MagickFalse)
10326     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10327       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
10328       (double) jpeg_image->rows);
10329
10330   if (jng_color_type == 8 || jng_color_type == 12)
10331     jpeg_image_info->type=GrayscaleType;
10332
10333   jpeg_image_info->quality=jng_quality % 1000;
10334   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
10335   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
10336
10337   if (logging != MagickFalse)
10338     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10339       "  Creating blob.");
10340
10341   blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
10342
10343   if (logging != MagickFalse)
10344     {
10345       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10346         "  Successfully read jpeg_image into a blob, length=%.20g.",
10347         (double) length);
10348
10349       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10350         "  Write JDAT chunk, length=%.20g.",(double) length);
10351     }
10352
10353   /* Write JDAT chunk(s) */
10354   (void) WriteBlobMSBULong(image,(size_t) length);
10355   PNGType(chunk,mng_JDAT);
10356   LogPNGChunk(logging,mng_JDAT,length);
10357   (void) WriteBlob(image,4,chunk);
10358   (void) WriteBlob(image,length,blob);
10359   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
10360
10361   jpeg_image=DestroyImage(jpeg_image);
10362   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
10363   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
10364   blob=(unsigned char *) RelinquishMagickMemory(blob);
10365
10366   /* Write any JNG-chunk-e profiles */
10367   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
10368
10369   /* Write IEND chunk */
10370   (void) WriteBlobMSBULong(image,0L);
10371   PNGType(chunk,mng_IEND);
10372   LogPNGChunk(logging,mng_IEND,0);
10373   (void) WriteBlob(image,4,chunk);
10374   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
10375
10376   if (logging != MagickFalse)
10377     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10378       "  exit WriteOneJNGImage()");
10379
10380   return(status);
10381 }
10382
10383
10384 /*
10385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10386 %                                                                             %
10387 %                                                                             %
10388 %                                                                             %
10389 %   W r i t e J N G I m a g e                                                 %
10390 %                                                                             %
10391 %                                                                             %
10392 %                                                                             %
10393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10394 %
10395 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
10396 %
10397 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
10398 %
10399 %  The format of the WriteJNGImage method is:
10400 %
10401 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10402 %
10403 %  A description of each parameter follows:
10404 %
10405 %    o image_info: the image info.
10406 %
10407 %    o image:  The image.
10408 %
10409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10410 */
10411 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
10412 {
10413   MagickBooleanType
10414     have_mng_structure,
10415     logging,
10416     status;
10417
10418   MngInfo
10419     *mng_info;
10420
10421   /*
10422     Open image file.
10423   */
10424   assert(image_info != (const ImageInfo *) NULL);
10425   assert(image_info->signature == MagickSignature);
10426   assert(image != (Image *) NULL);
10427   assert(image->signature == MagickSignature);
10428   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10429   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteJNGImage()");
10430   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
10431   if (status == MagickFalse)
10432     return(status);
10433
10434   /*
10435     Allocate a MngInfo structure.
10436   */
10437   have_mng_structure=MagickFalse;
10438   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10439   if (mng_info == (MngInfo *) NULL)
10440     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10441   /*
10442     Initialize members of the MngInfo structure.
10443   */
10444   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10445   mng_info->image=image;
10446   have_mng_structure=MagickTrue;
10447
10448   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
10449
10450   status=WriteOneJNGImage(mng_info,image_info,image);
10451   (void) CloseBlob(image);
10452
10453   (void) CatchImageException(image);
10454   MngInfoFreeStruct(mng_info,&have_mng_structure);
10455   if (logging != MagickFalse)
10456     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
10457   return(status);
10458 }
10459 #endif
10460
10461
10462
10463 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
10464 {
10465   const char
10466     *option;
10467
10468   Image
10469     *next_image;
10470
10471   MagickBooleanType
10472     have_mng_structure,
10473     status;
10474
10475   volatile MagickBooleanType
10476     logging;
10477
10478   MngInfo
10479     *mng_info;
10480
10481   int
10482     image_count,
10483     need_iterations,
10484     need_matte;
10485
10486   volatile int
10487 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
10488     defined(PNG_MNG_FEATURES_SUPPORTED)
10489     need_local_plte,
10490 #endif
10491     all_images_are_gray,
10492     need_defi,
10493     use_global_plte;
10494
10495   register ssize_t
10496     i;
10497
10498   unsigned char
10499     chunk[800];
10500
10501   volatile unsigned int
10502     write_jng,
10503     write_mng;
10504
10505   volatile size_t
10506     scene;
10507
10508   size_t
10509     final_delay=0,
10510     initial_delay;
10511
10512 #if (PNG_LIBPNG_VER < 10200)
10513     if (image_info->verbose)
10514       printf("Your PNG library (libpng-%s) is rather old.\n",
10515          PNG_LIBPNG_VER_STRING);
10516 #endif
10517
10518   /*
10519     Open image file.
10520   */
10521   assert(image_info != (const ImageInfo *) NULL);
10522   assert(image_info->signature == MagickSignature);
10523   assert(image != (Image *) NULL);
10524   assert(image->signature == MagickSignature);
10525   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10526   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteMNGImage()");
10527   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
10528   if (status == MagickFalse)
10529     return(status);
10530
10531   /*
10532     Allocate a MngInfo structure.
10533   */
10534   have_mng_structure=MagickFalse;
10535   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10536   if (mng_info == (MngInfo *) NULL)
10537     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10538   /*
10539     Initialize members of the MngInfo structure.
10540   */
10541   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10542   mng_info->image=image;
10543   have_mng_structure=MagickTrue;
10544   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
10545
10546   /*
10547    * See if user has requested a specific PNG subformat to be used
10548    * for all of the PNGs in the MNG being written, e.g.,
10549    *
10550    *    convert *.png png8:animation.mng
10551    *
10552    * To do: check -define png:bit_depth and png:color_type as well,
10553    * or perhaps use mng:bit_depth and mng:color_type instead for
10554    * global settings.
10555    */
10556
10557   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10558   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10559   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10560
10561   write_jng=MagickFalse;
10562   if (image_info->compression == JPEGCompression)
10563     write_jng=MagickTrue;
10564
10565   mng_info->adjoin=image_info->adjoin &&
10566     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
10567
10568   if (logging != MagickFalse)
10569     {
10570       /* Log some info about the input */
10571       Image
10572         *p;
10573
10574       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10575         "  Checking input image(s)");
10576
10577       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10578         "    Image_info depth: %.20g",(double) image_info->depth);
10579
10580       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10581         "    Type: %d",image_info->type);
10582
10583       scene=0;
10584       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
10585       {
10586         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10587           "    Scene: %.20g",(double) scene++);
10588
10589         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10590           "      Image depth: %.20g",(double) p->depth);
10591
10592         if (p->matte)
10593           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10594             "      Matte: True");
10595
10596         else
10597           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10598             "      Matte: False");
10599
10600         if (p->storage_class == PseudoClass)
10601           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10602             "      Storage class: PseudoClass");
10603
10604         else
10605           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10606             "      Storage class: DirectClass");
10607
10608         if (p->colors)
10609           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10610             "      Number of colors: %.20g",(double) p->colors);
10611
10612         else
10613           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10614             "      Number of colors: unspecified");
10615
10616         if (mng_info->adjoin == MagickFalse)
10617           break;
10618       }
10619     }
10620
10621   use_global_plte=MagickFalse;
10622   all_images_are_gray=MagickFalse;
10623 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
10624   need_local_plte=MagickTrue;
10625 #endif
10626   need_defi=MagickFalse;
10627   need_matte=MagickFalse;
10628   mng_info->framing_mode=1;
10629   mng_info->old_framing_mode=1;
10630
10631   if (write_mng)
10632       if (image_info->page != (char *) NULL)
10633         {
10634           /*
10635             Determine image bounding box.
10636           */
10637           SetGeometry(image,&mng_info->page);
10638           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
10639             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
10640         }
10641   if (write_mng)
10642     {
10643       unsigned int
10644         need_geom;
10645
10646       unsigned short
10647         red,
10648         green,
10649         blue;
10650
10651       mng_info->page=image->page;
10652       need_geom=MagickTrue;
10653       if (mng_info->page.width || mng_info->page.height)
10654          need_geom=MagickFalse;
10655       /*
10656         Check all the scenes.
10657       */
10658       initial_delay=image->delay;
10659       need_iterations=MagickFalse;
10660       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
10661       mng_info->equal_physs=MagickTrue,
10662       mng_info->equal_gammas=MagickTrue;
10663       mng_info->equal_srgbs=MagickTrue;
10664       mng_info->equal_backgrounds=MagickTrue;
10665       image_count=0;
10666 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
10667     defined(PNG_MNG_FEATURES_SUPPORTED)
10668       all_images_are_gray=MagickTrue;
10669       mng_info->equal_palettes=MagickFalse;
10670       need_local_plte=MagickFalse;
10671 #endif
10672       for (next_image=image; next_image != (Image *) NULL; )
10673       {
10674         if (need_geom)
10675           {
10676             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
10677               mng_info->page.width=next_image->columns+next_image->page.x;
10678
10679             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
10680               mng_info->page.height=next_image->rows+next_image->page.y;
10681           }
10682
10683         if (next_image->page.x || next_image->page.y)
10684           need_defi=MagickTrue;
10685
10686         if (next_image->matte)
10687           need_matte=MagickTrue;
10688
10689         if ((int) next_image->dispose >= BackgroundDispose)
10690           if (next_image->matte || next_image->page.x || next_image->page.y ||
10691               ((next_image->columns < mng_info->page.width) &&
10692                (next_image->rows < mng_info->page.height)))
10693             mng_info->need_fram=MagickTrue;
10694
10695         if (next_image->iterations)
10696           need_iterations=MagickTrue;
10697
10698         final_delay=next_image->delay;
10699
10700         if (final_delay != initial_delay || final_delay > 1UL*
10701            next_image->ticks_per_second)
10702           mng_info->need_fram=1;
10703
10704 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
10705     defined(PNG_MNG_FEATURES_SUPPORTED)
10706         /*
10707           check for global palette possibility.
10708         */
10709         if (image->matte != MagickFalse)
10710            need_local_plte=MagickTrue;
10711
10712         if (need_local_plte == 0)
10713           {
10714             if (ImageIsGray(image) == MagickFalse)
10715               all_images_are_gray=MagickFalse;
10716             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
10717             if (use_global_plte == 0)
10718               use_global_plte=mng_info->equal_palettes;
10719             need_local_plte=!mng_info->equal_palettes;
10720           }
10721 #endif
10722         if (GetNextImageInList(next_image) != (Image *) NULL)
10723           {
10724             if (next_image->background_color.red !=
10725                 next_image->next->background_color.red ||
10726                 next_image->background_color.green !=
10727                 next_image->next->background_color.green ||
10728                 next_image->background_color.blue !=
10729                 next_image->next->background_color.blue)
10730               mng_info->equal_backgrounds=MagickFalse;
10731
10732             if (next_image->gamma != next_image->next->gamma)
10733               mng_info->equal_gammas=MagickFalse;
10734
10735             if (next_image->rendering_intent !=
10736                 next_image->next->rendering_intent)
10737               mng_info->equal_srgbs=MagickFalse;
10738
10739             if ((next_image->units != next_image->next->units) ||
10740                 (next_image->x_resolution != next_image->next->x_resolution) ||
10741                 (next_image->y_resolution != next_image->next->y_resolution))
10742               mng_info->equal_physs=MagickFalse;
10743
10744             if (mng_info->equal_chrms)
10745               {
10746                 if (next_image->chromaticity.red_primary.x !=
10747                     next_image->next->chromaticity.red_primary.x ||
10748                     next_image->chromaticity.red_primary.y !=
10749                     next_image->next->chromaticity.red_primary.y ||
10750                     next_image->chromaticity.green_primary.x !=
10751                     next_image->next->chromaticity.green_primary.x ||
10752                     next_image->chromaticity.green_primary.y !=
10753                     next_image->next->chromaticity.green_primary.y ||
10754                     next_image->chromaticity.blue_primary.x !=
10755                     next_image->next->chromaticity.blue_primary.x ||
10756                     next_image->chromaticity.blue_primary.y !=
10757                     next_image->next->chromaticity.blue_primary.y ||
10758                     next_image->chromaticity.white_point.x !=
10759                     next_image->next->chromaticity.white_point.x ||
10760                     next_image->chromaticity.white_point.y !=
10761                     next_image->next->chromaticity.white_point.y)
10762                   mng_info->equal_chrms=MagickFalse;
10763               }
10764           }
10765         image_count++;
10766         next_image=GetNextImageInList(next_image);
10767       }
10768       if (image_count < 2)
10769         {
10770           mng_info->equal_backgrounds=MagickFalse;
10771           mng_info->equal_chrms=MagickFalse;
10772           mng_info->equal_gammas=MagickFalse;
10773           mng_info->equal_srgbs=MagickFalse;
10774           mng_info->equal_physs=MagickFalse;
10775           use_global_plte=MagickFalse;
10776 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
10777           need_local_plte=MagickTrue;
10778 #endif
10779           need_iterations=MagickFalse;
10780         }
10781
10782      if (mng_info->need_fram == MagickFalse)
10783        {
10784          /*
10785            Only certain framing rates 100/n are exactly representable without
10786            the FRAM chunk but we'll allow some slop in VLC files
10787          */
10788          if (final_delay == 0)
10789            {
10790              if (need_iterations != MagickFalse)
10791                {
10792                  /*
10793                    It's probably a GIF with loop; don't run it *too* fast.
10794                  */
10795                  if (mng_info->adjoin)
10796                    {
10797                      final_delay=10;
10798                      (void) ThrowMagickException(&image->exception,
10799                         GetMagickModule(),CoderWarning,
10800                        "input has zero delay between all frames; assuming",
10801                        " 10 cs `%s'","");
10802                    }
10803                }
10804              else
10805                mng_info->ticks_per_second=0;
10806            }
10807          if (final_delay != 0)
10808            mng_info->ticks_per_second=(png_uint_32)
10809               (image->ticks_per_second/final_delay);
10810          if (final_delay > 50)
10811            mng_info->ticks_per_second=2;
10812
10813          if (final_delay > 75)
10814            mng_info->ticks_per_second=1;
10815
10816          if (final_delay > 125)
10817            mng_info->need_fram=MagickTrue;
10818
10819          if (need_defi && final_delay > 2 && (final_delay != 4) &&
10820             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
10821             (final_delay != 25) && (final_delay != 50) && (final_delay !=
10822                1UL*image->ticks_per_second))
10823            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
10824        }
10825
10826      if (mng_info->need_fram != MagickFalse)
10827         mng_info->ticks_per_second=1UL*image->ticks_per_second;
10828      /*
10829         If pseudocolor, we should also check to see if all the
10830         palettes are identical and write a global PLTE if they are.
10831         ../glennrp Feb 99.
10832      */
10833      /*
10834         Write the MNG version 1.0 signature and MHDR chunk.
10835      */
10836      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
10837      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
10838      PNGType(chunk,mng_MHDR);
10839      LogPNGChunk(logging,mng_MHDR,28L);
10840      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
10841      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
10842      PNGLong(chunk+12,mng_info->ticks_per_second);
10843      PNGLong(chunk+16,0L);  /* layer count=unknown */
10844      PNGLong(chunk+20,0L);  /* frame count=unknown */
10845      PNGLong(chunk+24,0L);  /* play time=unknown   */
10846      if (write_jng)
10847        {
10848          if (need_matte)
10849            {
10850              if (need_defi || mng_info->need_fram || use_global_plte)
10851                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
10852
10853              else
10854                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
10855            }
10856
10857          else
10858            {
10859              if (need_defi || mng_info->need_fram || use_global_plte)
10860                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
10861
10862              else
10863                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
10864            }
10865        }
10866
10867      else
10868        {
10869          if (need_matte)
10870            {
10871              if (need_defi || mng_info->need_fram || use_global_plte)
10872                PNGLong(chunk+28,11L);    /* simplicity=LC */
10873
10874              else
10875                PNGLong(chunk+28,9L);    /* simplicity=VLC */
10876            }
10877
10878          else
10879            {
10880              if (need_defi || mng_info->need_fram || use_global_plte)
10881                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
10882
10883              else
10884                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
10885            }
10886        }
10887      (void) WriteBlob(image,32,chunk);
10888      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
10889      option=GetImageOption(image_info,"mng:need-cacheoff");
10890      if (option != (const char *) NULL)
10891        {
10892          size_t
10893            length;
10894
10895          /*
10896            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
10897          */
10898          PNGType(chunk,mng_nEED);
10899          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
10900          (void) WriteBlobMSBULong(image,(size_t) length);
10901          LogPNGChunk(logging,mng_nEED,(size_t) length);
10902          length+=4;
10903          (void) WriteBlob(image,length,chunk);
10904          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
10905        }
10906      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
10907          (GetNextImageInList(image) != (Image *) NULL) &&
10908          (image->iterations != 1))
10909        {
10910          /*
10911            Write MNG TERM chunk
10912          */
10913          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
10914          PNGType(chunk,mng_TERM);
10915          LogPNGChunk(logging,mng_TERM,10L);
10916          chunk[4]=3;  /* repeat animation */
10917          chunk[5]=0;  /* show last frame when done */
10918          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
10919             final_delay/MagickMax(image->ticks_per_second,1)));
10920
10921          if (image->iterations == 0)
10922            PNGLong(chunk+10,PNG_UINT_31_MAX);
10923
10924          else
10925            PNGLong(chunk+10,(png_uint_32) image->iterations);
10926
10927          if (logging != MagickFalse)
10928            {
10929              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10930                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
10931               final_delay/MagickMax(image->ticks_per_second,1)));
10932
10933              if (image->iterations == 0)
10934                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10935                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
10936
10937              else
10938                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10939                  "     Image iterations: %.20g",(double) image->iterations);
10940            }
10941          (void) WriteBlob(image,14,chunk);
10942          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
10943        }
10944      /*
10945        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10946      */
10947      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
10948           mng_info->equal_srgbs)
10949        {
10950          /*
10951            Write MNG sRGB chunk
10952          */
10953          (void) WriteBlobMSBULong(image,1L);
10954          PNGType(chunk,mng_sRGB);
10955          LogPNGChunk(logging,mng_sRGB,1L);
10956
10957          if (image->rendering_intent != UndefinedIntent)
10958            chunk[4]=(unsigned char)
10959              Magick_RenderingIntent_to_PNG_RenderingIntent(
10960              (image->rendering_intent));
10961
10962          else
10963            chunk[4]=(unsigned char)
10964              Magick_RenderingIntent_to_PNG_RenderingIntent(
10965                (PerceptualIntent));
10966
10967          (void) WriteBlob(image,5,chunk);
10968          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
10969          mng_info->have_write_global_srgb=MagickTrue;
10970        }
10971
10972      else
10973        {
10974          if (image->gamma && mng_info->equal_gammas)
10975            {
10976              /*
10977                 Write MNG gAMA chunk
10978              */
10979              (void) WriteBlobMSBULong(image,4L);
10980              PNGType(chunk,mng_gAMA);
10981              LogPNGChunk(logging,mng_gAMA,4L);
10982              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
10983              (void) WriteBlob(image,8,chunk);
10984              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
10985              mng_info->have_write_global_gama=MagickTrue;
10986            }
10987          if (mng_info->equal_chrms)
10988            {
10989              PrimaryInfo
10990                primary;
10991
10992              /*
10993                 Write MNG cHRM chunk
10994              */
10995              (void) WriteBlobMSBULong(image,32L);
10996              PNGType(chunk,mng_cHRM);
10997              LogPNGChunk(logging,mng_cHRM,32L);
10998              primary=image->chromaticity.white_point;
10999              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11000              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11001              primary=image->chromaticity.red_primary;
11002              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11003              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11004              primary=image->chromaticity.green_primary;
11005              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11006              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11007              primary=image->chromaticity.blue_primary;
11008              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11009              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11010              (void) WriteBlob(image,36,chunk);
11011              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11012              mng_info->have_write_global_chrm=MagickTrue;
11013            }
11014        }
11015      if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
11016        {
11017          /*
11018             Write MNG pHYs chunk
11019          */
11020          (void) WriteBlobMSBULong(image,9L);
11021          PNGType(chunk,mng_pHYs);
11022          LogPNGChunk(logging,mng_pHYs,9L);
11023
11024          if (image->units == PixelsPerInchResolution)
11025            {
11026              PNGLong(chunk+4,(png_uint_32)
11027                (image->x_resolution*100.0/2.54+0.5));
11028
11029              PNGLong(chunk+8,(png_uint_32)
11030                (image->y_resolution*100.0/2.54+0.5));
11031
11032              chunk[12]=1;
11033            }
11034
11035          else
11036            {
11037              if (image->units == PixelsPerCentimeterResolution)
11038                {
11039                  PNGLong(chunk+4,(png_uint_32)
11040                    (image->x_resolution*100.0+0.5));
11041
11042                  PNGLong(chunk+8,(png_uint_32)
11043                    (image->y_resolution*100.0+0.5));
11044
11045                  chunk[12]=1;
11046                }
11047
11048              else
11049                {
11050                  PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11051                  PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11052                  chunk[12]=0;
11053                }
11054            }
11055          (void) WriteBlob(image,13,chunk);
11056          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11057        }
11058      /*
11059        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
11060        or does not cover the entire frame.
11061      */
11062      if (write_mng && (image->matte || image->page.x > 0 ||
11063          image->page.y > 0 || (image->page.width &&
11064          (image->page.width+image->page.x < mng_info->page.width))
11065          || (image->page.height && (image->page.height+image->page.y
11066          < mng_info->page.height))))
11067        {
11068          (void) WriteBlobMSBULong(image,6L);
11069          PNGType(chunk,mng_BACK);
11070          LogPNGChunk(logging,mng_BACK,6L);
11071          red=ScaleQuantumToShort(image->background_color.red);
11072          green=ScaleQuantumToShort(image->background_color.green);
11073          blue=ScaleQuantumToShort(image->background_color.blue);
11074          PNGShort(chunk+4,red);
11075          PNGShort(chunk+6,green);
11076          PNGShort(chunk+8,blue);
11077          (void) WriteBlob(image,10,chunk);
11078          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11079          if (mng_info->equal_backgrounds)
11080            {
11081              (void) WriteBlobMSBULong(image,6L);
11082              PNGType(chunk,mng_bKGD);
11083              LogPNGChunk(logging,mng_bKGD,6L);
11084              (void) WriteBlob(image,10,chunk);
11085              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
11086            }
11087        }
11088
11089 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
11090      if ((need_local_plte == MagickFalse) &&
11091          (image->storage_class == PseudoClass) &&
11092          (all_images_are_gray == MagickFalse))
11093        {
11094          size_t
11095            data_length;
11096
11097          /*
11098            Write MNG PLTE chunk
11099          */
11100          data_length=3*image->colors;
11101          (void) WriteBlobMSBULong(image,data_length);
11102          PNGType(chunk,mng_PLTE);
11103          LogPNGChunk(logging,mng_PLTE,data_length);
11104
11105          for (i=0; i < (ssize_t) image->colors; i++)
11106          {
11107            chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
11108            chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
11109            chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
11110          }
11111
11112          (void) WriteBlob(image,data_length+4,chunk);
11113          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
11114          mng_info->have_write_global_plte=MagickTrue;
11115        }
11116 #endif
11117     }
11118   scene=0;
11119   mng_info->delay=0;
11120 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11121     defined(PNG_MNG_FEATURES_SUPPORTED)
11122   mng_info->equal_palettes=MagickFalse;
11123 #endif
11124   do
11125   {
11126     if (mng_info->adjoin)
11127     {
11128 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11129     defined(PNG_MNG_FEATURES_SUPPORTED)
11130     /*
11131       If we aren't using a global palette for the entire MNG, check to
11132       see if we can use one for two or more consecutive images.
11133     */
11134     if (need_local_plte && use_global_plte && !all_images_are_gray)
11135       {
11136         if (mng_info->IsPalette)
11137           {
11138             /*
11139               When equal_palettes is true, this image has the same palette
11140               as the previous PseudoClass image
11141             */
11142             mng_info->have_write_global_plte=mng_info->equal_palettes;
11143             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
11144             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
11145               {
11146                 /*
11147                   Write MNG PLTE chunk
11148                 */
11149                 size_t
11150                   data_length;
11151
11152                 data_length=3*image->colors;
11153                 (void) WriteBlobMSBULong(image,data_length);
11154                 PNGType(chunk,mng_PLTE);
11155                 LogPNGChunk(logging,mng_PLTE,data_length);
11156
11157                 for (i=0; i < (ssize_t) image->colors; i++)
11158                 {
11159                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
11160                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
11161                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
11162                 }
11163
11164                 (void) WriteBlob(image,data_length+4,chunk);
11165                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
11166                    (uInt) (data_length+4)));
11167                 mng_info->have_write_global_plte=MagickTrue;
11168               }
11169           }
11170         else
11171           mng_info->have_write_global_plte=MagickFalse;
11172       }
11173 #endif
11174     if (need_defi)
11175       {
11176         ssize_t
11177           previous_x,
11178           previous_y;
11179
11180         if (scene)
11181           {
11182             previous_x=mng_info->page.x;
11183             previous_y=mng_info->page.y;
11184           }
11185         else
11186           {
11187             previous_x=0;
11188             previous_y=0;
11189           }
11190         mng_info->page=image->page;
11191         if ((mng_info->page.x !=  previous_x) ||
11192             (mng_info->page.y != previous_y))
11193           {
11194              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
11195              PNGType(chunk,mng_DEFI);
11196              LogPNGChunk(logging,mng_DEFI,12L);
11197              chunk[4]=0; /* object 0 MSB */
11198              chunk[5]=0; /* object 0 LSB */
11199              chunk[6]=0; /* visible  */
11200              chunk[7]=0; /* abstract */
11201              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
11202              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
11203              (void) WriteBlob(image,16,chunk);
11204              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
11205           }
11206       }
11207     }
11208
11209    mng_info->write_mng=write_mng;
11210
11211    if ((int) image->dispose >= 3)
11212      mng_info->framing_mode=3;
11213
11214    if (mng_info->need_fram && mng_info->adjoin &&
11215        ((image->delay != mng_info->delay) ||
11216         (mng_info->framing_mode != mng_info->old_framing_mode)))
11217      {
11218        if (image->delay == mng_info->delay)
11219          {
11220            /*
11221              Write a MNG FRAM chunk with the new framing mode.
11222            */
11223            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
11224            PNGType(chunk,mng_FRAM);
11225            LogPNGChunk(logging,mng_FRAM,1L);
11226            chunk[4]=(unsigned char) mng_info->framing_mode;
11227            (void) WriteBlob(image,5,chunk);
11228            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11229          }
11230        else
11231          {
11232            /*
11233              Write a MNG FRAM chunk with the delay.
11234            */
11235            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
11236            PNGType(chunk,mng_FRAM);
11237            LogPNGChunk(logging,mng_FRAM,10L);
11238            chunk[4]=(unsigned char) mng_info->framing_mode;
11239            chunk[5]=0;  /* frame name separator (no name) */
11240            chunk[6]=2;  /* flag for changing default delay */
11241            chunk[7]=0;  /* flag for changing frame timeout */
11242            chunk[8]=0;  /* flag for changing frame clipping */
11243            chunk[9]=0;  /* flag for changing frame sync_id */
11244            PNGLong(chunk+10,(png_uint_32)
11245              ((mng_info->ticks_per_second*
11246              image->delay)/MagickMax(image->ticks_per_second,1)));
11247            (void) WriteBlob(image,14,chunk);
11248            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
11249            mng_info->delay=(png_uint_32) image->delay;
11250          }
11251        mng_info->old_framing_mode=mng_info->framing_mode;
11252      }
11253
11254 #if defined(JNG_SUPPORTED)
11255    if (image_info->compression == JPEGCompression)
11256      {
11257        ImageInfo
11258          *write_info;
11259
11260        if (logging != MagickFalse)
11261          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11262            "  Writing JNG object.");
11263        /* To do: specify the desired alpha compression method. */
11264        write_info=CloneImageInfo(image_info);
11265        write_info->compression=UndefinedCompression;
11266        status=WriteOneJNGImage(mng_info,write_info,image);
11267        write_info=DestroyImageInfo(write_info);
11268      }
11269    else
11270 #endif
11271      {
11272        if (logging != MagickFalse)
11273          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11274            "  Writing PNG object.");
11275
11276        mng_info->need_blob = MagickFalse;
11277
11278        /* We don't want any ancillary chunks written */
11279        mng_info->ping_exclude_bKGD=MagickTrue;
11280        mng_info->ping_exclude_cHRM=MagickTrue;
11281        mng_info->ping_exclude_EXIF=MagickTrue;
11282        mng_info->ping_exclude_gAMA=MagickTrue;
11283        mng_info->ping_exclude_cHRM=MagickTrue;
11284        mng_info->ping_exclude_iCCP=MagickTrue;
11285        /* mng_info->ping_exclude_iTXt=MagickTrue; */
11286        mng_info->ping_exclude_oFFs=MagickTrue;
11287        mng_info->ping_exclude_pHYs=MagickTrue;
11288        mng_info->ping_exclude_sRGB=MagickTrue;
11289        mng_info->ping_exclude_tEXt=MagickTrue;
11290        mng_info->ping_exclude_tRNS=MagickTrue;
11291        mng_info->ping_exclude_vpAg=MagickTrue;
11292        mng_info->ping_exclude_zCCP=MagickTrue;
11293        mng_info->ping_exclude_zTXt=MagickTrue;
11294
11295        status=WriteOnePNGImage(mng_info,image_info,image);
11296      }
11297
11298     if (status == MagickFalse)
11299       {
11300         MngInfoFreeStruct(mng_info,&have_mng_structure);
11301         (void) CloseBlob(image);
11302         return(MagickFalse);
11303       }
11304     (void) CatchImageException(image);
11305     if (GetNextImageInList(image) == (Image *) NULL)
11306       break;
11307     image=SyncNextImageInList(image);
11308     status=SetImageProgress(image,SaveImagesTag,scene++,
11309       GetImageListLength(image));
11310
11311     if (status == MagickFalse)
11312       break;
11313
11314   } while (mng_info->adjoin);
11315
11316   if (write_mng)
11317     {
11318       while (GetPreviousImageInList(image) != (Image *) NULL)
11319         image=GetPreviousImageInList(image);
11320       /*
11321         Write the MEND chunk.
11322       */
11323       (void) WriteBlobMSBULong(image,0x00000000L);
11324       PNGType(chunk,mng_MEND);
11325       LogPNGChunk(logging,mng_MEND,0L);
11326       (void) WriteBlob(image,4,chunk);
11327       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11328     }
11329   /*
11330     Relinquish resources.
11331   */
11332   (void) CloseBlob(image);
11333   MngInfoFreeStruct(mng_info,&have_mng_structure);
11334
11335   if (logging != MagickFalse)
11336     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
11337
11338   return(MagickTrue);
11339 }
11340 #else /* PNG_LIBPNG_VER > 10011 */
11341
11342 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
11343 {
11344   image=image;
11345   printf("Your PNG library is too old: You have libpng-%s\n",
11346      PNG_LIBPNG_VER_STRING);
11347
11348   ThrowBinaryException(CoderError,"PNG library is too old",
11349      image_info->filename);
11350 }
11351
11352 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11353 {
11354   return(WritePNGImage(image_info,image));
11355 }
11356 #endif /* PNG_LIBPNG_VER > 10011 */
11357 #endif