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