]> 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/colorspace.h"
52 #include "magick/constitute.h"
53 #include "magick/enhance.h"
54 #include "magick/exception.h"
55 #include "magick/exception-private.h"
56 #include "magick/geometry.h"
57 #include "magick/histogram.h"
58 #include "magick/image.h"
59 #include "magick/image-private.h"
60 #include "magick/layer.h"
61 #include "magick/list.h"
62 #include "magick/log.h"
63 #include "magick/magick.h"
64 #include "magick/memory_.h"
65 #include "magick/module.h"
66 #include "magick/monitor.h"
67 #include "magick/monitor-private.h"
68 #include "magick/option.h"
69 #include "magick/quantum-private.h"
70 #include "magick/profile.h"
71 #include "magick/property.h"
72 #include "magick/quantize.h"
73 #include "magick/resource_.h"
74 #include "magick/semaphore.h"
75 #include "magick/quantum-private.h"
76 #include "magick/static.h"
77 #include "magick/statistic.h"
78 #include "magick/string_.h"
79 #include "magick/string-private.h"
80 #include "magick/transform.h"
81 #include "magick/utility.h"
82 #if defined(MAGICKCORE_PNG_DELEGATE)
83
84 /* Suppress libpng pedantic warnings that were added in
85  * libpng-1.2.41 and libpng-1.4.0.  If you are working on
86  * migration to libpng-1.5, remove these defines and then
87  * fix any code that generates warnings.
88  */
89 /* #define PNG_DEPRECATED   Use of this function is deprecated */
90 /* #define PNG_USE_RESULT   The result of this function must be checked */
91 /* #define PNG_NORETURN     This function does not return */
92 /* #define PNG_ALLOCATED    The result of the function is new memory */
93 /* #define PNG_DEPSTRUCT    Access to this struct member is deprecated */
94
95 #include "png.h"
96 #include "zlib.h"
97 \f
98 /* ImageMagick differences */
99 #define first_scene scene
100
101 #if PNG_LIBPNG_VER > 10011
102 /*
103   Optional declarations. Define or undefine them as you like.
104 */
105 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
106
107 /*
108   Features under construction.  Define these to work on them.
109 */
110 #undef MNG_OBJECT_BUFFERS
111 #undef MNG_BASI_SUPPORTED
112 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
113 #define MNG_INSERT_LAYERS   /* Troublesome, but seem to work as of 5.4.4 */
114 #define PNG_BUILD_PALETTE   /* This works as of 5.4.3. */
115 #define PNG_SORT_PALETTE    /* This works as of 5.4.0. */
116 #if defined(MAGICKCORE_JPEG_DELEGATE)
117 #  define JNG_SUPPORTED /* Not finished as of 5.5.2.  See "To do" comments. */
118 #endif
119 #if !defined(RGBColorMatchExact)
120 #define IsPNGColorEqual(color,target) \
121    (((color).red == (target).red) && \
122     ((color).green == (target).green) && \
123     ((color).blue == (target).blue))
124 #endif
125
126 /*
127   Establish thread safety.
128   setjmp/longjmp is claimed to be safe on these platforms:
129   setjmp/longjmp is alleged to be unsafe on these platforms:
130 */
131 #ifndef SETJMP_IS_THREAD_SAFE
132 #define PNG_SETJMP_NOT_THREAD_SAFE
133 #endif
134
135 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
136 static SemaphoreInfo
137   *png_semaphore = (SemaphoreInfo *) NULL;
138 #endif
139
140 /*
141   This temporary until I set up malloc'ed object attributes array.
142   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
143   waste more memory.
144 */
145 #define MNG_MAX_OBJECTS 256
146
147 /*
148   If this not defined, spec is interpreted strictly.  If it is
149   defined, an attempt will be made to recover from some errors,
150   including
151       o global PLTE too short
152 */
153 #undef MNG_LOOSE
154
155 /*
156   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
157   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
158   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
159   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
160   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
161   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
162   will be enabled by default in libpng-1.2.0.
163 */
164 #ifdef PNG_MNG_FEATURES_SUPPORTED
165 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
166 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
167 #  endif
168 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
169 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
170 #  endif
171 #endif
172
173 /*
174   Maximum valid unsigned long in PNG/MNG chunks is (2^31)-1
175   This macro is only defined in libpng-1.0.3 and later.
176   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
177 */
178 #ifndef PNG_UINT_31_MAX
179 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
180 #endif
181
182 /*
183   Constant strings for known chunk types.  If you need to add a chunk,
184   add a string holding the name here.   To make the code more
185   portable, we use ASCII numbers like this, not characters.
186 */
187
188 static png_byte FARDATA mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
189 static png_byte FARDATA mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
190 static png_byte FARDATA mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
191 static png_byte FARDATA mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
192 static png_byte FARDATA mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
193 static png_byte FARDATA mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
194 static png_byte FARDATA mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
195 static png_byte FARDATA mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
196 static png_byte FARDATA mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
197 static png_byte FARDATA mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
198 static png_byte FARDATA mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
199 static png_byte FARDATA mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
200 static png_byte FARDATA mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
201 static png_byte FARDATA mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
202 static png_byte FARDATA mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
203 static png_byte FARDATA mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
204 static png_byte FARDATA mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
205 static png_byte FARDATA mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
206 static png_byte FARDATA mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
207 static png_byte FARDATA mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
208 static png_byte FARDATA mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
209 static png_byte FARDATA mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
210 static png_byte FARDATA mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
211 static png_byte FARDATA mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
212 static png_byte FARDATA mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
213 static png_byte FARDATA mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
214 static png_byte FARDATA mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
215 static png_byte FARDATA mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
216 static png_byte FARDATA mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
217 static png_byte FARDATA mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
218 static png_byte FARDATA mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
219 static png_byte FARDATA mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
220 static png_byte FARDATA mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
221 static png_byte FARDATA mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
222
223 #if defined(JNG_SUPPORTED)
224 static png_byte FARDATA mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
225 static png_byte FARDATA mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
226 static png_byte FARDATA mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
227 static png_byte FARDATA mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
228 static png_byte FARDATA mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
229 static png_byte FARDATA mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
230 #endif
231
232 /*
233 Other known chunks that are not yet supported by ImageMagick:
234 static png_byte FARDATA mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
235 static png_byte FARDATA mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
236 static png_byte FARDATA mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
237 static png_byte FARDATA mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
238 static png_byte FARDATA mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
239 static png_byte FARDATA mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
240 static png_byte FARDATA mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
241 static png_byte FARDATA mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
242 */
243
244 typedef struct _MngBox
245 {
246   long
247     left,
248     right,
249     top,
250     bottom;
251 } MngBox;
252
253 typedef struct _MngPair
254 {
255   volatile long
256     a,
257     b;
258 } MngPair;
259
260 #ifdef MNG_OBJECT_BUFFERS
261 typedef struct _MngBuffer
262 {
263
264   unsigned long
265     height,
266     width;
267
268   Image
269     *image;
270
271   png_color
272     plte[256];
273
274   int
275     reference_count;
276
277   unsigned char
278     alpha_sample_depth,
279     compression_method,
280     color_type,
281     concrete,
282     filter_method,
283     frozen,
284     image_type,
285     interlace_method,
286     pixel_sample_depth,
287     plte_length,
288     sample_depth,
289     viewable;
290 } MngBuffer;
291 #endif
292
293 typedef struct _MngInfo
294 {
295
296 #ifdef MNG_OBJECT_BUFFERS
297   MngBuffer
298     *ob[MNG_MAX_OBJECTS];
299 #endif
300
301   Image *
302     image;
303
304   RectangleInfo
305     page;
306
307   int
308     adjoin,
309 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
310     bytes_in_read_buffer,
311     found_empty_plte,
312 #endif
313     equal_backgrounds,
314     equal_chrms,
315     equal_gammas,
316 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
317     defined(PNG_MNG_FEATURES_SUPPORTED)
318     equal_palettes,
319 #endif
320     equal_physs,
321     equal_srgbs,
322     framing_mode,
323     have_global_bkgd,
324     have_global_chrm,
325     have_global_gama,
326     have_global_phys,
327     have_global_sbit,
328     have_global_srgb,
329     have_saved_bkgd_index,
330     have_write_global_chrm,
331     have_write_global_gama,
332     have_write_global_plte,
333     have_write_global_srgb,
334     need_fram,
335     object_id,
336     old_framing_mode,
337     optimize,
338     saved_bkgd_index;
339
340   int
341     new_number_colors;
342
343   long
344     image_found,
345     loop_count[256],
346     loop_iteration[256],
347     scenes_found,
348     x_off[MNG_MAX_OBJECTS],
349     y_off[MNG_MAX_OBJECTS];
350
351   MngBox
352     clip,
353     frame,
354     image_box,
355     object_clip[MNG_MAX_OBJECTS];
356
357   unsigned char
358     /* These flags could be combined into one byte */
359     exists[MNG_MAX_OBJECTS],
360     frozen[MNG_MAX_OBJECTS],
361     loop_active[256],
362     invisible[MNG_MAX_OBJECTS],
363     viewable[MNG_MAX_OBJECTS];
364
365   MagickOffsetType
366     loop_jump[256];
367
368   png_colorp
369     global_plte;
370
371   png_color_8
372     global_sbit;
373
374   png_byte
375 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
376     read_buffer[8],
377 #endif
378     global_trns[256];
379
380   float
381     global_gamma;
382
383   ChromaticityInfo
384     global_chrm;
385
386   RenderingIntent
387     global_srgb_intent;
388
389   unsigned long
390     delay,
391     global_plte_length,
392     global_trns_length,
393     global_x_pixels_per_unit,
394     global_y_pixels_per_unit,
395     mng_width,
396     mng_height,
397     ticks_per_second;
398
399   unsigned int
400     IsPalette,
401     global_phys_unit_type,
402     basi_warning,
403     clon_warning,
404     dhdr_warning,
405     jhdr_warning,
406     magn_warning,
407     past_warning,
408     phyg_warning,
409     phys_warning,
410     sbit_warning,
411     show_warning,
412     mng_type,
413     write_mng,
414     write_png_colortype,
415     write_png_depth,
416     write_png8,
417     write_png24,
418     write_png32;
419
420 #ifdef MNG_BASI_SUPPORTED
421   unsigned long
422     basi_width,
423     basi_height;
424
425   unsigned int
426     basi_depth,
427     basi_color_type,
428     basi_compression_method,
429     basi_filter_type,
430     basi_interlace_method,
431     basi_red,
432     basi_green,
433     basi_blue,
434     basi_alpha,
435     basi_viewable;
436 #endif
437
438   png_uint_16
439     magn_first,
440     magn_last,
441     magn_mb,
442     magn_ml,
443     magn_mr,
444     magn_mt,
445     magn_mx,
446     magn_my,
447     magn_methx,
448     magn_methy;
449
450   PixelPacket
451     mng_global_bkgd;
452
453 } MngInfo;
454 #endif /* VER */
455 \f
456 /*
457   Forward declarations.
458 */
459 static MagickBooleanType
460   WritePNGImage(const ImageInfo *,Image *);
461 static MagickBooleanType
462   WriteMNGImage(const ImageInfo *,Image *);
463 #if defined(JNG_SUPPORTED)
464 static MagickBooleanType
465   WriteJNGImage(const ImageInfo *,Image *);
466 #endif
467
468 static inline long MagickMax(const long x,const long y)
469 {
470   if (x > y)
471     return(x);
472   return(y);
473 }
474 static inline long MagickMin(const long x,const long y)
475 {
476   if (x < y)
477     return(x);
478   return(y);
479 }
480 \f
481 #if PNG_LIBPNG_VER > 10011
482 #if defined(PNG_SORT_PALETTE)
483 /*
484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485 %                                                                             %
486 %                                                                             %
487 %                                                                             %
488 %   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                       %
489 %                                                                             %
490 %                                                                             %
491 %                                                                             %
492 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
493 %
494 %  CompressColormapTransFirst compresses an image colormap removing
495 %  any duplicate and unused color entries and putting the transparent colors
496 %  first.  Returns MagickTrue on success, MagickFalse on error.
497 %
498 %  The format of the CompressColormapTransFirst method is:
499 %
500 %      unsigned int CompressColormapTransFirst(Image *image)
501 %
502 %  A description of each parameter follows:
503 %
504 %    o image: the address of a structure of type Image.
505 %      This function updates image->colors and image->colormap.
506 %
507 */
508 static MagickBooleanType CompressColormapTransFirst(Image *image)
509 {
510   int
511     remap_needed,
512     k;
513
514   long
515     j,
516     new_number_colors,
517     number_colors,
518     y;
519
520   PixelPacket
521     *colormap;
522
523   register const IndexPacket
524     *indices;
525
526   register const PixelPacket
527     *p;
528
529   IndexPacket
530     top_used;
531
532   register long
533     i,
534     x;
535
536   IndexPacket
537     *map,
538     *opacity;
539
540   unsigned char
541     *marker,
542     have_transparency;
543
544   /*
545     Determine if colormap can be compressed.
546   */
547   assert(image != (Image *) NULL);
548   assert(image->signature == MagickSignature);
549   if (image->debug != MagickFalse)
550     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
551            "    CompressColorMapTransFirst %s (%ld colors)",
552            image->filename,image->colors);
553   if (image->storage_class != PseudoClass || image->colors > 256 ||
554       image->colors < 2)
555     {
556       if (image->debug != MagickFalse)
557         {
558           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
559                "    Could not compress colormap");
560           if (image->colors > 256 || image->colors == 0)
561             return(MagickFalse);
562           else
563             return(MagickTrue);
564         }
565     }
566   marker=(unsigned char *) AcquireQuantumMemory(image->colors,sizeof(*marker));
567   if (marker == (unsigned char *) NULL)
568     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
569       image->filename);
570   opacity=(IndexPacket *) AcquireQuantumMemory(image->colors,sizeof(*opacity));
571   if (opacity == (IndexPacket *) NULL)
572     {
573       marker=(unsigned char *) RelinquishMagickMemory(marker);
574       ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
575         image->filename);
576     }
577   /*
578     Mark colors that are present.
579   */
580   number_colors=(long) image->colors;
581   for (i=0; i < number_colors; i++)
582   {
583     marker[i]=MagickFalse;
584     opacity[i]=OpaqueOpacity;
585   }
586   top_used=0;
587   for (y=0; y < (long) image->rows; y++)
588   {
589     p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
590     if (p == (const PixelPacket *) NULL)
591       break;
592     indices=GetVirtualIndexQueue(image);
593     if (image->matte != MagickFalse)
594       for (x=0; x < (long) image->columns; x++)
595       {
596         marker[(int) indices[x]]=MagickTrue;
597         opacity[(int) indices[x]]=GetOpacityPixelComponent(p);
598         if (indices[x] > top_used)
599            top_used=indices[x];
600         p++;
601       }
602     else
603       for (x=0; x < (long) image->columns; x++)
604       {
605         marker[(int) indices[x]]=MagickTrue;
606         if (indices[x] > top_used)
607            top_used=indices[x];
608       }
609   }
610
611   if (image->matte != MagickFalse)
612   {
613     /*
614       Mark background color, topmost occurrence if more than one.
615     */
616     for (i=number_colors-1; i; i--)
617     {
618       if (IsColorEqual(image->colormap+i,&image->background_color))
619         {
620           marker[i]=MagickTrue;
621           break;
622         }
623     }
624   }
625   /*
626     Unmark duplicates.
627   */
628   for (i=0; i < number_colors-1; i++)
629     if (marker[i])
630       {
631         for (j=i+1; j < number_colors; j++)
632           if ((opacity[i] == opacity[j]) &&
633               (IsColorEqual(image->colormap+i,image->colormap+j)))
634             marker[j]=MagickFalse;
635        }
636   /*
637     Count colors that still remain.
638   */
639   have_transparency=MagickFalse;
640   new_number_colors=0;
641   for (i=0; i < number_colors; i++)
642     if (marker[i])
643       {
644         new_number_colors++;
645         if (opacity[i] != OpaqueOpacity)
646           have_transparency=MagickTrue;
647       }
648   if ((!have_transparency || (marker[0] &&
649       (opacity[0] == (Quantum) TransparentOpacity)))
650       && (new_number_colors == number_colors))
651     {
652       /*
653         No duplicate or unused entries, and transparency-swap not needed.
654       */
655       marker=(unsigned char *) RelinquishMagickMemory(marker);
656       opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
657       return(MagickTrue);
658     }
659
660   remap_needed=MagickFalse;
661   if ((long) top_used >= new_number_colors)
662      remap_needed=MagickTrue;
663
664   /*
665     Compress colormap.
666   */
667
668   colormap=(PixelPacket *) AcquireQuantumMemory(image->colors,
669     sizeof(*colormap));
670   if (colormap == (PixelPacket *) NULL)
671     {
672       marker=(unsigned char *) RelinquishMagickMemory(marker);
673       opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
674       ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
675         image->filename);
676     }
677   /*
678     Eliminate unused colormap entries.
679   */
680   map=(IndexPacket *) AcquireQuantumMemory((size_t) number_colors,
681     sizeof(*map));
682   if (map == (IndexPacket *) NULL)
683     {
684       marker=(unsigned char *) RelinquishMagickMemory(marker);
685       opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
686       colormap=(PixelPacket *) RelinquishMagickMemory(colormap);
687       ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
688         image->filename);
689     }
690   k=0;
691   for (i=0; i < number_colors; i++)
692   {
693     map[i]=(IndexPacket) k;
694     if (marker[i])
695       {
696         for (j=i+1; j < number_colors; j++)
697         {
698           if ((opacity[i] == opacity[j]) &&
699               (IsColorEqual(image->colormap+i,image->colormap+j)))
700             {
701                map[j]=(IndexPacket) k;
702                marker[j]=MagickFalse;
703             }
704         }
705         k++;
706       }
707   }
708   j=0;
709   for (i=0; i < number_colors; i++)
710   {
711     if (marker[i])
712       {
713         colormap[j]=image->colormap[i];
714         j++;
715       }
716   }
717   if (have_transparency && (opacity[0] != (Quantum) TransparentOpacity))
718     {
719       /*
720         Move the first transparent color to palette entry 0.
721       */
722       for (i=1; i < number_colors; i++)
723       {
724         if (marker[i] && opacity[i] == (Quantum) TransparentOpacity)
725           {
726             PixelPacket
727               temp_colormap;
728
729             temp_colormap=colormap[0];
730             colormap[0]=colormap[(int) map[i]];
731             colormap[(long) map[i]]=temp_colormap;
732             for (j=0; j < number_colors; j++)
733             {
734               if (map[j] == 0)
735                 map[j]=map[i];
736               else if (map[j] == map[i])
737                 map[j]=0;
738             }
739             remap_needed=MagickTrue;
740             break;
741           }
742       }
743    }
744
745   opacity=(IndexPacket *) RelinquishMagickMemory(opacity);
746   marker=(unsigned char *) RelinquishMagickMemory(marker);
747
748   if (remap_needed)
749     {
750       ExceptionInfo
751         *exception;
752
753       register IndexPacket
754         *pixels;
755
756       register PixelPacket
757         *q;
758
759       /*
760         Remap pixels.
761       */
762       exception=(&image->exception);
763       for (y=0; y < (long) image->rows; y++)
764       {
765         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
766         if (q == (PixelPacket *) NULL)
767           break;
768         pixels=GetAuthenticIndexQueue(image);
769         for (x=0; x < (long) image->columns; x++)
770         {
771           j=(int) pixels[x];
772           pixels[x]=map[j];
773         }
774         if (SyncAuthenticPixels(image,exception) == MagickFalse)
775           break;
776       }
777       for (i=0; i < new_number_colors; i++)
778         image->colormap[i]=colormap[i];
779     }
780   colormap=(PixelPacket *) RelinquishMagickMemory(colormap);
781   image->colors=(unsigned long) new_number_colors;
782   map=(IndexPacket *) RelinquishMagickMemory(map);
783   return(MagickTrue);
784 }
785 #endif
786 \f
787 /*
788 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
789 %                                                                             %
790 %                                                                             %
791 %                                                                             %
792 %   I m a g e I s G r a y                                                     %
793 %                                                                             %
794 %                                                                             %
795 %                                                                             %
796 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
797 %                                                                             %
798 %   Like IsGrayImage except does not change DirectClass to PseudoClass        %
799 %                                                                             %
800 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
801 */
802 static MagickBooleanType ImageIsGray(Image *image)
803 {
804   register const PixelPacket
805     *p;
806
807   register long
808     i,
809     x,
810     y;
811
812   assert(image != (Image *) NULL);
813   assert(image->signature == MagickSignature);
814   if (image->debug != MagickFalse)
815     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
816
817   if (image->storage_class == PseudoClass)
818     {
819       for (i=0; i < (long) image->colors; i++)
820         if (IsGray(image->colormap+i) == MagickFalse)
821           return(MagickFalse);
822       return(MagickTrue);
823     }
824   for (y=0; y < (long) image->rows; y++)
825   {
826     p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
827     if (p == (const PixelPacket *) NULL)
828       return(MagickFalse);
829     for (x=(long) image->columns-1; x >= 0; x--)
830     {
831        if (IsGray(p) == MagickFalse)
832           return(MagickFalse);
833        p++;
834     }
835   }
836   return(MagickTrue);
837 }
838 \f
839 /*
840 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
841 %                                                                             %
842 %                                                                             %
843 %                                                                             %
844 %   I m a g e I s M o n o c h r o m e                                         %
845 %                                                                             %
846 %                                                                             %
847 %                                                                             %
848 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
849 %                                                                             %
850 %   Like IsMonochromeImage except does not change DirectClass to PseudoClass  %
851 %   and is more accurate.                                                     %
852 %                                                                             %
853 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
854 */
855 static MagickBooleanType ImageIsMonochrome(Image *image)
856 {
857   register const PixelPacket
858     *p;
859
860   register long
861     i,
862     x,
863     y;
864
865   assert(image != (Image *) NULL);
866   assert(image->signature == MagickSignature);
867   if (image->debug != MagickFalse)
868     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
869   if (image->storage_class == PseudoClass)
870     {
871       for (i=0; i < (long) image->colors; i++)
872       {
873         if ((IsGray(image->colormap+i) == MagickFalse) ||
874             ((image->colormap[i].red != 0) &&
875              (image->colormap[i].red != (Quantum) QuantumRange)))
876           return(MagickFalse);
877       }
878       return(MagickTrue);
879     }
880   for (y=0; y < (long) image->rows; y++)
881   {
882     p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
883     if (p == (const PixelPacket *) NULL)
884       return(MagickFalse);
885     for (x=(long) image->columns-1; x >= 0; x--)
886     {
887       if ((p->red != 0) && (p->red != (Quantum) QuantumRange))
888         return(MagickFalse);
889       if (IsGray(p) == MagickFalse)
890         return(MagickFalse);
891       if ((p->opacity != OpaqueOpacity) &&
892           (p->opacity != (Quantum) TransparentOpacity))
893         return(MagickFalse);
894       p++;
895     }
896   }
897   return(MagickTrue);
898 }
899 #endif /* PNG_LIBPNG_VER > 10011 */
900 #endif /* MAGICKCORE_PNG_DELEGATE */
901 \f
902 /*
903 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
904 %                                                                             %
905 %                                                                             %
906 %                                                                             %
907 %   I s M N G                                                                 %
908 %                                                                             %
909 %                                                                             %
910 %                                                                             %
911 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
912 %
913 %  IsMNG() returns MagickTrue if the image format type, identified by the
914 %  magick string, is MNG.
915 %
916 %  The format of the IsMNG method is:
917 %
918 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
919 %
920 %  A description of each parameter follows:
921 %
922 %    o magick: compare image format pattern against these bytes.
923 %
924 %    o length: Specifies the length of the magick string.
925 %
926 %
927 */
928 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
929 {
930   if (length < 8)
931     return(MagickFalse);
932   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
933     return(MagickTrue);
934   return(MagickFalse);
935 }
936 \f
937 /*
938 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
939 %                                                                             %
940 %                                                                             %
941 %                                                                             %
942 %   I s J N G                                                                 %
943 %                                                                             %
944 %                                                                             %
945 %                                                                             %
946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
947 %
948 %  IsJNG() returns MagickTrue if the image format type, identified by the
949 %  magick string, is JNG.
950 %
951 %  The format of the IsJNG method is:
952 %
953 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
954 %
955 %  A description of each parameter follows:
956 %
957 %    o magick: compare image format pattern against these bytes.
958 %
959 %    o length: Specifies the length of the magick string.
960 %
961 %
962 */
963 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
964 {
965   if (length < 8)
966     return(MagickFalse);
967   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
968     return(MagickTrue);
969   return(MagickFalse);
970 }
971 \f
972 /*
973 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
974 %                                                                             %
975 %                                                                             %
976 %                                                                             %
977 %   I s P N G                                                                 %
978 %                                                                             %
979 %                                                                             %
980 %                                                                             %
981 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
982 %
983 %  IsPNG() returns MagickTrue if the image format type, identified by the
984 %  magick string, is PNG.
985 %
986 %  The format of the IsPNG method is:
987 %
988 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
989 %
990 %  A description of each parameter follows:
991 %
992 %    o magick: compare image format pattern against these bytes.
993 %
994 %    o length: Specifies the length of the magick string.
995 %
996 */
997 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
998 {
999   if (length < 8)
1000     return(MagickFalse);
1001   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1002     return(MagickTrue);
1003   return(MagickFalse);
1004 }
1005 \f
1006 #if defined(MAGICKCORE_PNG_DELEGATE)
1007 #if defined(__cplusplus) || defined(c_plusplus)
1008 extern "C" {
1009 #endif
1010
1011 #if (PNG_LIBPNG_VER > 10011)
1012 static size_t WriteBlobMSBULong(Image *image,const unsigned long value)
1013 {
1014   unsigned char
1015     buffer[4];
1016
1017   assert(image != (Image *) NULL);
1018   assert(image->signature == MagickSignature);
1019   buffer[0]=(unsigned char) (value >> 24);
1020   buffer[1]=(unsigned char) (value >> 16);
1021   buffer[2]=(unsigned char) (value >> 8);
1022   buffer[3]=(unsigned char) value;
1023   return((size_t) WriteBlob(image,4,buffer));
1024 }
1025
1026 static void PNGLong(png_bytep p,png_uint_32 value)
1027 {
1028   *p++=(png_byte) ((value >> 24) & 0xff);
1029   *p++=(png_byte) ((value >> 16) & 0xff);
1030   *p++=(png_byte) ((value >> 8) & 0xff);
1031   *p++=(png_byte) (value & 0xff);
1032 }
1033
1034 static void PNGsLong(png_bytep p,png_int_32 value)
1035 {
1036   *p++=(png_byte) ((value >> 24) & 0xff);
1037   *p++=(png_byte) ((value >> 16) & 0xff);
1038   *p++=(png_byte) ((value >> 8) & 0xff);
1039   *p++=(png_byte) (value & 0xff);
1040 }
1041
1042 static void PNGShort(png_bytep p,png_uint_16 value)
1043 {
1044   *p++=(png_byte) ((value >> 8) & 0xff);
1045   *p++=(png_byte) (value & 0xff);
1046 }
1047
1048 static void PNGType(png_bytep p,png_bytep type)
1049 {
1050   (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1051 }
1052
1053 static void LogPNGChunk(int logging, png_bytep type, size_t length)
1054 {
1055   if (logging != MagickFalse)
1056     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1057       "  Writing %c%c%c%c chunk, length: %lu",
1058       type[0],type[1],type[2],type[3],(unsigned long) length);
1059 }
1060 #endif /* PNG_LIBPNG_VER > 10011 */
1061
1062 #if defined(__cplusplus) || defined(c_plusplus)
1063 }
1064 #endif
1065
1066 #if PNG_LIBPNG_VER > 10011
1067 /*
1068 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1069 %                                                                             %
1070 %                                                                             %
1071 %                                                                             %
1072 %   R e a d P N G I m a g e                                                   %
1073 %                                                                             %
1074 %                                                                             %
1075 %                                                                             %
1076 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1077 %
1078 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1079 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1080 %  allocates the memory necessary for the new Image structure and returns a
1081 %  pointer to the new image or set of images.
1082 %
1083 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1084 %
1085 %  The format of the ReadPNGImage method is:
1086 %
1087 %      Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1088 %
1089 %  A description of each parameter follows:
1090 %
1091 %    o image_info: the image info.
1092 %
1093 %    o exception: return any errors or warnings in this structure.
1094 %
1095 %  To do, more or less in chronological order (as of version 5.5.2,
1096 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1097 %
1098 %    Get 16-bit cheap transparency working.
1099 %
1100 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1101 %
1102 %    Preserve all unknown and not-yet-handled known chunks found in input
1103 %    PNG file and copy them into output PNG files according to the PNG
1104 %    copying rules.
1105 %
1106 %    (At this point, PNG encoding should be in full MNG compliance)
1107 %
1108 %    Provide options for choice of background to use when the MNG BACK
1109 %    chunk is not present or is not mandatory (i.e., leave transparent,
1110 %    user specified, MNG BACK, PNG bKGD)
1111 %
1112 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1113 %    efficiently by linking in the duplicate frames.].
1114 %
1115 %    Decode and act on the MHDR simplicity profile (offer option to reject
1116 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1117 %
1118 %    Upgrade to full MNG without Delta-PNG.
1119 %
1120 %        o  BACK [done a while ago except for background image ID]
1121 %        o  MOVE [done 15 May 1999]
1122 %        o  CLIP [done 15 May 1999]
1123 %        o  DISC [done 19 May 1999]
1124 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1125 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1126 %        o  SHOW
1127 %        o  PAST
1128 %        o  BASI
1129 %        o  MNG-level tEXt/iTXt/zTXt
1130 %        o  pHYg
1131 %        o  pHYs
1132 %        o  sBIT
1133 %        o  bKGD
1134 %        o  iTXt (wait for libpng implementation).
1135 %
1136 %    Use the scene signature to discover when an identical scene is
1137 %    being reused, and just point to the original image->exception instead
1138 %    of storing another set of pixels.  This not specific to MNG
1139 %    but could be applied generally.
1140 %
1141 %    Upgrade to full MNG with Delta-PNG.
1142 %
1143 %    JNG tEXt/iTXt/zTXt
1144 %
1145 %    We will not attempt to read files containing the CgBI chunk.
1146 %    They are really Xcode files meant for display on the iPhone.
1147 %    These are not valid PNG files and it is impossible to recover
1148 %    the orginal PNG from files that have been converted to Xcode-PNG,
1149 %    since irretrievable loss of color data has occurred due to the
1150 %    use of premultiplied alpha.
1151 */
1152
1153 #if defined(__cplusplus) || defined(c_plusplus)
1154 extern "C" {
1155 #endif
1156
1157 /*
1158   This the function that does the actual reading of data.  It is
1159   the same as the one supplied in libpng, except that it receives the
1160   datastream from the ReadBlob() function instead of standard input.
1161 */
1162 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1163 {
1164   Image
1165     *image;
1166
1167   image=(Image *) png_get_io_ptr(png_ptr);
1168   if (length)
1169     {
1170       png_size_t
1171         check;
1172
1173       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1174       if (check != length)
1175         {
1176           char
1177             msg[MaxTextExtent];
1178
1179           (void) FormatMagickString(msg,MaxTextExtent,
1180             "Expected %lu bytes; found %lu bytes",(unsigned long) length,
1181             (unsigned long) check);
1182           png_warning(png_ptr,msg);
1183           png_error(png_ptr,"Read Exception");
1184         }
1185     }
1186 }
1187
1188 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1189     !defined(PNG_MNG_FEATURES_SUPPORTED)
1190 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1191  * older than libpng-1.0.3a, which was the first to allow the empty
1192  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1193  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1194  * encountered after an empty PLTE, so we have to look ahead for bKGD
1195  * chunks and remove them from the datastream that is passed to libpng,
1196  * and store their contents for later use.
1197  */
1198 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1199 {
1200   MngInfo
1201     *mng_info;
1202
1203   Image
1204     *image;
1205
1206   png_size_t
1207     check;
1208
1209   register long
1210     i;
1211
1212   i=0;
1213   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1214   image=(Image *) mng_info->image;
1215   while (mng_info->bytes_in_read_buffer && length)
1216   {
1217     data[i]=mng_info->read_buffer[i];
1218     mng_info->bytes_in_read_buffer--;
1219     length--;
1220     i++;
1221   }
1222   if (length)
1223     {
1224       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1225       if (check != length)
1226         png_error(png_ptr,"Read Exception");
1227       if (length == 4)
1228         {
1229           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1230               (data[3] == 0))
1231             {
1232               check=(png_size_t) ReadBlob(image,(size_t) length,
1233                 (char *) mng_info->read_buffer);
1234               mng_info->read_buffer[4]=0;
1235               mng_info->bytes_in_read_buffer=4;
1236               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1237                 mng_info->found_empty_plte=MagickTrue;
1238               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1239                 {
1240                   mng_info->found_empty_plte=MagickFalse;
1241                   mng_info->have_saved_bkgd_index=MagickFalse;
1242                 }
1243             }
1244           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1245               (data[3] == 1))
1246             {
1247               check=(png_size_t) ReadBlob(image,(size_t) length,
1248                 (char *) mng_info->read_buffer);
1249               mng_info->read_buffer[4]=0;
1250               mng_info->bytes_in_read_buffer=4;
1251               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1252                 if (mng_info->found_empty_plte)
1253                   {
1254                     /*
1255                       Skip the bKGD data byte and CRC.
1256                     */
1257                     check=(png_size_t)
1258                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1259                     check=(png_size_t) ReadBlob(image,(size_t) length,
1260                       (char *) mng_info->read_buffer);
1261                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1262                     mng_info->have_saved_bkgd_index=MagickTrue;
1263                     mng_info->bytes_in_read_buffer=0;
1264                   }
1265             }
1266         }
1267     }
1268 }
1269 #endif
1270
1271 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1272 {
1273   Image
1274     *image;
1275
1276   image=(Image *) png_get_io_ptr(png_ptr);
1277   if (length)
1278     {
1279       png_size_t
1280         check;
1281
1282       check=(png_size_t) WriteBlob(image,(unsigned long) length,data);
1283       if (check != length)
1284         png_error(png_ptr,"WriteBlob Failed");
1285     }
1286 }
1287
1288 static void png_flush_data(png_structp png_ptr)
1289 {
1290   (void) png_ptr;
1291 }
1292
1293 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1294 static int PalettesAreEqual(Image *a,Image *b)
1295 {
1296   long
1297     i;
1298
1299   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1300     return((int) MagickFalse);
1301   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1302     return((int) MagickFalse);
1303   if (a->colors != b->colors)
1304     return((int) MagickFalse);
1305   for (i=0; i < (long) a->colors; i++)
1306   {
1307     if ((a->colormap[i].red != b->colormap[i].red) ||
1308         (a->colormap[i].green != b->colormap[i].green) ||
1309         (a->colormap[i].blue != b->colormap[i].blue))
1310       return((int) MagickFalse);
1311   }
1312   return((int) MagickTrue);
1313 }
1314 #endif
1315
1316 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1317 {
1318   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1319       mng_info->exists[i] && !mng_info->frozen[i])
1320     {
1321 #ifdef MNG_OBJECT_BUFFERS
1322       if (mng_info->ob[i] != (MngBuffer *) NULL)
1323         {
1324           if (mng_info->ob[i]->reference_count > 0)
1325             mng_info->ob[i]->reference_count--;
1326           if (mng_info->ob[i]->reference_count == 0)
1327             {
1328               if (mng_info->ob[i]->image != (Image *) NULL)
1329                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1330               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1331             }
1332         }
1333       mng_info->ob[i]=(MngBuffer *) NULL;
1334 #endif
1335       mng_info->exists[i]=MagickFalse;
1336       mng_info->invisible[i]=MagickFalse;
1337       mng_info->viewable[i]=MagickFalse;
1338       mng_info->frozen[i]=MagickFalse;
1339       mng_info->x_off[i]=0;
1340       mng_info->y_off[i]=0;
1341       mng_info->object_clip[i].left=0;
1342       mng_info->object_clip[i].right=(long) PNG_UINT_31_MAX;
1343       mng_info->object_clip[i].top=0;
1344       mng_info->object_clip[i].bottom=(long) PNG_UINT_31_MAX;
1345     }
1346 }
1347
1348 static void MngInfoFreeStruct(MngInfo *mng_info,int *have_mng_structure)
1349 {
1350   if (*have_mng_structure && (mng_info != (MngInfo *) NULL))
1351     {
1352       register long
1353         i;
1354
1355       for (i=1; i < MNG_MAX_OBJECTS; i++)
1356         MngInfoDiscardObject(mng_info,i);
1357       if (mng_info->global_plte != (png_colorp) NULL)
1358         mng_info->global_plte=(png_colorp)
1359           RelinquishMagickMemory(mng_info->global_plte);
1360       mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1361       *have_mng_structure=MagickFalse;
1362     }
1363 }
1364
1365 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1366 {
1367   MngBox
1368     box;
1369
1370   box=box1;
1371   if (box.left < box2.left)
1372     box.left=box2.left;
1373   if (box.top < box2.top)
1374     box.top=box2.top;
1375   if (box.right > box2.right)
1376     box.right=box2.right;
1377   if (box.bottom > box2.bottom)
1378     box.bottom=box2.bottom;
1379   return box;
1380 }
1381
1382 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1383 {
1384    MngBox
1385       box;
1386
1387   /*
1388     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1389   */
1390   box.left=(long) ((p[0]  << 24) | (p[1]  << 16) | (p[2]  << 8) | p[3]);
1391   box.right=(long) ((p[4]  << 24) | (p[5]  << 16) | (p[6]  << 8) | p[7]);
1392   box.top=(long) ((p[8]  << 24) | (p[9]  << 16) | (p[10] << 8) | p[11]);
1393   box.bottom=(long) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1394   if (delta_type != 0)
1395     {
1396       box.left+=previous_box.left;
1397       box.right+=previous_box.right;
1398       box.top+=previous_box.top;
1399       box.bottom+=previous_box.bottom;
1400     }
1401   return(box);
1402 }
1403
1404 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1405   unsigned char *p)
1406 {
1407   MngPair
1408     pair;
1409   /*
1410     Read two longs from CLON, MOVE or PAST chunk
1411   */
1412   pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1413   pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1414   if (delta_type != 0)
1415     {
1416       pair.a+=previous_pair.a;
1417       pair.b+=previous_pair.b;
1418     }
1419   return(pair);
1420 }
1421
1422 static long mng_get_long(unsigned char *p)
1423 {
1424   return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1425 }
1426
1427 static void PNGErrorHandler(png_struct *ping,png_const_charp message)
1428 {
1429   Image
1430     *image;
1431
1432   image=(Image *) png_get_error_ptr(ping);
1433   if (image->debug != MagickFalse)
1434     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1435       "  libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1436   (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1437     message,"`%s'",image->filename);
1438 #if PNG_LIBPNG_VER < 10500
1439   longjmp(ping->jmpbuf,1);
1440 #else
1441   png_longjmp(ping,1);
1442 #endif
1443 }
1444
1445 static void PNGWarningHandler(png_struct *ping,png_const_charp message)
1446 {
1447   Image
1448     *image;
1449
1450   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1451     png_error(ping, message);
1452   image=(Image *) png_get_error_ptr(ping);
1453   if (image->debug != MagickFalse)
1454     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1455       "  libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,
1456       message);
1457   (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1458     message,"`%s'",image->filename);
1459 }
1460
1461 #ifdef PNG_USER_MEM_SUPPORTED
1462 static png_voidp png_IM_malloc(png_structp png_ptr,png_uint_32 size)
1463 {
1464 #if (PNG_LIBPNG_VER < 10011)
1465   png_voidp
1466     ret;
1467
1468   png_ptr=png_ptr;
1469   ret=((png_voidp) AcquireMagickMemory((size_t) size));
1470   if (ret == NULL)
1471     png_error("Insufficient memory.");
1472   return(ret);
1473 #else
1474   png_ptr=png_ptr;
1475   return((png_voidp) AcquireMagickMemory((size_t) size));
1476 #endif
1477 }
1478
1479 /*
1480   Free a pointer.  It is removed from the list at the same time.
1481 */
1482 static png_free_ptr png_IM_free(png_structp png_ptr,png_voidp ptr)
1483 {
1484   png_ptr=png_ptr;
1485   ptr=RelinquishMagickMemory(ptr);
1486   return((png_free_ptr) NULL);
1487 }
1488 #endif
1489
1490 #if defined(__cplusplus) || defined(c_plusplus)
1491 }
1492 #endif
1493
1494 static int
1495 png_read_raw_profile(Image *image, const ImageInfo *image_info,
1496    png_textp text,int ii)
1497 {
1498   register long
1499     i;
1500
1501   register unsigned char
1502     *dp;
1503
1504   register png_charp
1505     sp;
1506
1507   png_uint_32
1508     length,
1509     nibbles;
1510
1511   StringInfo
1512     *profile;
1513
1514   unsigned char
1515     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1516                  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,1, 2,3,4,5,6,7,8,9,0,0,
1518                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1519                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1520                  13,14,15};
1521
1522   sp=text[ii].text+1;
1523   /* look for newline */
1524   while (*sp != '\n')
1525      sp++;
1526   /* look for length */
1527   while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1528      sp++;
1529   length=(png_uint_32) StringToLong(sp);
1530   while (*sp != ' ' && *sp != '\n')
1531      sp++;
1532   /* allocate space */
1533   if (length == 0)
1534   {
1535     (void) ThrowMagickException(&image->exception,GetMagickModule(),
1536       CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1537     return(MagickFalse);
1538   }
1539   profile=AcquireStringInfo(length);
1540   if (profile == (StringInfo *) NULL)
1541   {
1542     (void) ThrowMagickException(&image->exception,GetMagickModule(),
1543       ResourceLimitError,"MemoryAllocationFailed","`%s'",
1544       "unable to copy profile");
1545     return(MagickFalse);
1546   }
1547   /* copy profile, skipping white space and column 1 "=" signs */
1548   dp=GetStringInfoDatum(profile);
1549   nibbles=length*2;
1550   for (i=0; i < (long) nibbles; i++)
1551   {
1552     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1553     {
1554       if (*sp == '\0')
1555         {
1556           (void) ThrowMagickException(&image->exception,GetMagickModule(),
1557             CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1558           profile=DestroyStringInfo(profile);
1559           return(MagickFalse);
1560         }
1561       sp++;
1562     }
1563     if (i%2 == 0)
1564       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1565     else
1566       (*dp++)+=unhex[(int) *sp++];
1567   }
1568   /*
1569     We have already read "Raw profile type.
1570   */
1571   (void) SetImageProfile(image,&text[ii].key[17],profile);
1572   profile=DestroyStringInfo(profile);
1573   if (image_info->verbose)
1574     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1575   return MagickTrue;
1576 }
1577
1578 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1579 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1580 {
1581   Image
1582     *image;
1583
1584
1585   /* The unknown chunk structure contains the chunk data:
1586      png_byte name[5];
1587      png_byte *data;
1588      png_size_t size;
1589
1590      Note that libpng has already taken care of the CRC handling.
1591   */
1592
1593
1594   if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1595       chunk->name[2] != 65 ||chunk-> name[3] != 103)
1596     return(0); /* Did not recognize */
1597
1598   /* recognized vpAg */
1599
1600   if (chunk->size != 9)
1601     return(-1); /* Error return */
1602
1603   if (chunk->data[8] != 0)
1604     return(0);  /* ImageMagick requires pixel units */
1605
1606   image=(Image *) png_get_user_chunk_ptr(ping);
1607
1608   image->page.width=(unsigned long) ((chunk->data[0] << 24) |
1609      (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1610   image->page.height=(unsigned long) ((chunk->data[4] << 24) |
1611      (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1612
1613   /* Return one of the following: */
1614      /* return(-n);  chunk had an error */
1615      /* return(0);  did not recognize */
1616      /* return(n);  success */
1617
1618   return(1);
1619
1620 }
1621 #endif
1622
1623 /*
1624 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625 %                                                                             %
1626 %                                                                             %
1627 %                                                                             %
1628 %   R e a d O n e P N G I m a g e                                             %
1629 %                                                                             %
1630 %                                                                             %
1631 %                                                                             %
1632 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1633 %
1634 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1635 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
1636 %  necessary for the new Image structure and returns a pointer to the new
1637 %  image.
1638 %
1639 %  The format of the ReadOnePNGImage method is:
1640 %
1641 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1642 %         ExceptionInfo *exception)
1643 %
1644 %  A description of each parameter follows:
1645 %
1646 %    o mng_info: Specifies a pointer to a MngInfo structure.
1647 %
1648 %    o image_info: the image info.
1649 %
1650 %    o exception: return any errors or warnings in this structure.
1651 %
1652 */
1653 static Image *ReadOnePNGImage(MngInfo *mng_info,
1654     const ImageInfo *image_info, ExceptionInfo *exception)
1655 {
1656   /* Read one PNG image */
1657
1658   Image
1659     *image;
1660
1661   int
1662     logging,
1663     num_text,
1664     num_passes,
1665     pass,
1666     ping_bit_depth,
1667     ping_color_type,
1668     ping_interlace_method,
1669     ping_compression_method,
1670     ping_filter_method,
1671     ping_num_trans;
1672
1673   MagickBooleanType
1674     status;
1675
1676   PixelPacket
1677     transparent_color;
1678
1679   png_bytep
1680      ping_trans_alpha;
1681
1682   png_color_16p
1683      ping_background,
1684      ping_trans_color;
1685
1686   png_info
1687     *end_info,
1688     *ping_info;
1689
1690   png_struct
1691     *ping;
1692
1693   png_textp
1694     text;
1695
1696   png_uint_32
1697     ping_height,
1698     ping_width,
1699     ping_rowbytes;
1700
1701   QuantumInfo
1702     *quantum_info;
1703
1704   unsigned char
1705     *png_pixels;
1706
1707   long
1708     y;
1709
1710   register unsigned char
1711     *p;
1712
1713   register IndexPacket
1714     *indices;
1715
1716   register long
1717     i,
1718     x;
1719
1720   register PixelPacket
1721     *q;
1722
1723   size_t
1724     length;
1725
1726   unsigned long
1727     row_offset;
1728
1729 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1730   png_byte unused_chunks[]=
1731   {
1732     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
1733     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
1734     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
1735     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
1736     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
1737     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
1738   };
1739 #endif
1740
1741   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
1742     "  enter ReadOnePNGImage()");
1743
1744 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1745   LockSemaphoreInfo(png_semaphore);
1746 #endif
1747
1748 #if (PNG_LIBPNG_VER < 10200)
1749   if (image_info->verbose)
1750     printf("Your PNG library (libpng-%s) is rather old.\n",
1751        PNG_LIBPNG_VER_STRING);
1752 #endif
1753
1754 #if (PNG_LIBPNG_VER >= 10400)
1755 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
1756   if (image_info->verbose)
1757     {
1758       printf("Your PNG library (libpng-%s) is an old beta version.\n",
1759            PNG_LIBPNG_VER_STRING);
1760       printf("Please update it.\n");
1761     }
1762 #  endif
1763 #endif
1764
1765
1766   quantum_info = (QuantumInfo *) NULL;
1767   image=mng_info->image;
1768
1769   /*
1770     Allocate the PNG structures
1771   */
1772 #ifdef PNG_USER_MEM_SUPPORTED
1773  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
1774    PNGErrorHandler,PNGWarningHandler, NULL,
1775    (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
1776 #else
1777   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
1778     PNGErrorHandler,PNGWarningHandler);
1779 #endif
1780   if (ping == (png_struct *) NULL)
1781     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1782   ping_info=png_create_info_struct(ping);
1783   if (ping_info == (png_info *) NULL)
1784     {
1785       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
1786       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1787     }
1788   end_info=png_create_info_struct(ping);
1789   if (end_info == (png_info *) NULL)
1790     {
1791       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
1792       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1793     }
1794   png_pixels=(unsigned char *) NULL;
1795   if (setjmp(png_jmpbuf(ping)))
1796     {
1797       /*
1798         PNG image is corrupt.
1799       */
1800       png_destroy_read_struct(&ping,&ping_info,&end_info);
1801 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
1802       UnlockSemaphoreInfo(png_semaphore);
1803 #endif
1804       if (logging != MagickFalse)
1805         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1806           "  exit ReadOnePNGImage() with error.");
1807       if (image != (Image *) NULL)
1808         {
1809           InheritException(exception,&image->exception);
1810           image->columns=0;
1811         }
1812       return(GetFirstImageInList(image));
1813     }
1814   /*
1815     Prepare PNG for reading.
1816   */
1817
1818   mng_info->image_found++;
1819   png_set_sig_bytes(ping,8);
1820   if (LocaleCompare(image_info->magick,"MNG") == 0)
1821     {
1822 #if defined(PNG_MNG_FEATURES_SUPPORTED)
1823       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
1824       png_set_read_fn(ping,image,png_get_data);
1825 #else
1826 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
1827       png_permit_empty_plte(ping,MagickTrue);
1828       png_set_read_fn(ping,image,png_get_data);
1829 #else
1830       mng_info->image=image;
1831       mng_info->bytes_in_read_buffer=0;
1832       mng_info->found_empty_plte=MagickFalse;
1833       mng_info->have_saved_bkgd_index=MagickFalse;
1834       png_set_read_fn(ping,mng_info,mng_get_data);
1835 #endif
1836 #endif
1837     }
1838   else
1839     png_set_read_fn(ping,image,png_get_data);
1840
1841 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1842   /* Ignore unused chunks and all unknown chunks except for vpAg */
1843   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
1844   png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
1845   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
1846      (int)sizeof(unused_chunks)/5);
1847   /* Callback for other unknown chunks */
1848   png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
1849 #endif
1850
1851 #if (PNG_LIBPNG_VER < 10400)
1852 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
1853    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
1854   /* Disable thread-unsafe features of pnggccrd */
1855   if (png_access_version_number() >= 10200)
1856   {
1857     png_uint_32 mmx_disable_mask=0;
1858     png_uint_32 asm_flags;
1859
1860     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
1861                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
1862                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
1863                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
1864     asm_flags=png_get_asm_flags(ping);
1865     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
1866   }
1867 #  endif
1868 #endif
1869
1870   png_read_info(ping,ping_info);
1871
1872   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
1873                &ping_bit_depth,&ping_color_type,
1874                &ping_interlace_method,&ping_compression_method,
1875                &ping_filter_method);
1876
1877   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
1878                       &ping_trans_color);
1879
1880   (void) png_get_bKGD(ping, ping_info, &ping_background);
1881
1882   if (ping_bit_depth < 8)
1883     {
1884       if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
1885         {
1886           png_set_packing(ping);
1887           ping_bit_depth = 8;
1888         }
1889     }
1890
1891   image->depth=ping_bit_depth;
1892   image->depth=GetImageQuantumDepth(image,MagickFalse);
1893   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
1894   if (logging != MagickFalse)
1895     {
1896       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1897         "    PNG width: %lu, height: %lu",
1898         (unsigned long) ping_width, (unsigned long) ping_height);
1899       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1900         "    PNG color_type: %d, bit_depth: %d",
1901         ping_color_type, ping_bit_depth);
1902       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1903         "    PNG compression_method: %d",
1904         ping_compression_method);
1905       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1906         "    PNG interlace_method: %d, filter_method: %d",
1907         ping_interlace_method,ping_filter_method);
1908     }
1909
1910 #ifdef PNG_READ_iCCP_SUPPORTED
1911   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
1912     {
1913       int
1914         compression;
1915
1916       png_charp
1917         info,
1918         name;
1919
1920       png_uint_32
1921         profile_length;
1922
1923       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
1924         &profile_length);
1925       if (profile_length != 0)
1926         {
1927           StringInfo
1928             *profile;
1929
1930           if (logging != MagickFalse)
1931             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1932               "    Reading PNG iCCP chunk.");
1933           profile=AcquireStringInfo(profile_length);
1934           SetStringInfoDatum(profile,(const unsigned char *) info);
1935           (void) SetImageProfile(image,"icc",profile);
1936           profile=DestroyStringInfo(profile);
1937       }
1938     }
1939 #endif
1940 #if defined(PNG_READ_sRGB_SUPPORTED)
1941   {
1942     int
1943       intent;
1944
1945     if (mng_info->have_global_srgb)
1946       image->rendering_intent=(RenderingIntent)
1947         (mng_info->global_srgb_intent+1);
1948     if (png_get_sRGB(ping,ping_info,&intent))
1949       {
1950         image->rendering_intent=(RenderingIntent) (intent+1);
1951         if (logging != MagickFalse)
1952           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1953             "    Reading PNG sRGB chunk: rendering_intent: %d",intent+1);
1954       }
1955   }
1956 #endif
1957   {
1958      double
1959         file_gamma;
1960
1961      if (!png_get_gAMA(ping,ping_info,&file_gamma))
1962        if (mng_info->have_global_gama)
1963          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
1964      if (png_get_gAMA(ping,ping_info,&file_gamma))
1965        {
1966          image->gamma=(float) file_gamma;
1967          if (logging != MagickFalse)
1968            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1969              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
1970        }
1971   }
1972   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1973     {
1974       if (mng_info->have_global_chrm != MagickFalse)
1975         {
1976           (void) png_set_cHRM(ping,ping_info,
1977             mng_info->global_chrm.white_point.x,
1978             mng_info->global_chrm.white_point.y,
1979             mng_info->global_chrm.red_primary.x,
1980             mng_info->global_chrm.red_primary.y,
1981             mng_info->global_chrm.green_primary.x,
1982             mng_info->global_chrm.green_primary.y,
1983             mng_info->global_chrm.blue_primary.x,
1984             mng_info->global_chrm.blue_primary.y);
1985         }
1986     }
1987   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
1988     {
1989       (void) png_get_cHRM(ping,ping_info,
1990         &image->chromaticity.white_point.x,
1991         &image->chromaticity.white_point.y,
1992         &image->chromaticity.red_primary.x,
1993         &image->chromaticity.red_primary.y,
1994         &image->chromaticity.green_primary.x,
1995         &image->chromaticity.green_primary.y,
1996         &image->chromaticity.blue_primary.x,
1997         &image->chromaticity.blue_primary.y);
1998       if (logging != MagickFalse)
1999         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2000           "    Reading PNG cHRM chunk.");
2001     }
2002   if (image->rendering_intent)
2003     {
2004       png_set_sRGB(ping,ping_info,image->rendering_intent-1);
2005       png_set_gAMA(ping,ping_info,0.45455f);
2006       png_set_cHRM(ping,ping_info,
2007                   0.6400f, 0.3300f, 0.3000f, 0.6000f,
2008                   0.1500f, 0.0600f, 0.3127f, 0.3290f);
2009     }
2010 #if defined(PNG_oFFs_SUPPORTED)
2011   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2012     {
2013       image->page.x=png_get_x_offset_pixels(ping, ping_info);
2014       image->page.y=png_get_y_offset_pixels(ping, ping_info);
2015       if (logging != MagickFalse)
2016         if (image->page.x || image->page.y)
2017           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2018             "    Reading PNG oFFs chunk: x: %ld, y: %ld.",image->page.x,
2019             image->page.y);
2020     }
2021 #endif
2022 #if defined(PNG_pHYs_SUPPORTED)
2023   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2024     {
2025       if (mng_info->have_global_phys)
2026         {
2027           png_set_pHYs(ping,ping_info,
2028                        mng_info->global_x_pixels_per_unit,
2029                        mng_info->global_y_pixels_per_unit,
2030                        mng_info->global_phys_unit_type);
2031         }
2032     }
2033
2034   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2035     {
2036       int
2037         unit_type;
2038
2039       png_uint_32
2040         x_resolution,
2041         y_resolution;
2042
2043       /*
2044         Set image resolution.
2045       */
2046       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2047           &unit_type);
2048       image->x_resolution=(float) x_resolution;
2049       image->y_resolution=(float) y_resolution;
2050       if (unit_type == PNG_RESOLUTION_METER)
2051         {
2052           image->units=PixelsPerCentimeterResolution;
2053           image->x_resolution=(double) x_resolution/100.0;
2054           image->y_resolution=(double) y_resolution/100.0;
2055         }
2056       if (logging != MagickFalse)
2057         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2058           "    Reading PNG pHYs chunk: xres: %lu, yres: %lu, units: %d.",
2059           (unsigned long) x_resolution, (unsigned long) y_resolution,
2060           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 ",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",ping_width);
6493      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6494         "    height=%lu",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",ping_width);
7752       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7753         "    Height: %lu",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                (png_uint_32) (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",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