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