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