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