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