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