]> granicus.if.org Git - imagemagick/blob - coders/png.c
6ca8fb2ab073e612b976e3799d747a89965f459b
[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 ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2968       (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2969     image->colorspace=GRAYColorspace;
2970   if (png_get_text(ping,ping_info,&text,&num_text) != 0)
2971     for (i=0; i < (ssize_t) num_text; i++)
2972     {
2973       /* Check for a profile */
2974
2975       if (logging != MagickFalse)
2976         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2977           "    Reading PNG text chunk");
2978       if (memcmp(text[i].key, "Raw profile type ",17) == 0)
2979           (void) png_read_raw_profile(image,image_info,text,(int) i);
2980       else
2981         {
2982           char
2983             *value;
2984
2985           length=text[i].text_length;
2986           value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
2987             sizeof(*value));
2988           if (value == (char *) NULL)
2989             {
2990               (void) ThrowMagickException(&image->exception,GetMagickModule(),
2991                 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2992                 image->filename);
2993               break;
2994             }
2995           *value='\0';
2996           (void) ConcatenateMagickString(value,text[i].text,length+2);
2997           (void) SetImageProperty(image,text[i].key,value);
2998           if (logging != MagickFalse)
2999             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3000               "      Keyword: %s",text[i].key);
3001           value=DestroyString(value);
3002         }
3003     }
3004 #ifdef MNG_OBJECT_BUFFERS
3005   /*
3006     Store the object if necessary.
3007   */
3008   if (object_id && !mng_info->frozen[object_id])
3009     {
3010       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3011         {
3012           /*
3013             create a new object buffer.
3014           */
3015           mng_info->ob[object_id]=(MngBuffer *)
3016             AcquireAlignedMemory(1,sizeof(MngBuffer));
3017           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3018             {
3019               mng_info->ob[object_id]->image=(Image *) NULL;
3020               mng_info->ob[object_id]->reference_count=1;
3021             }
3022         }
3023       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3024           mng_info->ob[object_id]->frozen)
3025         {
3026           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3027             (void) ThrowMagickException(&image->exception,GetMagickModule(),
3028               ResourceLimitError,"MemoryAllocationFailed","`%s'",
3029               image->filename);
3030           if (mng_info->ob[object_id]->frozen)
3031             (void) ThrowMagickException(&image->exception,GetMagickModule(),
3032               ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3033               "`%s'",image->filename);
3034         }
3035       else
3036         {
3037
3038           if (mng_info->ob[object_id]->image != (Image *) NULL)
3039             mng_info->ob[object_id]->image=DestroyImage
3040                 (mng_info->ob[object_id]->image);
3041           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3042             &image->exception);
3043           if (mng_info->ob[object_id]->image != (Image *) NULL)
3044             mng_info->ob[object_id]->image->file=(FILE *) NULL;
3045           else
3046             (void) ThrowMagickException(&image->exception,GetMagickModule(),
3047               ResourceLimitError,"Cloning image for object buffer failed",
3048               "`%s'",image->filename);
3049           if (ping_width > 250000L || ping_height > 250000L)
3050              png_error(ping,"PNG Image dimensions are too large.");
3051           mng_info->ob[object_id]->width=ping_width;
3052           mng_info->ob[object_id]->height=ping_height;
3053           mng_info->ob[object_id]->color_type=ping_color_type;
3054           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3055           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3056           mng_info->ob[object_id]->compression_method=
3057              ping_compression_method;
3058           mng_info->ob[object_id]->filter_method=ping_filter_method;
3059           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3060             {
3061               int
3062                 number_colors;
3063
3064               png_colorp
3065                 plte;
3066
3067               /*
3068                 Copy the PLTE to the object buffer.
3069               */
3070               png_get_PLTE(ping,ping_info,&plte,&number_colors);
3071               mng_info->ob[object_id]->plte_length=number_colors;
3072               for (i=0; i < number_colors; i++)
3073               {
3074                 mng_info->ob[object_id]->plte[i]=plte[i];
3075               }
3076             }
3077           else
3078               mng_info->ob[object_id]->plte_length=0;
3079         }
3080     }
3081 #endif
3082   /*
3083     Relinquish resources.
3084   */
3085   png_destroy_read_struct(&ping,&ping_info,&end_info);
3086
3087   png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
3088 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
3089   UnlockSemaphoreInfo(png_semaphore);
3090 #endif
3091
3092   if (logging != MagickFalse)
3093     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3094       "  exit ReadOnePNGImage()");
3095   return(image);
3096
3097 /* end of reading one PNG image */
3098 }
3099
3100 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3101 {
3102   Image
3103     *image,
3104     *previous;
3105
3106   MagickBooleanType
3107     status;
3108
3109   MngInfo
3110     *mng_info;
3111
3112   char
3113     magic_number[MaxTextExtent];
3114
3115   int
3116     have_mng_structure,
3117     logging;
3118
3119   ssize_t
3120     count;
3121
3122   /*
3123     Open image file.
3124   */
3125   assert(image_info != (const ImageInfo *) NULL);
3126   assert(image_info->signature == MagickSignature);
3127   if (image_info->debug != MagickFalse)
3128     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3129       image_info->filename);
3130   assert(exception != (ExceptionInfo *) NULL);
3131   assert(exception->signature == MagickSignature);
3132   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadPNGImage()");
3133   image=AcquireImage(image_info);
3134   mng_info=(MngInfo *) NULL;
3135   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3136   if (status == MagickFalse)
3137     ThrowReaderException(FileOpenError,"UnableToOpenFile");
3138   /*
3139     Verify PNG signature.
3140   */
3141   count=ReadBlob(image,8,(unsigned char *) magic_number);
3142   if (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3143     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3144   /*
3145     Allocate a MngInfo structure.
3146   */
3147   have_mng_structure=MagickFalse;
3148   mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
3149   if (mng_info == (MngInfo *) NULL)
3150     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3151   /*
3152     Initialize members of the MngInfo structure.
3153   */
3154   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3155   mng_info->image=image;
3156   have_mng_structure=MagickTrue;
3157
3158   previous=image;
3159   image=ReadOnePNGImage(mng_info,image_info,exception);
3160   MngInfoFreeStruct(mng_info,&have_mng_structure);
3161   if (image == (Image *) NULL)
3162     {
3163       if (previous != (Image *) NULL)
3164         {
3165           if (previous->signature != MagickSignature)
3166             ThrowReaderException(CorruptImageError,"CorruptImage");
3167           (void) CloseBlob(previous);
3168           (void) DestroyImageList(previous);
3169         }
3170       if (logging != MagickFalse)
3171         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3172           "exit ReadPNGImage() with error");
3173       return((Image *) NULL);
3174     }
3175   (void) CloseBlob(image);
3176   if ((image->columns == 0) || (image->rows == 0))
3177     {
3178       if (logging != MagickFalse)
3179         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3180           "exit ReadPNGImage() with error.");
3181       ThrowReaderException(CorruptImageError,"CorruptImage");
3182     }
3183   if (LocaleCompare(image_info->magick,"PNG8") == 0)
3184     {
3185       (void) SetImageType(image,PaletteType);
3186       if (image->matte != MagickFalse)
3187         {
3188           /* To do: Reduce to binary transparency */
3189         }
3190     }
3191   if (LocaleCompare(image_info->magick,"PNG24") == 0)
3192     {
3193       (void) SetImageType(image,TrueColorType);
3194       image->matte=MagickFalse;
3195     }
3196   if (LocaleCompare(image_info->magick,"PNG32") == 0)
3197     (void) SetImageType(image,TrueColorMatteType);
3198   if (logging != MagickFalse)
3199     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
3200   return(image);
3201 }
3202
3203
3204
3205 #if defined(JNG_SUPPORTED)
3206 /*
3207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3208 %                                                                             %
3209 %                                                                             %
3210 %                                                                             %
3211 %   R e a d O n e J N G I m a g e                                             %
3212 %                                                                             %
3213 %                                                                             %
3214 %                                                                             %
3215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3216 %
3217 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3218 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
3219 %  necessary for the new Image structure and returns a pointer to the new
3220 %  image.
3221 %
3222 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
3223 %
3224 %  The format of the ReadOneJNGImage method is:
3225 %
3226 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3227 %         ExceptionInfo *exception)
3228 %
3229 %  A description of each parameter follows:
3230 %
3231 %    o mng_info: Specifies a pointer to a MngInfo structure.
3232 %
3233 %    o image_info: the image info.
3234 %
3235 %    o exception: return any errors or warnings in this structure.
3236 %
3237 */
3238 static Image *ReadOneJNGImage(MngInfo *mng_info,
3239     const ImageInfo *image_info, ExceptionInfo *exception)
3240 {
3241   Image
3242     *alpha_image,
3243     *color_image,
3244     *image,
3245     *jng_image;
3246
3247   ImageInfo
3248     *alpha_image_info,
3249     *color_image_info;
3250
3251   ssize_t
3252     y;
3253
3254   MagickBooleanType
3255     status;
3256
3257   png_uint_32
3258     jng_height,
3259     jng_width;
3260
3261   png_byte
3262     jng_color_type,
3263     jng_image_sample_depth,
3264     jng_image_compression_method,
3265     jng_image_interlace_method,
3266     jng_alpha_sample_depth,
3267     jng_alpha_compression_method,
3268     jng_alpha_filter_method,
3269     jng_alpha_interlace_method;
3270
3271   register const PixelPacket
3272     *s;
3273
3274   register ssize_t
3275     i,
3276     x;
3277
3278   register PixelPacket
3279     *q;
3280
3281   register unsigned char
3282     *p;
3283
3284   unsigned int
3285     logging,
3286     read_JSEP,
3287     reading_idat,
3288     skip_to_iend;
3289
3290   size_t
3291     length;
3292
3293   jng_alpha_compression_method=0;
3294   jng_alpha_sample_depth=8;
3295   jng_color_type=0;
3296   jng_height=0;
3297   jng_width=0;
3298   alpha_image=(Image *) NULL;
3299   color_image=(Image *) NULL;
3300   alpha_image_info=(ImageInfo *) NULL;
3301   color_image_info=(ImageInfo *) NULL;
3302
3303   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
3304     "  enter ReadOneJNGImage()");
3305
3306   image=mng_info->image;
3307   if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3308     {
3309       /*
3310         Allocate next image structure.
3311       */
3312       if (logging != MagickFalse)
3313         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3314            "  AcquireNextImage()");
3315       AcquireNextImage(image_info,image);
3316       if (GetNextImageInList(image) == (Image *) NULL)
3317         return((Image *) NULL);
3318       image=SyncNextImageInList(image);
3319     }
3320   mng_info->image=image;
3321
3322   /*
3323     Signature bytes have already been read.
3324   */
3325
3326   read_JSEP=MagickFalse;
3327   reading_idat=MagickFalse;
3328   skip_to_iend=MagickFalse;
3329   for (;;)
3330   {
3331     char
3332       type[MaxTextExtent];
3333
3334     unsigned char
3335       *chunk;
3336
3337     unsigned int
3338       count;
3339
3340     /*
3341       Read a new JNG chunk.
3342     */
3343     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3344       2*GetBlobSize(image));
3345     if (status == MagickFalse)
3346       break;
3347     type[0]='\0';
3348     (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3349     length=ReadBlobMSBLong(image);
3350     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3351
3352     if (logging != MagickFalse)
3353       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3354         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
3355         type[0],type[1],type[2],type[3],(double) length);
3356
3357     if (length > PNG_UINT_31_MAX || count == 0)
3358       ThrowReaderException(CorruptImageError,"CorruptImage");
3359     p=NULL;
3360     chunk=(unsigned char *) NULL;
3361     if (length)
3362       {
3363         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
3364         if (chunk == (unsigned char *) NULL)
3365           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3366         for (i=0; i < (ssize_t) length; i++)
3367           chunk[i]=(unsigned char) ReadBlobByte(image);
3368         p=chunk;
3369       }
3370     (void) ReadBlobMSBLong(image);  /* read crc word */
3371
3372     if (skip_to_iend)
3373       {
3374         if (length)
3375           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3376         continue;
3377       }
3378
3379     if (memcmp(type,mng_JHDR,4) == 0)
3380       {
3381         if (length == 16)
3382           {
3383             jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
3384                 (p[2] << 8) | p[3]);
3385             jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
3386                 (p[6] << 8) | p[7]);
3387             jng_color_type=p[8];
3388             jng_image_sample_depth=p[9];
3389             jng_image_compression_method=p[10];
3390             jng_image_interlace_method=p[11];
3391             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3392               NoInterlace;
3393             jng_alpha_sample_depth=p[12];
3394             jng_alpha_compression_method=p[13];
3395             jng_alpha_filter_method=p[14];
3396             jng_alpha_interlace_method=p[15];
3397             if (logging != MagickFalse)
3398               {
3399                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3400                   "    jng_width:      %16lu",(unsigned long) jng_width);
3401                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3402                   "    jng_width:      %16lu",(unsigned long) jng_height);
3403                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3404                   "    jng_color_type: %16d",jng_color_type);
3405                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3406                   "    jng_image_sample_depth:      %3d",
3407                   jng_image_sample_depth);
3408                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3409                   "    jng_image_compression_method:%3d",
3410                   jng_image_compression_method);
3411                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3412                   "    jng_image_interlace_method:  %3d",
3413                   jng_image_interlace_method);
3414                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3415                   "    jng_alpha_sample_depth:      %3d",
3416                   jng_alpha_sample_depth);
3417                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3418                   "    jng_alpha_compression_method:%3d",
3419                   jng_alpha_compression_method);
3420                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3421                   "    jng_alpha_filter_method:     %3d",
3422                   jng_alpha_filter_method);
3423                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3424                   "    jng_alpha_interlace_method:  %3d",
3425                   jng_alpha_interlace_method);
3426               }
3427           }
3428         if (length)
3429           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3430         continue;
3431       }
3432
3433
3434     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3435         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3436          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3437       {
3438         /*
3439            o create color_image
3440            o open color_blob, attached to color_image
3441            o if (color type has alpha)
3442                open alpha_blob, attached to alpha_image
3443         */
3444
3445         color_image_info=(ImageInfo *)AcquireAlignedMemory(1,sizeof(ImageInfo));
3446         if (color_image_info == (ImageInfo *) NULL)
3447           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3448         GetImageInfo(color_image_info);
3449         color_image=AcquireImage(color_image_info);
3450         if (color_image == (Image *) NULL)
3451           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3452
3453         if (logging != MagickFalse)
3454           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3455             "    Creating color_blob.");
3456         (void) AcquireUniqueFilename(color_image->filename);
3457         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3458           exception);
3459         if (status == MagickFalse)
3460           return((Image *) NULL);
3461
3462         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3463           {
3464             alpha_image_info=(ImageInfo *)
3465               AcquireAlignedMemory(1,sizeof(ImageInfo));
3466             if (alpha_image_info == (ImageInfo *) NULL)
3467               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3468             GetImageInfo(alpha_image_info);
3469             alpha_image=AcquireImage(alpha_image_info);
3470             if (alpha_image == (Image *) NULL)
3471               {
3472                 alpha_image=DestroyImage(alpha_image);
3473                 ThrowReaderException(ResourceLimitError,
3474                   "MemoryAllocationFailed");
3475               }
3476             if (logging != MagickFalse)
3477               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3478                 "    Creating alpha_blob.");
3479             (void) AcquireUniqueFilename(alpha_image->filename);
3480             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
3481               exception);
3482             if (status == MagickFalse)
3483               return((Image *) NULL);
3484             if (jng_alpha_compression_method == 0)
3485               {
3486                 unsigned char
3487                   data[18];
3488
3489                 if (logging != MagickFalse)
3490                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3491                     "    Writing IHDR chunk to alpha_blob.");
3492                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
3493                   "\211PNG\r\n\032\n");
3494                 (void) WriteBlobMSBULong(alpha_image,13L);
3495                 PNGType(data,mng_IHDR);
3496                 LogPNGChunk((int) logging,mng_IHDR,13L);
3497                 PNGLong(data+4,jng_width);
3498                 PNGLong(data+8,jng_height);
3499                 data[12]=jng_alpha_sample_depth;
3500                 data[13]=0; /* color_type gray */
3501                 data[14]=0; /* compression method 0 */
3502                 data[15]=0; /* filter_method 0 */
3503                 data[16]=0; /* interlace_method 0 */
3504                 (void) WriteBlob(alpha_image,17,data);
3505                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
3506               }
3507           }
3508         reading_idat=MagickTrue;
3509       }
3510
3511     if (memcmp(type,mng_JDAT,4) == 0)
3512       {
3513         /*
3514            Copy chunk to color_image->blob
3515         */
3516
3517         if (logging != MagickFalse)
3518           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3519             "    Copying JDAT chunk data to color_blob.");
3520
3521         (void) WriteBlob(color_image,length,chunk);
3522         if (length)
3523           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3524         continue;
3525       }
3526
3527     if (memcmp(type,mng_IDAT,4) == 0)
3528       {
3529         png_byte
3530            data[5];
3531
3532         /*
3533            Copy IDAT header and chunk data to alpha_image->blob
3534         */
3535
3536         if (image_info->ping == MagickFalse)
3537           {
3538             if (logging != MagickFalse)
3539               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3540                 "    Copying IDAT chunk data to alpha_blob.");
3541
3542             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
3543             PNGType(data,mng_IDAT);
3544             LogPNGChunk((int) logging,mng_IDAT,length);
3545             (void) WriteBlob(alpha_image,4,data);
3546             (void) WriteBlob(alpha_image,length,chunk);
3547             (void) WriteBlobMSBULong(alpha_image,
3548               crc32(crc32(0,data,4),chunk,(uInt) length));
3549           }
3550         if (length)
3551           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3552         continue;
3553       }
3554
3555     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
3556       {
3557         /*
3558            Copy chunk data to alpha_image->blob
3559         */
3560
3561         if (image_info->ping == MagickFalse)
3562           {
3563             if (logging != MagickFalse)
3564               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3565                 "    Copying JDAA chunk data to alpha_blob.");
3566
3567             (void) WriteBlob(alpha_image,length,chunk);
3568           }
3569         if (length)
3570           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3571         continue;
3572       }
3573
3574     if (memcmp(type,mng_JSEP,4) == 0)
3575       {
3576         read_JSEP=MagickTrue;
3577         if (length)
3578           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3579         continue;
3580       }
3581
3582     if (memcmp(type,mng_bKGD,4) == 0)
3583       {
3584         if (length == 2)
3585           {
3586             image->background_color.red=ScaleCharToQuantum(p[1]);
3587             image->background_color.green=image->background_color.red;
3588             image->background_color.blue=image->background_color.red;
3589           }
3590         if (length == 6)
3591           {
3592             image->background_color.red=ScaleCharToQuantum(p[1]);
3593             image->background_color.green=ScaleCharToQuantum(p[3]);
3594             image->background_color.blue=ScaleCharToQuantum(p[5]);
3595           }
3596         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3597         continue;
3598       }
3599
3600     if (memcmp(type,mng_gAMA,4) == 0)
3601       {
3602         if (length == 4)
3603           image->gamma=((float) mng_get_long(p))*0.00001;
3604         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3605         continue;
3606       }
3607
3608     if (memcmp(type,mng_cHRM,4) == 0)
3609       {
3610         if (length == 32)
3611           {
3612             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
3613             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
3614             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
3615             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
3616             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
3617             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
3618             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
3619             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
3620           }
3621         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3622         continue;
3623       }
3624
3625     if (memcmp(type,mng_sRGB,4) == 0)
3626       {
3627         if (length == 1)
3628           {
3629             image->rendering_intent=
3630               PNG_RenderingIntent_to_Magick_RenderingIntent(p[0]);
3631             image->gamma=0.45455f;
3632             image->chromaticity.red_primary.x=0.6400f;
3633             image->chromaticity.red_primary.y=0.3300f;
3634             image->chromaticity.green_primary.x=0.3000f;
3635             image->chromaticity.green_primary.y=0.6000f;
3636             image->chromaticity.blue_primary.x=0.1500f;
3637             image->chromaticity.blue_primary.y=0.0600f;
3638             image->chromaticity.white_point.x=0.3127f;
3639             image->chromaticity.white_point.y=0.3290f;
3640           }
3641         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3642         continue;
3643       }
3644
3645     if (memcmp(type,mng_oFFs,4) == 0)
3646       {
3647         if (length > 8)
3648           {
3649             image->page.x=mng_get_long(p);
3650             image->page.y=mng_get_long(&p[4]);
3651             if ((int) p[8] != 0)
3652               {
3653                 image->page.x/=10000;
3654                 image->page.y/=10000;
3655               }
3656           }
3657         if (length)
3658           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3659         continue;
3660       }
3661
3662     if (memcmp(type,mng_pHYs,4) == 0)
3663       {
3664         if (length > 8)
3665           {
3666             image->x_resolution=(double) mng_get_long(p);
3667             image->y_resolution=(double) mng_get_long(&p[4]);
3668             if ((int) p[8] == PNG_RESOLUTION_METER)
3669               {
3670                 image->units=PixelsPerCentimeterResolution;
3671                 image->x_resolution=image->x_resolution/100.0f;
3672                 image->y_resolution=image->y_resolution/100.0f;
3673               }
3674           }
3675         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3676         continue;
3677       }
3678
3679 #if 0
3680     if (memcmp(type,mng_iCCP,4) == 0)
3681       {
3682         /* To do. */
3683         if (length)
3684           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3685         continue;
3686       }
3687 #endif
3688
3689     if (length)
3690       chunk=(unsigned char *) RelinquishMagickMemory(chunk);
3691
3692     if (memcmp(type,mng_IEND,4))
3693       continue;
3694     break;
3695   }
3696
3697
3698   /* IEND found */
3699
3700   /*
3701     Finish up reading image data:
3702
3703        o read main image from color_blob.
3704
3705        o close color_blob.
3706
3707        o if (color_type has alpha)
3708             if alpha_encoding is PNG
3709                read secondary image from alpha_blob via ReadPNG
3710             if alpha_encoding is JPEG
3711                read secondary image from alpha_blob via ReadJPEG
3712
3713        o close alpha_blob.
3714
3715        o copy intensity of secondary image into
3716          opacity samples of main image.
3717
3718        o destroy the secondary image.
3719   */
3720
3721   (void) CloseBlob(color_image);
3722   if (logging != MagickFalse)
3723     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3724       "    Reading jng_image from color_blob.");
3725   (void) FormatMagickString(color_image_info->filename,MaxTextExtent,"%s",
3726     color_image->filename);
3727   color_image_info->ping=MagickFalse;   /* To do: avoid this */
3728   jng_image=ReadImage(color_image_info,exception);
3729   if (jng_image == (Image *) NULL)
3730     return((Image *) NULL);
3731
3732   (void) RelinquishUniqueFileResource(color_image->filename);
3733   color_image=DestroyImage(color_image);
3734   color_image_info=DestroyImageInfo(color_image_info);
3735
3736   if (jng_image == (Image *) NULL)
3737     return((Image *) NULL);
3738
3739   if (logging != MagickFalse)
3740     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3741       "    Copying jng_image pixels to main image.");
3742   image->rows=jng_height;
3743   image->columns=jng_width;
3744   length=image->columns*sizeof(PixelPacket);
3745   for (y=0; y < (ssize_t) image->rows; y++)
3746   {
3747     s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
3748     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3749     (void) CopyMagickMemory(q,s,length);
3750     if (SyncAuthenticPixels(image,exception) == MagickFalse)
3751       break;
3752   }
3753   jng_image=DestroyImage(jng_image);
3754   if (image_info->ping == MagickFalse)
3755     {
3756      if (jng_color_type >= 12)
3757        {
3758          if (jng_alpha_compression_method == 0)
3759            {
3760              png_byte
3761                data[5];
3762              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
3763              PNGType(data,mng_IEND);
3764              LogPNGChunk((int) logging,mng_IEND,0L);
3765              (void) WriteBlob(alpha_image,4,data);
3766              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
3767            }
3768          (void) CloseBlob(alpha_image);
3769          if (logging != MagickFalse)
3770            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3771              "    Reading opacity from alpha_blob.");
3772
3773          (void) FormatMagickString(alpha_image_info->filename,MaxTextExtent,
3774            "%s",alpha_image->filename);
3775
3776          jng_image=ReadImage(alpha_image_info,exception);
3777          if (jng_image != (Image *) NULL)
3778            for (y=0; y < (ssize_t) image->rows; y++)
3779            {
3780              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
3781                 &image->exception);
3782              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3783              if (image->matte != MagickFalse)
3784                for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3785                   q->opacity=(Quantum) QuantumRange-s->red;
3786              else
3787                for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
3788                {
3789                   q->opacity=(Quantum) QuantumRange-s->red;
3790                   if (q->opacity != OpaqueOpacity)
3791                     image->matte=MagickTrue;
3792                }
3793              if (SyncAuthenticPixels(image,exception) == MagickFalse)
3794                break;
3795            }
3796          (void) RelinquishUniqueFileResource(alpha_image->filename);
3797          alpha_image=DestroyImage(alpha_image);
3798          alpha_image_info=DestroyImageInfo(alpha_image_info);
3799          if (jng_image != (Image *) NULL)
3800            jng_image=DestroyImage(jng_image);
3801        }
3802     }
3803
3804   /*
3805     Read the JNG image.
3806   */
3807   if (mng_info->mng_type == 0)
3808     {
3809       mng_info->mng_width=jng_width;
3810       mng_info->mng_height=jng_height;
3811     }
3812   if (image->page.width == 0 && image->page.height == 0)
3813   {
3814     image->page.width=jng_width;
3815     image->page.height=jng_height;
3816   }
3817   if (image->page.x == 0 && image->page.y == 0)
3818   {
3819     image->page.x=mng_info->x_off[mng_info->object_id];
3820     image->page.y=mng_info->y_off[mng_info->object_id];
3821   }
3822   else
3823   {
3824     image->page.y=mng_info->y_off[mng_info->object_id];
3825   }
3826   mng_info->image_found++;
3827   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
3828     2*GetBlobSize(image));
3829   if (logging != MagickFalse)
3830     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3831       "  exit ReadOneJNGImage()");
3832   return(image);
3833 }
3834
3835 /*
3836 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3837 %                                                                             %
3838 %                                                                             %
3839 %                                                                             %
3840 %   R e a d J N G I m a g e                                                   %
3841 %                                                                             %
3842 %                                                                             %
3843 %                                                                             %
3844 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3845 %
3846 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
3847 %  (including the 8-byte signature)  and returns it.  It allocates the memory
3848 %  necessary for the new Image structure and returns a pointer to the new
3849 %  image.
3850 %
3851 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
3852 %
3853 %  The format of the ReadJNGImage method is:
3854 %
3855 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
3856 %         *exception)
3857 %
3858 %  A description of each parameter follows:
3859 %
3860 %    o image_info: the image info.
3861 %
3862 %    o exception: return any errors or warnings in this structure.
3863 %
3864 */
3865
3866 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3867 {
3868   Image
3869     *image,
3870     *previous;
3871
3872   MagickBooleanType
3873     status;
3874
3875   MngInfo
3876     *mng_info;
3877
3878   char
3879     magic_number[MaxTextExtent];
3880
3881   int
3882     have_mng_structure,
3883     logging;
3884
3885   size_t
3886     count;
3887
3888   /*
3889     Open image file.
3890   */
3891   assert(image_info != (const ImageInfo *) NULL);
3892   assert(image_info->signature == MagickSignature);
3893   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
3894   assert(exception != (ExceptionInfo *) NULL);
3895   assert(exception->signature == MagickSignature);
3896   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadJNGImage()");
3897   image=AcquireImage(image_info);
3898   mng_info=(MngInfo *) NULL;
3899   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3900   if (status == MagickFalse)
3901     return((Image *) NULL);
3902   if (LocaleCompare(image_info->magick,"JNG") != 0)
3903     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3904   /*
3905     Verify JNG signature.
3906   */
3907   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
3908   if (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
3909     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3910   /*
3911     Allocate a MngInfo structure.
3912   */
3913   have_mng_structure=MagickFalse;
3914   mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(*mng_info));
3915   if (mng_info == (MngInfo *) NULL)
3916     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3917   /*
3918     Initialize members of the MngInfo structure.
3919   */
3920   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3921   have_mng_structure=MagickTrue;
3922
3923   mng_info->image=image;
3924   previous=image;
3925   image=ReadOneJNGImage(mng_info,image_info,exception);
3926   MngInfoFreeStruct(mng_info,&have_mng_structure);
3927   if (image == (Image *) NULL)
3928     {
3929       if (IsImageObject(previous) != MagickFalse)
3930         {
3931           (void) CloseBlob(previous);
3932           (void) DestroyImageList(previous);
3933         }
3934       if (logging != MagickFalse)
3935         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3936           "exit ReadJNGImage() with error");
3937       return((Image *) NULL);
3938     }
3939   (void) CloseBlob(image);
3940   if (image->columns == 0 || image->rows == 0)
3941     {
3942       if (logging != MagickFalse)
3943         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3944           "exit ReadJNGImage() with error");
3945       ThrowReaderException(CorruptImageError,"CorruptImage");
3946     }
3947   if (logging != MagickFalse)
3948     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
3949   return(image);
3950 }
3951 #endif
3952
3953 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3954 {
3955   char
3956     page_geometry[MaxTextExtent];
3957
3958   Image
3959     *image,
3960     *previous;
3961
3962   int
3963     have_mng_structure;
3964
3965   volatile int
3966     first_mng_object,
3967     logging,
3968     object_id,
3969     term_chunk_found,
3970     skip_to_iend;
3971
3972   volatile ssize_t
3973     image_count=0;
3974
3975   MagickBooleanType
3976     status;
3977
3978   MagickOffsetType
3979     offset;
3980
3981   MngInfo
3982     *mng_info;
3983
3984   MngBox
3985     default_fb,
3986     fb,
3987     previous_fb;
3988
3989 #if defined(MNG_INSERT_LAYERS)
3990   PixelPacket
3991     mng_background_color;
3992 #endif
3993
3994   register unsigned char
3995     *p;
3996
3997   register ssize_t
3998     i;
3999
4000   size_t
4001     count;
4002
4003   ssize_t
4004     loop_level;
4005
4006   volatile short
4007     skipping_loop;
4008
4009 #if defined(MNG_INSERT_LAYERS)
4010   unsigned int
4011     mandatory_back=0;
4012 #endif
4013
4014   volatile unsigned int
4015 #ifdef MNG_OBJECT_BUFFERS
4016     mng_background_object=0,
4017 #endif
4018     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4019
4020   size_t
4021     default_frame_timeout,
4022     frame_timeout,
4023 #if defined(MNG_INSERT_LAYERS)
4024     image_height,
4025     image_width,
4026 #endif
4027     length;
4028
4029   /* These delays are all measured in image ticks_per_second,
4030    * not in MNG ticks_per_second
4031    */
4032   volatile size_t
4033     default_frame_delay,
4034     final_delay,
4035     final_image_delay,
4036     frame_delay,
4037 #if defined(MNG_INSERT_LAYERS)
4038     insert_layers,
4039 #endif
4040     mng_iterations=1,
4041     simplicity=0,
4042     subframe_height=0,
4043     subframe_width=0;
4044
4045   previous_fb.top=0;
4046   previous_fb.bottom=0;
4047   previous_fb.left=0;
4048   previous_fb.right=0;
4049   default_fb.top=0;
4050   default_fb.bottom=0;
4051   default_fb.left=0;
4052   default_fb.right=0;
4053
4054   /*
4055     Set image_info->type=OptimizeType (new in version 5.4.0) to get the
4056     following optimizations:
4057
4058     o  16-bit depth is reduced to 8 if all pixels contain samples whose
4059        high byte and low byte are identical.
4060     o  Opaque matte channel is removed.
4061     o  If matte channel is present but only one transparent color is
4062        present, RGB+tRNS is written instead of RGBA
4063     o  Grayscale images are reduced to 1, 2, or 4 bit depth if
4064        this can be done without loss.
4065     o  Palette is sorted to remove unused entries and to put a
4066        transparent color first, if PNG_SORT_PALETTE is also defined.
4067    */
4068
4069   /*
4070     Open image file.
4071   */
4072   assert(image_info != (const ImageInfo *) NULL);
4073   assert(image_info->signature == MagickSignature);
4074   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4075   assert(exception != (ExceptionInfo *) NULL);
4076   assert(exception->signature == MagickSignature);
4077   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter ReadMNGImage()");
4078   image=AcquireImage(image_info);
4079   mng_info=(MngInfo *) NULL;
4080   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4081   if (status == MagickFalse)
4082     return((Image *) NULL);
4083   first_mng_object=MagickFalse;
4084   skipping_loop=(-1);
4085   have_mng_structure=MagickFalse;
4086   /*
4087     Allocate a MngInfo structure.
4088   */
4089   mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
4090   if (mng_info == (MngInfo *) NULL)
4091     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4092   /*
4093     Initialize members of the MngInfo structure.
4094   */
4095   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4096   mng_info->image=image;
4097   have_mng_structure=MagickTrue;
4098 #if (MAGICKCORE_QUANTUM_DEPTH == 16)
4099     mng_info->optimize=image_info->type == OptimizeType;
4100 #endif
4101
4102   if (LocaleCompare(image_info->magick,"MNG") == 0)
4103     {
4104       char
4105         magic_number[MaxTextExtent];
4106
4107       /*
4108         Verify MNG signature.
4109       */
4110       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4111       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4112         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4113       /*
4114         Initialize some nonzero members of the MngInfo structure.
4115       */
4116       for (i=0; i < MNG_MAX_OBJECTS; i++)
4117       {
4118         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4119         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
4120       }
4121       mng_info->exists[0]=MagickTrue;
4122     }
4123   first_mng_object=MagickTrue;
4124   mng_type=0;
4125 #if defined(MNG_INSERT_LAYERS)
4126   insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4127 #endif
4128   default_frame_delay=0;
4129   default_frame_timeout=0;
4130   frame_delay=0;
4131   final_delay=1;
4132   mng_info->ticks_per_second=1UL*image->ticks_per_second;
4133   object_id=0;
4134   skip_to_iend=MagickFalse;
4135   term_chunk_found=MagickFalse;
4136   mng_info->framing_mode=1;
4137 #if defined(MNG_INSERT_LAYERS)
4138   mandatory_back=MagickFalse;
4139 #endif
4140 #if defined(MNG_INSERT_LAYERS)
4141   mng_background_color=image->background_color;
4142 #endif
4143   default_fb=mng_info->frame;
4144   previous_fb=mng_info->frame;
4145   do
4146   {
4147     char
4148       type[MaxTextExtent];
4149
4150     if (LocaleCompare(image_info->magick,"MNG") == 0)
4151       {
4152         unsigned char
4153           *chunk;
4154
4155         /*
4156           Read a new chunk.
4157         */
4158         type[0]='\0';
4159         (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4160         length=ReadBlobMSBLong(image);
4161         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4162
4163         if (logging != MagickFalse)
4164           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4165            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
4166            type[0],type[1],type[2],type[3],(double) length);
4167
4168         if (length > PNG_UINT_31_MAX)
4169           status=MagickFalse;
4170         if (count == 0)
4171           ThrowReaderException(CorruptImageError,"CorruptImage");
4172         p=NULL;
4173         chunk=(unsigned char *) NULL;
4174         if (length)
4175           {
4176             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4177             if (chunk == (unsigned char *) NULL)
4178               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4179             for (i=0; i < (ssize_t) length; i++)
4180               chunk[i]=(unsigned char) ReadBlobByte(image);
4181             p=chunk;
4182           }
4183         (void) ReadBlobMSBLong(image);  /* read crc word */
4184
4185 #if !defined(JNG_SUPPORTED)
4186         if (memcmp(type,mng_JHDR,4) == 0)
4187           {
4188             skip_to_iend=MagickTrue;
4189             if (mng_info->jhdr_warning == 0)
4190               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4191                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
4192             mng_info->jhdr_warning++;
4193           }
4194 #endif
4195         if (memcmp(type,mng_DHDR,4) == 0)
4196           {
4197             skip_to_iend=MagickTrue;
4198             if (mng_info->dhdr_warning == 0)
4199               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4200                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
4201             mng_info->dhdr_warning++;
4202           }
4203         if (memcmp(type,mng_MEND,4) == 0)
4204           break;
4205         if (skip_to_iend)
4206           {
4207             if (memcmp(type,mng_IEND,4) == 0)
4208               skip_to_iend=MagickFalse;
4209             if (length)
4210               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4211             if (logging != MagickFalse)
4212               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4213                 "  Skip to IEND.");
4214             continue;
4215           }
4216         if (memcmp(type,mng_MHDR,4) == 0)
4217           {
4218             mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4219                 (p[2] << 8) | p[3]);
4220             mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4221                 (p[6] << 8) | p[7]);
4222             if (logging != MagickFalse)
4223               {
4224                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4225                   "  MNG width: %.20g",(double) mng_info->mng_width);
4226                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4227                   "  MNG height: %.20g",(double) mng_info->mng_height);
4228               }
4229             p+=8;
4230             mng_info->ticks_per_second=(size_t) mng_get_long(p);
4231             if (mng_info->ticks_per_second == 0)
4232               default_frame_delay=0;
4233             else
4234               default_frame_delay=1UL*image->ticks_per_second/
4235                 mng_info->ticks_per_second;
4236             frame_delay=default_frame_delay;
4237             simplicity=0;
4238             if (length > 16)
4239               {
4240                 p+=16;
4241                 simplicity=(size_t) mng_get_long(p);
4242               }
4243             mng_type=1;    /* Full MNG */
4244             if ((simplicity != 0) && ((simplicity | 11) == 11))
4245               mng_type=2; /* LC */
4246             if ((simplicity != 0) && ((simplicity | 9) == 9))
4247               mng_type=3; /* VLC */
4248 #if defined(MNG_INSERT_LAYERS)
4249             if (mng_type != 3)
4250               insert_layers=MagickTrue;
4251 #endif
4252             if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4253               {
4254                 /*
4255                   Allocate next image structure.
4256                 */
4257                 AcquireNextImage(image_info,image);
4258                 if (GetNextImageInList(image) == (Image *) NULL)
4259                   return((Image *) NULL);
4260                 image=SyncNextImageInList(image);
4261                 mng_info->image=image;
4262               }
4263
4264             if ((mng_info->mng_width > 65535L) ||
4265                 (mng_info->mng_height > 65535L))
4266               ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
4267             (void) FormatMagickString(page_geometry,MaxTextExtent,
4268               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
4269               mng_info->mng_height);
4270             mng_info->frame.left=0;
4271             mng_info->frame.right=(ssize_t) mng_info->mng_width;
4272             mng_info->frame.top=0;
4273             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
4274             mng_info->clip=default_fb=previous_fb=mng_info->frame;
4275             for (i=0; i < MNG_MAX_OBJECTS; i++)
4276               mng_info->object_clip[i]=mng_info->frame;
4277             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4278             continue;
4279           }
4280
4281         if (memcmp(type,mng_TERM,4) == 0)
4282           {
4283             int
4284               repeat=0;
4285
4286
4287             if (length)
4288               repeat=p[0];
4289             if (repeat == 3)
4290               {
4291                 final_delay=(png_uint_32) mng_get_long(&p[2]);
4292                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
4293                 if (mng_iterations == PNG_UINT_31_MAX)
4294                   mng_iterations=0;
4295                 image->iterations=mng_iterations;
4296                 term_chunk_found=MagickTrue;
4297               }
4298             if (logging != MagickFalse)
4299               {
4300                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4301                   "    repeat=%d",repeat);
4302                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4303                   "    final_delay=%.20g",(double) final_delay);
4304                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4305                   "    image->iterations=%.20g",(double) image->iterations);
4306               }
4307             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4308             continue;
4309           }
4310         if (memcmp(type,mng_DEFI,4) == 0)
4311           {
4312             if (mng_type == 3)
4313               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4314                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4315                 image->filename);
4316             object_id=(p[0] << 8) | p[1];
4317             if (mng_type == 2 && object_id != 0)
4318               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4319                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4320                 image->filename);
4321             if (object_id > MNG_MAX_OBJECTS)
4322               {
4323                 /*
4324                   Instead ofsuing a warning we should allocate a larger
4325                   MngInfo structure and continue.
4326                 */
4327                 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4328                   CoderError,"object id too large","`%s'",image->filename);
4329                 object_id=MNG_MAX_OBJECTS;
4330               }
4331             if (mng_info->exists[object_id])
4332               if (mng_info->frozen[object_id])
4333                 {
4334                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4335                   (void) ThrowMagickException(&image->exception,
4336                     GetMagickModule(),CoderError,
4337                     "DEFI cannot redefine a frozen MNG object","`%s'",
4338                     image->filename);
4339                   continue;
4340                 }
4341             mng_info->exists[object_id]=MagickTrue;
4342             if (length > 2)
4343               mng_info->invisible[object_id]=p[2];
4344             /*
4345               Extract object offset info.
4346             */
4347             if (length > 11)
4348               {
4349                 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) | (p[5] << 16) |
4350                 (p[6] << 8) | p[7]);
4351                 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) | (p[9] << 16) |
4352                 (p[10] << 8) | p[11]);
4353                 if (logging != MagickFalse)
4354                   {
4355                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4356                       "  x_off[%d]: %.20g",object_id,(double)
4357                       mng_info->x_off[object_id]);
4358                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4359                       "  y_off[%d]: %.20g",object_id,(double)
4360                       mng_info->y_off[object_id]);
4361                   }
4362               }
4363             /*
4364               Extract object clipping info.
4365             */
4366             if (length > 27)
4367               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4368                 &p[12]);
4369             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4370             continue;
4371           }
4372         if (memcmp(type,mng_bKGD,4) == 0)
4373           {
4374             mng_info->have_global_bkgd=MagickFalse;
4375             if (length > 5)
4376               {
4377                 mng_info->mng_global_bkgd.red=
4378                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4379                 mng_info->mng_global_bkgd.green=
4380                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4381                 mng_info->mng_global_bkgd.blue=
4382                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4383                 mng_info->have_global_bkgd=MagickTrue;
4384               }
4385             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4386             continue;
4387           }
4388         if (memcmp(type,mng_BACK,4) == 0)
4389           {
4390 #if defined(MNG_INSERT_LAYERS)
4391             if (length > 6)
4392               mandatory_back=p[6];
4393             else
4394               mandatory_back=0;
4395             if (mandatory_back && length > 5)
4396               {
4397                 mng_background_color.red=
4398                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
4399                 mng_background_color.green=
4400                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
4401                 mng_background_color.blue=
4402                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
4403                 mng_background_color.opacity=OpaqueOpacity;
4404               }
4405 #ifdef MNG_OBJECT_BUFFERS
4406             if (length > 8)
4407               mng_background_object=(p[7] << 8) | p[8];
4408 #endif
4409 #endif
4410             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4411             continue;
4412           }
4413         if (memcmp(type,mng_PLTE,4) == 0)
4414           {
4415             /*
4416               Read global PLTE.
4417             */
4418             if (length && (length < 769))
4419               {
4420                 if (mng_info->global_plte == (png_colorp) NULL)
4421                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
4422                     sizeof(*mng_info->global_plte));
4423                 for (i=0; i < (ssize_t) (length/3); i++)
4424                 {
4425                   mng_info->global_plte[i].red=p[3*i];
4426                   mng_info->global_plte[i].green=p[3*i+1];
4427                   mng_info->global_plte[i].blue=p[3*i+2];
4428                 }
4429                 mng_info->global_plte_length=(unsigned int) (length/3);
4430               }
4431 #ifdef MNG_LOOSE
4432             for ( ; i < 256; i++)
4433             {
4434               mng_info->global_plte[i].red=i;
4435               mng_info->global_plte[i].green=i;
4436               mng_info->global_plte[i].blue=i;
4437             }
4438             if (length)
4439               mng_info->global_plte_length=256;
4440 #endif
4441             else
4442               mng_info->global_plte_length=0;
4443             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4444             continue;
4445           }
4446         if (memcmp(type,mng_tRNS,4) == 0)
4447           {
4448             /* read global tRNS */
4449
4450             if (length < 257)
4451               for (i=0; i < (ssize_t) length; i++)
4452                 mng_info->global_trns[i]=p[i];
4453
4454 #ifdef MNG_LOOSE
4455             for ( ; i < 256; i++)
4456               mng_info->global_trns[i]=255;
4457 #endif
4458             mng_info->global_trns_length=(unsigned int) length;
4459             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4460             continue;
4461           }
4462         if (memcmp(type,mng_gAMA,4) == 0)
4463           {
4464             if (length == 4)
4465               {
4466                 ssize_t
4467                   igamma;
4468
4469                 igamma=mng_get_long(p);
4470                 mng_info->global_gamma=((float) igamma)*0.00001;
4471                 mng_info->have_global_gama=MagickTrue;
4472               }
4473             else
4474               mng_info->have_global_gama=MagickFalse;
4475             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4476             continue;
4477           }
4478
4479         if (memcmp(type,mng_cHRM,4) == 0)
4480           {
4481             /*
4482               Read global cHRM
4483             */
4484             if (length == 32)
4485               {
4486                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
4487                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
4488                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
4489                 mng_info->global_chrm.red_primary.y=0.00001*
4490                   mng_get_long(&p[12]);
4491                 mng_info->global_chrm.green_primary.x=0.00001*
4492                   mng_get_long(&p[16]);
4493                 mng_info->global_chrm.green_primary.y=0.00001*
4494                   mng_get_long(&p[20]);
4495                 mng_info->global_chrm.blue_primary.x=0.00001*
4496                   mng_get_long(&p[24]);
4497                 mng_info->global_chrm.blue_primary.y=0.00001*
4498                   mng_get_long(&p[28]);
4499                 mng_info->have_global_chrm=MagickTrue;
4500               }
4501             else
4502               mng_info->have_global_chrm=MagickFalse;
4503             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4504             continue;
4505           }
4506         if (memcmp(type,mng_sRGB,4) == 0)
4507           {
4508             /*
4509               Read global sRGB.
4510             */
4511             if (length)
4512               {
4513                 mng_info->global_srgb_intent=
4514                   PNG_RenderingIntent_to_Magick_RenderingIntent(p[0]);
4515                 mng_info->have_global_srgb=MagickTrue;
4516               }
4517             else
4518               mng_info->have_global_srgb=MagickFalse;
4519             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4520             continue;
4521           }
4522         if (memcmp(type,mng_iCCP,4) == 0)
4523           {
4524             /* To do. */
4525
4526             /*
4527               Read global iCCP.
4528             */
4529             if (length)
4530               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4531             continue;
4532           }
4533         if (memcmp(type,mng_FRAM,4) == 0)
4534           {
4535             if (mng_type == 3)
4536               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4537                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
4538                 image->filename);
4539             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
4540               image->delay=frame_delay;
4541             frame_delay=default_frame_delay;
4542             frame_timeout=default_frame_timeout;
4543             fb=default_fb;
4544             if (length)
4545               if (p[0])
4546                 mng_info->framing_mode=p[0];
4547             if (logging != MagickFalse)
4548               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4549                 "    Framing_mode=%d",mng_info->framing_mode);
4550             if (length > 6)
4551               {
4552                 /*
4553                   Note the delay and frame clipping boundaries.
4554                 */
4555                 p++; /* framing mode */
4556                 while (*p && ((p-chunk) < (ssize_t) length))
4557                   p++;  /* frame name */
4558                 p++;  /* frame name terminator */
4559                 if ((p-chunk) < (ssize_t) (length-4))
4560                   {
4561                     int
4562                       change_delay,
4563                       change_timeout,
4564                       change_clipping;
4565
4566                     change_delay=(*p++);
4567                     change_timeout=(*p++);
4568                     change_clipping=(*p++);
4569                     p++; /* change_sync */
4570                     if (change_delay)
4571                       {
4572                         frame_delay=1UL*image->ticks_per_second*
4573                           mng_get_long(p);
4574                         if (mng_info->ticks_per_second != 0)
4575                           frame_delay/=mng_info->ticks_per_second;
4576                         else
4577                           frame_delay=PNG_UINT_31_MAX;
4578                         if (change_delay == 2)
4579                           default_frame_delay=frame_delay;
4580                         p+=4;
4581                         if (logging != MagickFalse)
4582                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4583                             "    Framing_delay=%.20g",(double) frame_delay);
4584                       }
4585                     if (change_timeout)
4586                       {
4587                         frame_timeout=1UL*image->ticks_per_second*
4588                           mng_get_long(p);
4589                         if (mng_info->ticks_per_second != 0)
4590                           frame_timeout/=mng_info->ticks_per_second;
4591                         else
4592                           frame_timeout=PNG_UINT_31_MAX;
4593                         if (change_delay == 2)
4594                           default_frame_timeout=frame_timeout;
4595                         p+=4;
4596                         if (logging != MagickFalse)
4597                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4598                             "    Framing_timeout=%.20g",(double) frame_timeout);
4599                       }
4600                     if (change_clipping)
4601                       {
4602                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
4603                         p+=17;
4604                         previous_fb=fb;
4605                         if (logging != MagickFalse)
4606                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4607                             "    Frame_clipping: L=%.20g R=%.20g T=%.20g B=%.20g",
4608                             (double) fb.left,(double) fb.right,(double) fb.top,
4609                             (double) fb.bottom);
4610                         if (change_clipping == 2)
4611                           default_fb=fb;
4612                       }
4613                   }
4614               }
4615             mng_info->clip=fb;
4616             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
4617             subframe_width=(size_t) (mng_info->clip.right
4618                -mng_info->clip.left);
4619             subframe_height=(size_t) (mng_info->clip.bottom
4620                -mng_info->clip.top);
4621             /*
4622               Insert a background layer behind the frame if framing_mode is 4.
4623             */
4624 #if defined(MNG_INSERT_LAYERS)
4625             if (logging != MagickFalse)
4626               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4627                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
4628                 subframe_width,(double) subframe_height);
4629             if (insert_layers && (mng_info->framing_mode == 4) &&
4630                 (subframe_width) && (subframe_height))
4631               {
4632                 /*
4633                   Allocate next image structure.
4634                 */
4635                 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4636                   {
4637                     AcquireNextImage(image_info,image);
4638                     if (GetNextImageInList(image) == (Image *) NULL)
4639                       {
4640                         image=DestroyImageList(image);
4641                         MngInfoFreeStruct(mng_info,&have_mng_structure);
4642                         return((Image *) NULL);
4643                       }
4644                     image=SyncNextImageInList(image);
4645                   }
4646                 mng_info->image=image;
4647                 if (term_chunk_found)
4648                   {
4649                     image->start_loop=MagickTrue;
4650                     image->iterations=mng_iterations;
4651                     term_chunk_found=MagickFalse;
4652                   }
4653                 else
4654                     image->start_loop=MagickFalse;
4655                 image->columns=subframe_width;
4656                 image->rows=subframe_height;
4657                 image->page.width=subframe_width;
4658                 image->page.height=subframe_height;
4659                 image->page.x=mng_info->clip.left;
4660                 image->page.y=mng_info->clip.top;
4661                 image->background_color=mng_background_color;
4662                 image->matte=MagickFalse;
4663                 image->delay=0;
4664                 (void) SetImageBackgroundColor(image);
4665                 if (logging != MagickFalse)
4666                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4667                     "  Inserted background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
4668                     (double) mng_info->clip.left,(double) mng_info->clip.right,
4669                     (double) mng_info->clip.top,(double) mng_info->clip.bottom);
4670               }
4671 #endif
4672             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4673             continue;
4674           }
4675         if (memcmp(type,mng_CLIP,4) == 0)
4676           {
4677             unsigned int
4678               first_object,
4679               last_object;
4680
4681             /*
4682               Read CLIP.
4683             */
4684             first_object=(p[0] << 8) | p[1];
4685             last_object=(p[2] << 8) | p[3];
4686             for (i=(int) first_object; i <= (int) last_object; i++)
4687             {
4688               if (mng_info->exists[i] && !mng_info->frozen[i])
4689                 {
4690                   MngBox
4691                     box;
4692
4693                   box=mng_info->object_clip[i];
4694                   mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
4695                 }
4696             }
4697             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4698             continue;
4699           }
4700         if (memcmp(type,mng_SAVE,4) == 0)
4701           {
4702             for (i=1; i < MNG_MAX_OBJECTS; i++)
4703               if (mng_info->exists[i])
4704                 {
4705                  mng_info->frozen[i]=MagickTrue;
4706 #ifdef MNG_OBJECT_BUFFERS
4707                  if (mng_info->ob[i] != (MngBuffer *) NULL)
4708                     mng_info->ob[i]->frozen=MagickTrue;
4709 #endif
4710                 }
4711             if (length)
4712               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4713             continue;
4714           }
4715
4716         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
4717           {
4718             /*
4719               Read DISC or SEEK.
4720             */
4721             if ((length == 0) || !memcmp(type,mng_SEEK,4))
4722               {
4723                 for (i=1; i < MNG_MAX_OBJECTS; i++)
4724                   MngInfoDiscardObject(mng_info,i);
4725               }
4726             else
4727               {
4728                 register ssize_t
4729                   j;
4730
4731                 for (j=0; j < (ssize_t) length; j+=2)
4732                 {
4733                   i=p[j] << 8 | p[j+1];
4734                   MngInfoDiscardObject(mng_info,i);
4735                 }
4736               }
4737             if (length)
4738               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4739             continue;
4740           }
4741         if (memcmp(type,mng_MOVE,4) == 0)
4742           {
4743             size_t
4744               first_object,
4745               last_object;
4746
4747             /*
4748               read MOVE
4749             */
4750             first_object=(p[0] << 8) | p[1];
4751             last_object=(p[2] << 8) | p[3];
4752             for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
4753             {
4754               if (mng_info->exists[i] && !mng_info->frozen[i])
4755                 {
4756                   MngPair
4757                     new_pair;
4758
4759                   MngPair
4760                     old_pair;
4761
4762                   old_pair.a=mng_info->x_off[i];
4763                   old_pair.b=mng_info->y_off[i];
4764                   new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
4765                   mng_info->x_off[i]=new_pair.a;
4766                   mng_info->y_off[i]=new_pair.b;
4767                 }
4768             }
4769             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4770             continue;
4771           }
4772
4773         if (memcmp(type,mng_LOOP,4) == 0)
4774           {
4775             ssize_t loop_iters=1;
4776             loop_level=chunk[0];
4777             mng_info->loop_active[loop_level]=1;  /* mark loop active */
4778             /*
4779               Record starting point.
4780             */
4781             loop_iters=mng_get_long(&chunk[1]);
4782             if (logging != MagickFalse)
4783               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4784                 "  LOOP level %.20g has %.20g iterations ",(double) loop_level,
4785                 (double) loop_iters);
4786             if (loop_iters == 0)
4787               skipping_loop=loop_level;
4788             else
4789               {
4790                 mng_info->loop_jump[loop_level]=TellBlob(image);
4791                 mng_info->loop_count[loop_level]=loop_iters;
4792               }
4793             mng_info->loop_iteration[loop_level]=0;
4794             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4795             continue;
4796           }
4797         if (memcmp(type,mng_ENDL,4) == 0)
4798           {
4799             loop_level=chunk[0];
4800             if (skipping_loop > 0)
4801               {
4802                 if (skipping_loop == loop_level)
4803                   {
4804                     /*
4805                       Found end of zero-iteration loop.
4806                     */
4807                     skipping_loop=(-1);
4808                     mng_info->loop_active[loop_level]=0;
4809                   }
4810               }
4811             else
4812               {
4813                 if (mng_info->loop_active[loop_level] == 1)
4814                   {
4815                     mng_info->loop_count[loop_level]--;
4816                     mng_info->loop_iteration[loop_level]++;
4817                     if (logging != MagickFalse)
4818                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4819                         "  ENDL: LOOP level %.20g has %.20g remaining iterations ",
4820                         (double) loop_level,(double)
4821                         mng_info->loop_count[loop_level]);
4822                     if (mng_info->loop_count[loop_level] != 0)
4823                       {
4824                         offset=SeekBlob(image,mng_info->loop_jump[loop_level],
4825                           SEEK_SET);
4826                         if (offset < 0)
4827                           ThrowReaderException(CorruptImageError,
4828                             "ImproperImageHeader");
4829                       }
4830                     else
4831                       {
4832                         short
4833                           last_level;
4834
4835                         /*
4836                           Finished loop.
4837                         */
4838                         mng_info->loop_active[loop_level]=0;
4839                         last_level=(-1);
4840                         for (i=0; i < loop_level; i++)
4841                           if (mng_info->loop_active[i] == 1)
4842                             last_level=(short) i;
4843                         loop_level=last_level;
4844                       }
4845                   }
4846               }
4847             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4848             continue;
4849           }
4850         if (memcmp(type,mng_CLON,4) == 0)
4851           {
4852             if (mng_info->clon_warning == 0)
4853               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4854                 CoderError,"CLON is not implemented yet","`%s'",
4855                 image->filename);
4856             mng_info->clon_warning++;
4857           }
4858         if (memcmp(type,mng_MAGN,4) == 0)
4859           {
4860             png_uint_16
4861               magn_first,
4862               magn_last,
4863               magn_mb,
4864               magn_ml,
4865               magn_mr,
4866               magn_mt,
4867               magn_mx,
4868               magn_my,
4869               magn_methx,
4870               magn_methy;
4871
4872             if (length > 1)
4873               magn_first=(p[0] << 8) | p[1];
4874             else
4875               magn_first=0;
4876             if (length > 3)
4877               magn_last=(p[2] << 8) | p[3];
4878             else
4879               magn_last=magn_first;
4880 #ifndef MNG_OBJECT_BUFFERS
4881             if (magn_first || magn_last)
4882               if (mng_info->magn_warning == 0)
4883                 {
4884                   (void) ThrowMagickException(&image->exception,
4885                      GetMagickModule(),CoderError,
4886                      "MAGN is not implemented yet for nonzero objects",
4887                      "`%s'",image->filename);
4888                    mng_info->magn_warning++;
4889                 }
4890 #endif
4891             if (length > 4)
4892               magn_methx=p[4];
4893             else
4894               magn_methx=0;
4895
4896             if (length > 6)
4897               magn_mx=(p[5] << 8) | p[6];
4898             else
4899               magn_mx=1;
4900             if (magn_mx == 0)
4901               magn_mx=1;
4902
4903             if (length > 8)
4904               magn_my=(p[7] << 8) | p[8];
4905             else
4906               magn_my=magn_mx;
4907             if (magn_my == 0)
4908               magn_my=1;
4909
4910             if (length > 10)
4911               magn_ml=(p[9] << 8) | p[10];
4912             else
4913               magn_ml=magn_mx;
4914             if (magn_ml == 0)
4915               magn_ml=1;
4916
4917             if (length > 12)
4918               magn_mr=(p[11] << 8) | p[12];
4919             else
4920               magn_mr=magn_mx;
4921             if (magn_mr == 0)
4922               magn_mr=1;
4923
4924             if (length > 14)
4925               magn_mt=(p[13] << 8) | p[14];
4926             else
4927               magn_mt=magn_my;
4928             if (magn_mt == 0)
4929               magn_mt=1;
4930
4931             if (length > 16)
4932               magn_mb=(p[15] << 8) | p[16];
4933             else
4934               magn_mb=magn_my;
4935             if (magn_mb == 0)
4936               magn_mb=1;
4937
4938             if (length > 17)
4939               magn_methy=p[17];
4940             else
4941               magn_methy=magn_methx;
4942
4943             if (magn_methx > 5 || magn_methy > 5)
4944               if (mng_info->magn_warning == 0)
4945                 {
4946                   (void) ThrowMagickException(&image->exception,
4947                      GetMagickModule(),CoderError,
4948                      "Unknown MAGN method in MNG datastream","`%s'",
4949                      image->filename);
4950                    mng_info->magn_warning++;
4951                 }
4952 #ifdef MNG_OBJECT_BUFFERS
4953           /* Magnify existing objects in the range magn_first to magn_last */
4954 #endif
4955             if (magn_first == 0 || magn_last == 0)
4956               {
4957                 /* Save the magnification factors for object 0 */
4958                 mng_info->magn_mb=magn_mb;
4959                 mng_info->magn_ml=magn_ml;
4960                 mng_info->magn_mr=magn_mr;
4961                 mng_info->magn_mt=magn_mt;
4962                 mng_info->magn_mx=magn_mx;
4963                 mng_info->magn_my=magn_my;
4964                 mng_info->magn_methx=magn_methx;
4965                 mng_info->magn_methy=magn_methy;
4966               }
4967           }
4968         if (memcmp(type,mng_PAST,4) == 0)
4969           {
4970             if (mng_info->past_warning == 0)
4971               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4972                 CoderError,"PAST is not implemented yet","`%s'",
4973                 image->filename);
4974             mng_info->past_warning++;
4975           }
4976         if (memcmp(type,mng_SHOW,4) == 0)
4977           {
4978             if (mng_info->show_warning == 0)
4979               (void) ThrowMagickException(&image->exception,GetMagickModule(),
4980                 CoderError,"SHOW is not implemented yet","`%s'",
4981                 image->filename);
4982             mng_info->show_warning++;
4983           }
4984         if (memcmp(type,mng_sBIT,4) == 0)
4985           {
4986             if (length < 4)
4987               mng_info->have_global_sbit=MagickFalse;
4988             else
4989               {
4990                 mng_info->global_sbit.gray=p[0];
4991                 mng_info->global_sbit.red=p[0];
4992                 mng_info->global_sbit.green=p[1];
4993                 mng_info->global_sbit.blue=p[2];
4994                 mng_info->global_sbit.alpha=p[3];
4995                 mng_info->have_global_sbit=MagickTrue;
4996              }
4997           }
4998         if (memcmp(type,mng_pHYs,4) == 0)
4999           {
5000             if (length > 8)
5001               {
5002                 mng_info->global_x_pixels_per_unit=
5003                     (size_t) mng_get_long(p);
5004                 mng_info->global_y_pixels_per_unit=
5005                     (size_t) mng_get_long(&p[4]);
5006                 mng_info->global_phys_unit_type=p[8];
5007                 mng_info->have_global_phys=MagickTrue;
5008               }
5009             else
5010               mng_info->have_global_phys=MagickFalse;
5011           }
5012         if (memcmp(type,mng_pHYg,4) == 0)
5013           {
5014             if (mng_info->phyg_warning == 0)
5015               (void) ThrowMagickException(&image->exception,GetMagickModule(),
5016                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
5017             mng_info->phyg_warning++;
5018           }
5019         if (memcmp(type,mng_BASI,4) == 0)
5020           {
5021             skip_to_iend=MagickTrue;
5022             if (mng_info->basi_warning == 0)
5023               (void) ThrowMagickException(&image->exception,GetMagickModule(),
5024                 CoderError,"BASI is not implemented yet","`%s'",
5025                 image->filename);
5026             mng_info->basi_warning++;
5027 #ifdef MNG_BASI_SUPPORTED
5028             basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5029                (p[2] << 8) | p[3]);
5030             basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5031                (p[6] << 8) | p[7]);
5032             basi_color_type=p[8];
5033             basi_compression_method=p[9];
5034             basi_filter_type=p[10];
5035             basi_interlace_method=p[11];
5036             if (length > 11)
5037               basi_red=(p[12] << 8) & p[13];
5038             else
5039               basi_red=0;
5040             if (length > 13)
5041               basi_green=(p[14] << 8) & p[15];
5042             else
5043               basi_green=0;
5044             if (length > 15)
5045               basi_blue=(p[16] << 8) & p[17];
5046             else
5047               basi_blue=0;
5048             if (length > 17)
5049               basi_alpha=(p[18] << 8) & p[19];
5050             else
5051               {
5052                 if (basi_sample_depth == 16)
5053                   basi_alpha=65535L;
5054                 else
5055                   basi_alpha=255;
5056               }
5057             if (length > 19)
5058               basi_viewable=p[20];
5059             else
5060               basi_viewable=0;
5061 #endif
5062             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5063             continue;
5064           }
5065         if (memcmp(type,mng_IHDR,4)
5066 #if defined(JNG_SUPPORTED)
5067             && memcmp(type,mng_JHDR,4)
5068 #endif
5069             )
5070           {
5071             /* Not an IHDR or JHDR chunk */
5072             if (length)
5073               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5074             continue;
5075           }
5076 /* Process IHDR */
5077         if (logging != MagickFalse)
5078           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5079             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
5080         mng_info->exists[object_id]=MagickTrue;
5081         mng_info->viewable[object_id]=MagickTrue;
5082         if (mng_info->invisible[object_id])
5083           {
5084             if (logging != MagickFalse)
5085               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5086                 "  Skipping invisible object");
5087             skip_to_iend=MagickTrue;
5088             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5089             continue;
5090           }
5091 #if defined(MNG_INSERT_LAYERS)
5092         if (length < 8)
5093           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5094         image_width=(size_t) mng_get_long(p);
5095         image_height=(size_t) mng_get_long(&p[4]);
5096 #endif
5097         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5098
5099         /*
5100           Insert a transparent background layer behind the entire animation
5101           if it is not full screen.
5102         */
5103 #if defined(MNG_INSERT_LAYERS)
5104         if (insert_layers && mng_type && first_mng_object)
5105           {
5106             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5107                 (image_width < mng_info->mng_width) ||
5108                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
5109                 (image_height < mng_info->mng_height) ||
5110                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
5111               {
5112                 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5113                   {
5114                     /*
5115                       Allocate next image structure.
5116                     */
5117                     AcquireNextImage(image_info,image);
5118                     if (GetNextImageInList(image) == (Image *) NULL)
5119                       {
5120                         image=DestroyImageList(image);
5121                         MngInfoFreeStruct(mng_info,&have_mng_structure);
5122                         return((Image *) NULL);
5123                       }
5124                     image=SyncNextImageInList(image);
5125                   }
5126                 mng_info->image=image;
5127                 if (term_chunk_found)
5128                   {
5129                     image->start_loop=MagickTrue;
5130                     image->iterations=mng_iterations;
5131                     term_chunk_found=MagickFalse;
5132                   }
5133                 else
5134                     image->start_loop=MagickFalse;
5135                 /*
5136                   Make a background rectangle.
5137                 */
5138                 image->delay=0;
5139                 image->columns=mng_info->mng_width;
5140                 image->rows=mng_info->mng_height;
5141                 image->page.width=mng_info->mng_width;
5142                 image->page.height=mng_info->mng_height;
5143                 image->page.x=0;
5144                 image->page.y=0;
5145                 image->background_color=mng_background_color;
5146                 (void) SetImageBackgroundColor(image);
5147                 if (logging != MagickFalse)
5148                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5149                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
5150                     (double) mng_info->mng_width,(double) mng_info->mng_height);
5151               }
5152           }
5153         /*
5154           Insert a background layer behind the upcoming image if
5155           framing_mode is 3, and we haven't already inserted one.
5156         */
5157         if (insert_layers && (mng_info->framing_mode == 3) &&
5158                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5159                 (simplicity & 0x08)))
5160           {
5161             if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5162             {
5163               /*
5164                 Allocate next image structure.
5165               */
5166               AcquireNextImage(image_info,image);
5167               if (GetNextImageInList(image) == (Image *) NULL)
5168                 {
5169                   image=DestroyImageList(image);
5170                   MngInfoFreeStruct(mng_info,&have_mng_structure);
5171                   return((Image *) NULL);
5172                 }
5173               image=SyncNextImageInList(image);
5174             }
5175             mng_info->image=image;
5176             if (term_chunk_found)
5177               {
5178                 image->start_loop=MagickTrue;
5179                 image->iterations=mng_iterations;
5180                 term_chunk_found=MagickFalse;
5181               }
5182             else
5183                 image->start_loop=MagickFalse;
5184             image->delay=0;
5185             image->columns=subframe_width;
5186             image->rows=subframe_height;
5187             image->page.width=subframe_width;
5188             image->page.height=subframe_height;
5189             image->page.x=mng_info->clip.left;
5190             image->page.y=mng_info->clip.top;
5191             image->background_color=mng_background_color;
5192             image->matte=MagickFalse;
5193             (void) SetImageBackgroundColor(image);
5194             if (logging != MagickFalse)
5195               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5196                 "  Inserted background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5197                 (double) mng_info->clip.left,(double) mng_info->clip.right,
5198                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5199           }
5200 #endif /* MNG_INSERT_LAYERS */
5201         first_mng_object=MagickFalse;
5202         if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5203           {
5204             /*
5205               Allocate next image structure.
5206             */
5207             AcquireNextImage(image_info,image);
5208             if (GetNextImageInList(image) == (Image *) NULL)
5209               {
5210                 image=DestroyImageList(image);
5211                 MngInfoFreeStruct(mng_info,&have_mng_structure);
5212                 return((Image *) NULL);
5213               }
5214             image=SyncNextImageInList(image);
5215           }
5216         mng_info->image=image;
5217         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5218           GetBlobSize(image));
5219         if (status == MagickFalse)
5220           break;
5221         if (term_chunk_found)
5222           {
5223             image->start_loop=MagickTrue;
5224             term_chunk_found=MagickFalse;
5225           }
5226         else
5227             image->start_loop=MagickFalse;
5228         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5229           {
5230             image->delay=frame_delay;
5231             frame_delay=default_frame_delay;
5232           }
5233         else
5234           image->delay=0;
5235         image->page.width=mng_info->mng_width;
5236         image->page.height=mng_info->mng_height;
5237         image->page.x=mng_info->x_off[object_id];
5238         image->page.y=mng_info->y_off[object_id];
5239         image->iterations=mng_iterations;
5240         /*
5241           Seek back to the beginning of the IHDR or JHDR chunk's length field.
5242         */
5243         if (logging != MagickFalse)
5244           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5245             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
5246             type[2],type[3]);
5247         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
5248         if (offset < 0)
5249           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5250       }
5251
5252     previous=image;
5253     mng_info->image=image;
5254     mng_info->mng_type=mng_type;
5255     mng_info->object_id=object_id;
5256
5257     if (memcmp(type,mng_IHDR,4) == 0)
5258       image=ReadOnePNGImage(mng_info,image_info,exception);
5259 #if defined(JNG_SUPPORTED)
5260     else
5261       image=ReadOneJNGImage(mng_info,image_info,exception);
5262 #endif
5263
5264     if (image == (Image *) NULL)
5265       {
5266         if (IsImageObject(previous) != MagickFalse)
5267           {
5268             (void) DestroyImageList(previous);
5269             (void) CloseBlob(previous);
5270           }
5271         MngInfoFreeStruct(mng_info,&have_mng_structure);
5272         return((Image *) NULL);
5273       }
5274     if (image->columns == 0 || image->rows == 0)
5275       {
5276         (void) CloseBlob(image);
5277         image=DestroyImageList(image);
5278         MngInfoFreeStruct(mng_info,&have_mng_structure);
5279         return((Image *) NULL);
5280       }
5281     mng_info->image=image;
5282
5283     if (mng_type)
5284       {
5285         MngBox
5286           crop_box;
5287
5288         if (mng_info->magn_methx || mng_info->magn_methy)
5289           {
5290             png_uint_32
5291                magnified_height,
5292                magnified_width;
5293
5294             if (logging != MagickFalse)
5295               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5296                 "  Processing MNG MAGN chunk");
5297
5298             if (mng_info->magn_methx == 1)
5299               {
5300                 magnified_width=mng_info->magn_ml;
5301                 if (image->columns > 1)
5302                    magnified_width += mng_info->magn_mr;
5303                 if (image->columns > 2)
5304                    magnified_width += (png_uint_32) ((image->columns-2)*(mng_info->magn_mx));
5305               }
5306             else
5307               {
5308                 magnified_width=(png_uint_32) image->columns;
5309                 if (image->columns > 1)
5310                    magnified_width += mng_info->magn_ml-1;
5311                 if (image->columns > 2)
5312                    magnified_width += mng_info->magn_mr-1;
5313                 if (image->columns > 3)
5314                    magnified_width += (png_uint_32) ((image->columns-3)*(mng_info->magn_mx-1));
5315               }
5316             if (mng_info->magn_methy == 1)
5317               {
5318                 magnified_height=mng_info->magn_mt;
5319                 if (image->rows > 1)
5320                    magnified_height += mng_info->magn_mb;
5321                 if (image->rows > 2)
5322                    magnified_height += (image->rows-2)*(mng_info->magn_my);
5323               }
5324             else
5325               {
5326                 magnified_height=(png_uint_32) image->rows;
5327                 if (image->rows > 1)
5328                    magnified_height += mng_info->magn_mt-1;
5329                 if (image->rows > 2)
5330                    magnified_height += mng_info->magn_mb-1;
5331                 if (image->rows > 3)
5332                    magnified_height += (png_uint_32) ((image->rows-3)*(mng_info->magn_my-1));
5333               }
5334             if (magnified_height > image->rows ||
5335                 magnified_width > image->columns)
5336               {
5337                 Image
5338                   *large_image;
5339
5340                 int
5341                   yy;
5342
5343                 ssize_t
5344                   m,
5345                   y;
5346
5347                 register ssize_t
5348                   x;
5349
5350                 register PixelPacket
5351                   *n,
5352                   *q;
5353
5354                 PixelPacket
5355                   *next,
5356                   *prev;
5357
5358                 png_uint_16
5359                   magn_methx,
5360                   magn_methy;
5361
5362                 /*
5363                   Allocate next image structure.
5364                 */
5365                 if (logging != MagickFalse)
5366                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5367                     "    Allocate magnified image");
5368                 AcquireNextImage(image_info,image);
5369                 if (GetNextImageInList(image) == (Image *) NULL)
5370                   {
5371                     image=DestroyImageList(image);
5372                     MngInfoFreeStruct(mng_info,&have_mng_structure);
5373                     return((Image *) NULL);
5374                   }
5375
5376                 large_image=SyncNextImageInList(image);
5377
5378                 large_image->columns=magnified_width;
5379                 large_image->rows=magnified_height;
5380
5381                 magn_methx=mng_info->magn_methx;
5382                 magn_methy=mng_info->magn_methy;
5383
5384 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5385 #define QM unsigned short
5386                 if (magn_methx != 1 || magn_methy != 1)
5387                   {
5388                   /*
5389                      Scale pixels to unsigned shorts to prevent
5390                      overflow of intermediate values of interpolations
5391                   */
5392                      for (y=0; y < (ssize_t) image->rows; y++)
5393                      {
5394                        q=GetAuthenticPixels(image,0,y,image->columns,1,
5395                           exception);
5396                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
5397                        {
5398                           q->red=ScaleQuantumToShort(q->red);
5399                           q->green=ScaleQuantumToShort(q->green);
5400                           q->blue=ScaleQuantumToShort(q->blue);
5401                           q->opacity=ScaleQuantumToShort(q->opacity);
5402                           q++;
5403                        }
5404                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
5405                          break;
5406                      }
5407                   }
5408 #else
5409 #define QM Quantum
5410 #endif
5411
5412                 if (image->matte != MagickFalse)
5413                    (void) SetImageBackgroundColor(large_image);
5414                 else
5415                   {
5416                     large_image->background_color.opacity=OpaqueOpacity;
5417                     (void) SetImageBackgroundColor(large_image);
5418                     if (magn_methx == 4)
5419                       magn_methx=2;
5420                     if (magn_methx == 5)
5421                       magn_methx=3;
5422                     if (magn_methy == 4)
5423                       magn_methy=2;
5424                     if (magn_methy == 5)
5425                       magn_methy=3;
5426                   }
5427
5428                 /* magnify the rows into the right side of the large image */
5429
5430                 if (logging != MagickFalse)
5431                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5432                     "    Magnify the rows to %.20g",(double) large_image->rows);
5433                 m=(ssize_t) mng_info->magn_mt;
5434                 yy=0;
5435                 length=(size_t) image->columns;
5436                 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
5437                 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
5438                 if ((prev == (PixelPacket *) NULL) ||
5439                     (next == (PixelPacket *) NULL))
5440                   {
5441                      image=DestroyImageList(image);
5442                      MngInfoFreeStruct(mng_info,&have_mng_structure);
5443                      ThrowReaderException(ResourceLimitError,
5444                        "MemoryAllocationFailed");
5445                   }
5446                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
5447                 (void) CopyMagickMemory(next,n,length);
5448                 for (y=0; y < (ssize_t) image->rows; y++)
5449                 {
5450                   if (y == 0)
5451                     m=(ssize_t) mng_info->magn_mt;
5452                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
5453                     m=(ssize_t) mng_info->magn_mb;
5454                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
5455                     m=(ssize_t) mng_info->magn_mb;
5456                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
5457                     m=1;
5458                   else
5459                     m=(ssize_t) mng_info->magn_my;
5460                   n=prev;
5461                   prev=next;
5462                   next=n;
5463                   if (y < (ssize_t) image->rows-1)
5464                     {
5465                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
5466                           exception);
5467                       (void) CopyMagickMemory(next,n,length);
5468                     }
5469                   for (i=0; i < m; i++, yy++)
5470                   {
5471                     register PixelPacket
5472                       *pixels;
5473
5474                     assert(yy < (ssize_t) large_image->rows);
5475                     pixels=prev;
5476                     n=next;
5477                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
5478                           1,exception);
5479                     q+=(large_image->columns-image->columns);
5480                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
5481                     {
5482                       /* TO DO: get color as function of indexes[x] */
5483                       /*
5484                       if (image->storage_class == PseudoClass)
5485                         {
5486                         }
5487                       */
5488
5489                       if (magn_methy <= 1)
5490                         {
5491                           *q=(*pixels); /* replicate previous */
5492                         }
5493                       else if (magn_methy == 2 || magn_methy == 4)
5494                         {
5495                           if (i == 0)
5496                              *q=(*pixels);
5497                           else
5498                             {
5499                               /* Interpolate */
5500                               (*q).red=(QM) (((ssize_t) (2*i*((*n).red
5501                                  -(*pixels).red)+m))/((ssize_t) (m*2))
5502                                  +(*pixels).red);
5503                               (*q).green=(QM) (((ssize_t) (2*i*((*n).green
5504                                  -(*pixels).green)+m))/((ssize_t) (m*2))
5505                                  +(*pixels).green);
5506                               (*q).blue=(QM) (((ssize_t) (2*i*((*n).blue
5507                                  -(*pixels).blue)+m))/((ssize_t) (m*2))
5508                                  +(*pixels).blue);
5509                               if (image->matte != MagickFalse)
5510                                  (*q).opacity=(QM) (((ssize_t)
5511                                  (2*i*((*n).opacity
5512                                  -(*pixels).opacity)+m))
5513                                  /((ssize_t) (m*2))+(*pixels).opacity);
5514                             }
5515                           if (magn_methy == 4)
5516                             {
5517                               /* Replicate nearest */
5518                               if (i <= ((m+1) << 1))
5519                                  (*q).opacity=(*pixels).opacity+0;
5520                               else
5521                                  (*q).opacity=(*n).opacity+0;
5522                             }
5523                         }
5524                       else /* if (magn_methy == 3 || magn_methy == 5) */
5525                         {
5526                           /* Replicate nearest */
5527                           if (i <= ((m+1) << 1))
5528                              *q=(*pixels);
5529                           else
5530                              *q=(*n);
5531                           if (magn_methy == 5)
5532                             {
5533                               (*q).opacity=(QM) (((ssize_t) (2*i*((*n).opacity
5534                                  -(*pixels).opacity)+m))/((ssize_t) (m*2))
5535                                  +(*pixels).opacity);
5536                             }
5537                         }
5538                       n++;
5539                       q++;
5540                       pixels++;
5541                     } /* x */
5542                     if (SyncAuthenticPixels(large_image,exception) == 0)
5543                       break;
5544                   } /* i */
5545                 } /* y */
5546                 prev=(PixelPacket *) RelinquishMagickMemory(prev);
5547                 next=(PixelPacket *) RelinquishMagickMemory(next);
5548
5549                 length=image->columns;
5550
5551                 if (logging != MagickFalse)
5552                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5553                     "    Delete original image");
5554
5555                 DeleteImageFromList(&image);
5556
5557                 image=large_image;
5558
5559                 mng_info->image=image;
5560
5561                 /* magnify the columns */
5562                 if (logging != MagickFalse)
5563                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5564                     "    Magnify the columns to %.20g",(double) image->columns);
5565
5566                 for (y=0; y < (ssize_t) image->rows; y++)
5567                 {
5568                   register PixelPacket
5569                     *pixels;
5570
5571                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5572                   pixels=q+(image->columns-length);
5573                   n=pixels+1;
5574                   for (x=(ssize_t) (image->columns-length);
5575                     x < (ssize_t) image->columns; x++)
5576                   {
5577                     if (x == (ssize_t) (image->columns-length))
5578                       m=(ssize_t) mng_info->magn_ml;
5579                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
5580                       m=(ssize_t) mng_info->magn_mr;
5581                     else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
5582                       m=(ssize_t) mng_info->magn_mr;
5583                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
5584                       m=1;
5585                     else
5586                       m=(ssize_t) mng_info->magn_mx;
5587                     for (i=0; i < m; i++)
5588                     {
5589                       if (magn_methx <= 1)
5590                         {
5591                           /* replicate previous */
5592                           *q=(*pixels);
5593                         }
5594                       else if (magn_methx == 2 || magn_methx == 4)
5595                         {
5596                           if (i == 0)
5597                             *q=(*pixels);
5598                           else
5599                             {
5600                               /* Interpolate */
5601                               (*q).red=(QM) ((2*i*((*n).red
5602                                  -(*pixels).red)+m)
5603                                  /((ssize_t) (m*2))+(*pixels).red);
5604                               (*q).green=(QM) ((2*i*((*n).green
5605                                  -(*pixels).green)
5606                                  +m)/((ssize_t) (m*2))+(*pixels).green);
5607                               (*q).blue=(QM) ((2*i*((*n).blue
5608                                  -(*pixels).blue)+m)
5609                                  /((ssize_t) (m*2))+(*pixels).blue);
5610                               if (image->matte != MagickFalse)
5611                                  (*q).opacity=(QM) ((2*i*((*n).opacity
5612                                    -(*pixels).opacity)+m)/((ssize_t) (m*2))
5613                                    +(*pixels).opacity);
5614                             }
5615                           if (magn_methx == 4)
5616                             {
5617                               /* Replicate nearest */
5618                               if (i <= ((m+1) << 1))
5619                                  (*q).opacity=(*pixels).opacity+0;
5620                               else
5621                                  (*q).opacity=(*n).opacity+0;
5622                             }
5623                         }
5624                       else /* if (magn_methx == 3 || magn_methx == 5) */
5625                         {
5626                           /* Replicate nearest */
5627                           if (i <= ((m+1) << 1))
5628                              *q=(*pixels);
5629                           else
5630                              *q=(*n);
5631                           if (magn_methx == 5)
5632                             {
5633                               /* Interpolate */
5634                               (*q).opacity=(QM) ((2*i*((*n).opacity
5635                                  -(*pixels).opacity)+m) /((ssize_t) (m*2))
5636                                  +(*pixels).opacity);
5637                             }
5638                         }
5639                       q++;
5640                     }
5641                     n++;
5642                     p++;
5643                   }
5644                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
5645                     break;
5646                 }
5647 #if (MAGICKCORE_QUANTUM_DEPTH == 32)
5648               if (magn_methx != 1 || magn_methy != 1)
5649                 {
5650                 /*
5651                    Rescale pixels to Quantum
5652                 */
5653                    for (y=0; y < (ssize_t) image->rows; y++)
5654                    {
5655                      q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5656                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
5657                      {
5658                         q->red=ScaleShortToQuantum(q->red);
5659                         q->green=ScaleShortToQuantum(q->green);
5660                         q->blue=ScaleShortToQuantum(q->blue);
5661                         q->opacity=ScaleShortToQuantum(q->opacity);
5662                         q++;
5663                      }
5664                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
5665                        break;
5666                    }
5667                 }
5668 #endif
5669                 if (logging != MagickFalse)
5670                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5671                     "  Finished MAGN processing");
5672               }
5673           }
5674
5675         /*
5676           Crop_box is with respect to the upper left corner of the MNG.
5677         */
5678         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
5679         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
5680         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
5681         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
5682         crop_box=mng_minimum_box(crop_box,mng_info->clip);
5683         crop_box=mng_minimum_box(crop_box,mng_info->frame);
5684         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
5685         if ((crop_box.left != (mng_info->image_box.left
5686             +mng_info->x_off[object_id])) ||
5687             (crop_box.right != (mng_info->image_box.right
5688             +mng_info->x_off[object_id])) ||
5689             (crop_box.top != (mng_info->image_box.top
5690             +mng_info->y_off[object_id])) ||
5691             (crop_box.bottom != (mng_info->image_box.bottom
5692             +mng_info->y_off[object_id])))
5693           {
5694             if (logging != MagickFalse)
5695               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5696                 "  Crop the PNG image");
5697             if ((crop_box.left < crop_box.right) &&
5698                 (crop_box.top < crop_box.bottom))
5699               {
5700                 Image
5701                   *im;
5702
5703                 RectangleInfo
5704                   crop_info;
5705
5706                 /*
5707                   Crop_info is with respect to the upper left corner of
5708                   the image.
5709                 */
5710                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
5711                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
5712                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
5713                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
5714                 image->page.width=image->columns;
5715                 image->page.height=image->rows;
5716                 image->page.x=0;
5717                 image->page.y=0;
5718                 im=CropImage(image,&crop_info,exception);
5719                 if (im != (Image *) NULL)
5720                   {
5721                     image->columns=im->columns;
5722                     image->rows=im->rows;
5723                     im=DestroyImage(im);
5724                     image->page.width=image->columns;
5725                     image->page.height=image->rows;
5726                     image->page.x=crop_box.left;
5727                     image->page.y=crop_box.top;
5728                   }
5729               }
5730             else
5731               {
5732                 /*
5733                   No pixels in crop area.  The MNG spec still requires
5734                   a layer, though, so make a single transparent pixel in
5735                   the top left corner.
5736                 */
5737                 image->columns=1;
5738                 image->rows=1;
5739                 image->colors=2;
5740                 (void) SetImageBackgroundColor(image);
5741                 image->page.width=1;
5742                 image->page.height=1;
5743                 image->page.x=0;
5744                 image->page.y=0;
5745               }
5746           }
5747 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
5748         image=mng_info->image;
5749 #endif
5750       }
5751
5752 #if (MAGICKCORE_QUANTUM_DEPTH == 16)  /* TO DO: treat Q:32 */
5753     /* Determine if bit depth can be reduced from 16 to 8.
5754      * Note that the method GetImageDepth doesn't check background
5755      * and doesn't handle PseudoClass specially.  Also it uses
5756      * multiplication and division by 257 instead of shifting, so
5757      * might be slower.
5758      */
5759     if (mng_info->optimize && image->depth == 16)
5760       {
5761         int
5762           ok_to_reduce;
5763
5764         const PixelPacket
5765           *p;
5766
5767         ok_to_reduce=(((((size_t) image->background_color.red >> 8) &
5768                      0xff)
5769           == ((size_t) image->background_color.red & 0xff)) &&
5770            ((((size_t) image->background_color.green >> 8) & 0xff)
5771           == ((size_t) image->background_color.green & 0xff)) &&
5772            ((((size_t) image->background_color.blue >> 8) & 0xff)
5773           == ((size_t) image->background_color.blue & 0xff)));
5774         if (ok_to_reduce && image->storage_class == PseudoClass)
5775           {
5776             int indx;
5777
5778             for (indx=0; indx < (ssize_t) image->colors; indx++)
5779               {
5780                 ok_to_reduce=(((((size_t) image->colormap[indx].red >>
5781                     8) & 0xff)
5782                   == ((size_t) image->colormap[indx].red & 0xff)) &&
5783                   ((((size_t) image->colormap[indx].green >> 8) & 0xff)
5784                   == ((size_t) image->colormap[indx].green & 0xff)) &&
5785                   ((((size_t) image->colormap[indx].blue >> 8) & 0xff)
5786                   == ((size_t) image->colormap[indx].blue & 0xff)));
5787                 if (ok_to_reduce == MagickFalse)
5788                   break;
5789               }
5790           }
5791         if ((ok_to_reduce != MagickFalse) &&
5792             (image->storage_class != PseudoClass))
5793           {
5794             ssize_t
5795               y;
5796
5797             register ssize_t
5798               x;
5799
5800             for (y=0; y < (ssize_t) image->rows; y++)
5801             {
5802               p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
5803               if (p == (const PixelPacket *) NULL)
5804                 break;
5805               for (x=(ssize_t) image->columns-1; x >= 0; x--)
5806               {
5807                 ok_to_reduce=((
5808                   (((size_t) p->red >> 8) & 0xff) ==
5809                   ((size_t) p->red & 0xff)) &&
5810                   ((((size_t) p->green >> 8) & 0xff) ==
5811                   ((size_t) p->green & 0xff)) &&
5812                   ((((size_t) p->blue >> 8) & 0xff) ==
5813                   ((size_t) p->blue & 0xff)) &&
5814                   (((!image->matte ||
5815                   (((size_t) p->opacity >> 8) & 0xff) ==
5816                   ((size_t) p->opacity & 0xff)))));
5817                 if (ok_to_reduce == 0)
5818                   break;
5819                 p++;
5820               }
5821               if (x != 0)
5822                 break;
5823             }
5824           }
5825         if (ok_to_reduce)
5826           {
5827             image->depth=8;
5828             if (logging != MagickFalse)
5829               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5830                 "  Reducing PNG bit depth to 8 without loss of info");
5831           }
5832       }
5833 #endif
5834       GetImageException(image,exception);
5835       if (image_info->number_scenes != 0)
5836         {
5837           if (mng_info->scenes_found >
5838              (ssize_t) (image_info->first_scene+image_info->number_scenes))
5839             break;
5840         }
5841       if (logging != MagickFalse)
5842         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5843           "  Finished reading image datastream.");
5844   } while (LocaleCompare(image_info->magick,"MNG") == 0);
5845   (void) CloseBlob(image);
5846   if (logging != MagickFalse)
5847     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5848       "  Finished reading all image datastreams.");
5849 #if defined(MNG_INSERT_LAYERS)
5850   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
5851        (mng_info->mng_height))
5852     {
5853       /*
5854         Insert a background layer if nothing else was found.
5855       */
5856       if (logging != MagickFalse)
5857         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5858           "  No images found.  Inserting a background layer.");
5859       if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5860         {
5861           /*
5862             Allocate next image structure.
5863           */
5864           AcquireNextImage(image_info,image);
5865           if (GetNextImageInList(image) == (Image *) NULL)
5866             {
5867               image=DestroyImageList(image);
5868               MngInfoFreeStruct(mng_info,&have_mng_structure);
5869               if (logging != MagickFalse)
5870                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5871                   "  Allocation failed, returning NULL.");
5872               return((Image *) NULL);
5873             }
5874           image=SyncNextImageInList(image);
5875         }
5876       image->columns=mng_info->mng_width;
5877       image->rows=mng_info->mng_height;
5878       image->page.width=mng_info->mng_width;
5879       image->page.height=mng_info->mng_height;
5880       image->page.x=0;
5881       image->page.y=0;
5882       image->background_color=mng_background_color;
5883       image->matte=MagickFalse;
5884       if (image_info->ping == MagickFalse)
5885         (void) SetImageBackgroundColor(image);
5886       mng_info->image_found++;
5887     }
5888 #endif
5889   image->iterations=mng_iterations;
5890   if (mng_iterations == 1)
5891     image->start_loop=MagickTrue;
5892   while (GetPreviousImageInList(image) != (Image *) NULL)
5893   {
5894     image_count++;
5895     if (image_count > 10*mng_info->image_found)
5896       {
5897         if (logging != MagickFalse)
5898           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
5899         (void) ThrowMagickException(&image->exception,GetMagickModule(),
5900           CoderError,"Linked list is corrupted, beginning of list not found",
5901           "`%s'",image_info->filename);
5902         return((Image *) NULL);
5903       }
5904     image=GetPreviousImageInList(image);
5905     if (GetNextImageInList(image) == (Image *) NULL)
5906       {
5907         if (logging != MagickFalse)
5908           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
5909         (void) ThrowMagickException(&image->exception,GetMagickModule(),
5910           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
5911           image_info->filename);
5912       }
5913   }
5914   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
5915              GetNextImageInList(image) ==
5916      (Image *) NULL)
5917     {
5918       if (logging != MagickFalse)
5919         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5920             "  First image null");
5921       (void) ThrowMagickException(&image->exception,GetMagickModule(),
5922         CoderError,"image->next for first image is NULL but shouldn't be.",
5923         "`%s'",image_info->filename);
5924     }
5925   if (mng_info->image_found == 0)
5926     {
5927       if (logging != MagickFalse)
5928         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5929           "  No visible images found.");
5930       (void) ThrowMagickException(&image->exception,GetMagickModule(),
5931         CoderError,"No visible images in file","`%s'",image_info->filename);
5932       if (image != (Image *) NULL)
5933         image=DestroyImageList(image);
5934       MngInfoFreeStruct(mng_info,&have_mng_structure);
5935       return((Image *) NULL);
5936     }
5937
5938   if (mng_info->ticks_per_second)
5939     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
5940             final_delay/mng_info->ticks_per_second;
5941   else
5942     image->start_loop=MagickTrue;
5943   /* Find final nonzero image delay */
5944   final_image_delay=0;
5945   while (GetNextImageInList(image) != (Image *) NULL)
5946     {
5947       if (image->delay)
5948         final_image_delay=image->delay;
5949       image=GetNextImageInList(image);
5950     }
5951   if (final_delay < final_image_delay)
5952     final_delay=final_image_delay;
5953   image->delay=final_delay;
5954   if (logging != MagickFalse)
5955       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5956         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
5957         (double) final_delay);
5958   if (logging != MagickFalse)
5959     {
5960       int
5961         scene;
5962
5963       scene=0;
5964       image=GetFirstImageInList(image);
5965       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5966         "  Before coalesce:");
5967       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5968         "    scene 0 delay=%.20g",(double) image->delay);
5969       while (GetNextImageInList(image) != (Image *) NULL)
5970       {
5971         image=GetNextImageInList(image);
5972         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5973           "    scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
5974       }
5975     }
5976
5977   image=GetFirstImageInList(image);
5978 #ifdef MNG_COALESCE_LAYERS
5979   if (insert_layers)
5980     {
5981       Image
5982         *next_image,
5983         *next;
5984
5985       size_t
5986         scene;
5987
5988       if (logging != MagickFalse)
5989         (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Coalesce Images");
5990       scene=image->scene;
5991       next_image=CoalesceImages(image,&image->exception);
5992       if (next_image == (Image *) NULL)
5993         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5994       image=DestroyImageList(image);
5995       image=next_image;
5996       for (next=image; next != (Image *) NULL; next=next_image)
5997       {
5998          next->page.width=mng_info->mng_width;
5999          next->page.height=mng_info->mng_height;
6000          next->page.x=0;
6001          next->page.y=0;
6002          next->scene=scene++;
6003          next_image=GetNextImageInList(next);
6004          if (next_image == (Image *) NULL)
6005            break;
6006          if (next->delay == 0)
6007            {
6008              scene--;
6009              next_image->previous=GetPreviousImageInList(next);
6010              if (GetPreviousImageInList(next) == (Image *) NULL)
6011                image=next_image;
6012              else
6013                next->previous->next=next_image;
6014              next=DestroyImage(next);
6015            }
6016       }
6017     }
6018 #endif
6019
6020   while (GetNextImageInList(image) != (Image *) NULL)
6021       image=GetNextImageInList(image);
6022   image->dispose=BackgroundDispose;
6023
6024   if (logging != MagickFalse)
6025     {
6026       int
6027         scene;
6028
6029       scene=0;
6030       image=GetFirstImageInList(image);
6031       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6032         "  After coalesce:");
6033       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6034         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6035         (double) image->dispose);
6036       while (GetNextImageInList(image) != (Image *) NULL)
6037       {
6038         image=GetNextImageInList(image);
6039         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6040           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6041           (double) image->delay,(double) image->dispose);
6042       }
6043    }
6044   image=GetFirstImageInList(image);
6045   MngInfoFreeStruct(mng_info,&have_mng_structure);
6046   have_mng_structure=MagickFalse;
6047   if (logging != MagickFalse)
6048     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
6049   return(GetFirstImageInList(image));
6050 }
6051 #else /* PNG_LIBPNG_VER > 10011 */
6052 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6053 {
6054   printf("Your PNG library is too old: You have libpng-%s\n",
6055      PNG_LIBPNG_VER_STRING);
6056   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6057     "PNG library is too old","`%s'",image_info->filename);
6058   return(Image *) NULL;
6059 }
6060 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6061 {
6062   return(ReadPNGImage(image_info,exception));
6063 }
6064 #endif /* PNG_LIBPNG_VER > 10011 */
6065 #endif
6066 \f
6067 /*
6068 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6069 %                                                                             %
6070 %                                                                             %
6071 %                                                                             %
6072 %   R e g i s t e r P N G I m a g e                                           %
6073 %                                                                             %
6074 %                                                                             %
6075 %                                                                             %
6076 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6077 %
6078 %  RegisterPNGImage() adds properties for the PNG image format to
6079 %  the list of supported formats.  The properties include the image format
6080 %  tag, a method to read and/or write the format, whether the format
6081 %  supports the saving of more than one frame to the same file or blob,
6082 %  whether the format supports native in-memory I/O, and a brief
6083 %  description of the format.
6084 %
6085 %  The format of the RegisterPNGImage method is:
6086 %
6087 %      size_t RegisterPNGImage(void)
6088 %
6089 */
6090 ModuleExport size_t RegisterPNGImage(void)
6091 {
6092   char
6093     version[MaxTextExtent];
6094
6095   MagickInfo
6096     *entry;
6097
6098   static const char
6099     *PNGNote=
6100     {
6101       "See http://www.libpng.org/ for details about the PNG format."
6102     },
6103     *JNGNote=
6104     {
6105       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6106       "format."
6107     },
6108     *MNGNote=
6109     {
6110       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6111       "format."
6112     };
6113
6114   *version='\0';
6115 #if defined(PNG_LIBPNG_VER_STRING)
6116   (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
6117   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
6118   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
6119     {
6120       (void) ConcatenateMagickString(version,",",MaxTextExtent);
6121       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
6122             MaxTextExtent);
6123     }
6124 #endif
6125   entry=SetMagickInfo("MNG");
6126   entry->seekable_stream=MagickTrue;  /* To do: eliminate this. */
6127 #if defined(MAGICKCORE_PNG_DELEGATE)
6128   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
6129   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
6130 #endif
6131   entry->magick=(IsImageFormatHandler *) IsMNG;
6132   entry->description=ConstantString("Multiple-image Network Graphics");
6133   if (*version != '\0')
6134     entry->version=ConstantString(version);
6135   entry->module=ConstantString("PNG");
6136   entry->note=ConstantString(MNGNote);
6137   (void) RegisterMagickInfo(entry);
6138
6139   entry=SetMagickInfo("PNG");
6140 #if defined(MAGICKCORE_PNG_DELEGATE)
6141   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6142   entry->encoder=(EncodeImageHandler *) WritePNGImage;
6143 #endif
6144   entry->magick=(IsImageFormatHandler *) IsPNG;
6145   entry->adjoin=MagickFalse;
6146   entry->description=ConstantString("Portable Network Graphics");
6147   entry->module=ConstantString("PNG");
6148   if (*version != '\0')
6149     entry->version=ConstantString(version);
6150   entry->note=ConstantString(PNGNote);
6151   (void) RegisterMagickInfo(entry);
6152
6153   entry=SetMagickInfo("PNG8");
6154 #if defined(MAGICKCORE_PNG_DELEGATE)
6155   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6156   entry->encoder=(EncodeImageHandler *) WritePNGImage;
6157 #endif
6158   entry->magick=(IsImageFormatHandler *) IsPNG;
6159   entry->adjoin=MagickFalse;
6160   entry->description=ConstantString(
6161             "8-bit indexed with optional binary transparency");
6162   entry->module=ConstantString("PNG");
6163   (void) RegisterMagickInfo(entry);
6164
6165   entry=SetMagickInfo("PNG24");
6166   *version='\0';
6167 #if defined(ZLIB_VERSION)
6168   (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
6169   (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
6170   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
6171     {
6172       (void) ConcatenateMagickString(version,",",MaxTextExtent);
6173       (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
6174     }
6175 #endif
6176   if (*version != '\0')
6177     entry->version=ConstantString(version);
6178 #if defined(MAGICKCORE_PNG_DELEGATE)
6179   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6180   entry->encoder=(EncodeImageHandler *) WritePNGImage;
6181 #endif
6182   entry->magick=(IsImageFormatHandler *) IsPNG;
6183   entry->adjoin=MagickFalse;
6184   entry->description=ConstantString("opaque 24-bit RGB");
6185   entry->module=ConstantString("PNG");
6186   (void) RegisterMagickInfo(entry);
6187
6188   entry=SetMagickInfo("PNG32");
6189 #if defined(MAGICKCORE_PNG_DELEGATE)
6190   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
6191   entry->encoder=(EncodeImageHandler *) WritePNGImage;
6192 #endif
6193   entry->magick=(IsImageFormatHandler *) IsPNG;
6194   entry->adjoin=MagickFalse;
6195   entry->description=ConstantString("opaque or transparent 32-bit RGBA");
6196   entry->module=ConstantString("PNG");
6197   (void) RegisterMagickInfo(entry);
6198
6199   entry=SetMagickInfo("JNG");
6200 #if defined(JNG_SUPPORTED)
6201 #if defined(MAGICKCORE_PNG_DELEGATE)
6202   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
6203   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
6204 #endif
6205 #endif
6206   entry->magick=(IsImageFormatHandler *) IsJNG;
6207   entry->adjoin=MagickFalse;
6208   entry->description=ConstantString("JPEG Network Graphics");
6209   entry->module=ConstantString("PNG");
6210   entry->note=ConstantString(JNGNote);
6211   (void) RegisterMagickInfo(entry);
6212 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6213   png_semaphore=AllocateSemaphoreInfo();
6214 #endif
6215   return(MagickImageCoderSignature);
6216 }
6217 \f
6218 /*
6219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6220 %                                                                             %
6221 %                                                                             %
6222 %                                                                             %
6223 %   U n r e g i s t e r P N G I m a g e                                       %
6224 %                                                                             %
6225 %                                                                             %
6226 %                                                                             %
6227 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6228 %
6229 %  UnregisterPNGImage() removes format registrations made by the
6230 %  PNG module from the list of supported formats.
6231 %
6232 %  The format of the UnregisterPNGImage method is:
6233 %
6234 %      UnregisterPNGImage(void)
6235 %
6236 */
6237 ModuleExport void UnregisterPNGImage(void)
6238 {
6239   (void) UnregisterMagickInfo("MNG");
6240   (void) UnregisterMagickInfo("PNG");
6241   (void) UnregisterMagickInfo("PNG8");
6242   (void) UnregisterMagickInfo("PNG24");
6243   (void) UnregisterMagickInfo("PNG32");
6244   (void) UnregisterMagickInfo("JNG");
6245 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6246   if (png_semaphore != (SemaphoreInfo *) NULL)
6247     DestroySemaphoreInfo(&png_semaphore);
6248 #endif
6249 }
6250 \f
6251 #if defined(MAGICKCORE_PNG_DELEGATE)
6252 #if PNG_LIBPNG_VER > 10011
6253 /*
6254 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6255 %                                                                             %
6256 %                                                                             %
6257 %                                                                             %
6258 %   W r i t e M N G I m a g e                                                 %
6259 %                                                                             %
6260 %                                                                             %
6261 %                                                                             %
6262 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6263 %
6264 %  WriteMNGImage() writes an image in the Portable Network Graphics
6265 %  Group's "Multiple-image Network Graphics" encoded image format.
6266 %
6267 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
6268 %
6269 %  The format of the WriteMNGImage method is:
6270 %
6271 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
6272 %
6273 %  A description of each parameter follows.
6274 %
6275 %    o image_info: the image info.
6276 %
6277 %    o image:  The image.
6278 %
6279 %
6280 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
6281 %    "To do" under ReadPNGImage):
6282 %
6283 %    Fix problem with palette sorting (when PNG_SORT_PALETTE is enabled,
6284 %    some GIF animations don't convert properly)
6285 %
6286 %    Preserve all unknown and not-yet-handled known chunks found in input
6287 %    PNG file and copy them  into output PNG files according to the PNG
6288 %    copying rules.
6289 %
6290 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
6291 %
6292 %    Improve selection of color type (use indexed-colour or indexed-colour
6293 %    with tRNS when 256 or fewer unique RGBA values are present).
6294 %
6295 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
6296 %    This will be complicated if we limit ourselves to generating MNG-LC
6297 %    files.  For now we ignore disposal method 3 and simply overlay the next
6298 %    image on it.
6299 %
6300 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
6301 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
6302 %    [mostly done 15 June 1999 but still need to take care of tRNS]
6303 %
6304 %    Check for identical sRGB and replace with a global sRGB (and remove
6305 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
6306 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
6307 %    local gAMA/cHRM with local sRGB if appropriate).
6308 %
6309 %    Check for identical sBIT chunks and write global ones.
6310 %
6311 %    Provide option to skip writing the signature tEXt chunks.
6312 %
6313 %    Use signatures to detect identical objects and reuse the first
6314 %    instance of such objects instead of writing duplicate objects.
6315 %
6316 %    Use a smaller-than-32k value of compression window size when
6317 %    appropriate.
6318 %
6319 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
6320 %    ancillary text chunks and save profiles.
6321 %
6322 %    Provide an option to force LC files (to ensure exact framing rate)
6323 %    instead of VLC.
6324 %
6325 %    Provide an option to force VLC files instead of LC, even when offsets
6326 %    are present.  This will involve expanding the embedded images with a
6327 %    transparent region at the top and/or left.
6328 */
6329
6330 static void
6331 png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
6332    png_info *ping_info, unsigned char *profile_type, unsigned char
6333    *profile_description, unsigned char *profile_data, png_uint_32 length)
6334 {
6335    png_textp
6336      text;
6337
6338    register ssize_t
6339      i;
6340
6341    unsigned char
6342      *sp;
6343
6344    png_charp
6345      dp;
6346
6347    png_uint_32
6348      allocated_length,
6349      description_length;
6350
6351    unsigned char
6352      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
6353
6354    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
6355       return;
6356
6357    if (image_info->verbose)
6358      {
6359      (void) printf("writing raw profile: type=%s, length=%.20g\n",
6360        (char *) profile_type, (double) length);
6361      }
6362    text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
6363    description_length=(png_uint_32) strlen((const char *) profile_description);
6364    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
6365       + description_length);
6366    text[0].text=(png_charp) png_malloc(ping,allocated_length);
6367    text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
6368    text[0].key[0]='\0';
6369    (void) ConcatenateMagickString(text[0].key,
6370       "Raw profile type ",MaxTextExtent);
6371    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
6372    sp=profile_data;
6373    dp=text[0].text;
6374    *dp++='\n';
6375    (void) CopyMagickString(dp,(const char *) profile_description,
6376      allocated_length);
6377    dp+=description_length;
6378    *dp++='\n';
6379    (void) FormatMagickString(dp,allocated_length-
6380      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
6381    dp+=8;
6382    for (i=0; i < (ssize_t) length; i++)
6383    {
6384      if (i%36 == 0)
6385        *dp++='\n';
6386      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
6387      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
6388    }
6389    *dp++='\n';
6390    *dp='\0';
6391    text[0].text_length=(png_size_t) (dp-text[0].text);
6392    text[0].compression=image_info->compression == NoCompression ||
6393      (image_info->compression == UndefinedCompression &&
6394      text[0].text_length < 128) ? -1 : 0;
6395    if (text[0].text_length <= allocated_length)
6396      png_set_text(ping,ping_info,text,1);
6397    png_free(ping,text[0].text);
6398    png_free(ping,text[0].key);
6399    png_free(ping,text);
6400 }
6401
6402 static MagickBooleanType png_write_chunk_from_profile(Image *image,
6403    const char *string, int logging)
6404 {
6405   char
6406     *name;
6407
6408   const StringInfo
6409     *profile;
6410
6411   unsigned char
6412     *data;
6413
6414   png_uint_32 length;
6415
6416   ResetImageProfileIterator(image);
6417   for (name=GetNextImageProfile(image); name != (const char *) NULL; ){
6418     profile=GetImageProfile(image,name);
6419     if (profile != (const StringInfo *) NULL)
6420       {
6421         StringInfo
6422           *png_profile;
6423
6424         if (LocaleNCompare(name,string,11) == 0) {
6425           if (logging != MagickFalse)
6426              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6427              "  Found %s profile",name);
6428
6429        png_profile=CloneStringInfo(profile);
6430        data=GetStringInfoDatum(png_profile),
6431        length=(png_uint_32) GetStringInfoLength(png_profile);
6432        data[4]=data[3];
6433        data[3]=data[2];
6434        data[2]=data[1];
6435        data[1]=data[0];
6436        (void) WriteBlobMSBULong(image,length-5);  /* data length */
6437        (void) WriteBlob(image,length-1,data+1);
6438        (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
6439        png_profile=DestroyStringInfo(png_profile);
6440         }
6441       }
6442       name=GetNextImageProfile(image);
6443    }
6444    return(MagickTrue);
6445 }
6446
6447 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
6448    const ImageInfo *image_info,Image *image)
6449 {
6450 /* Write one PNG image */
6451   char
6452     s[2];
6453
6454   const char
6455     *name,
6456     *property,
6457     *value;
6458
6459   const StringInfo
6460     *profile;
6461
6462
6463   int
6464     image_matte,
6465     num_passes,
6466     pass;
6467
6468   png_byte
6469      ping_trans_alpha[256];
6470
6471   png_colorp
6472      palette;
6473
6474   png_color_16
6475     ping_background,
6476     ping_trans_color;
6477
6478   png_info
6479     *ping_info;
6480
6481   png_struct
6482     *ping;
6483
6484   png_uint_32
6485     ping_height,
6486     ping_width;
6487
6488   ssize_t
6489     y;
6490
6491   MagickBooleanType
6492     status;
6493
6494   QuantumInfo
6495     *quantum_info;
6496
6497   register IndexPacket
6498     *indexes;
6499
6500   register ssize_t
6501     i,
6502     x;
6503
6504   unsigned char
6505     *png_pixels;
6506
6507   unsigned int
6508     logging,
6509     matte;
6510
6511   volatile int
6512     ping_bit_depth, 
6513     ping_color_type,
6514     ping_interlace_method,
6515     ping_compression_method,
6516     ping_filter_method,
6517     ping_num_trans;
6518
6519   volatile size_t
6520     image_colors,
6521     image_depth,
6522     old_bit_depth;
6523
6524   size_t
6525     quality,
6526     rowbytes,
6527     save_image_depth;
6528
6529   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
6530     "  enter WriteOnePNGImage()");
6531
6532 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6533   LockSemaphoreInfo(png_semaphore);
6534 #endif
6535
6536   /* Initialize some stuff */
6537   ping_bit_depth=0, 
6538   ping_color_type=0,
6539   ping_interlace_method=0,
6540   ping_compression_method=0,
6541   ping_filter_method=0,
6542   ping_num_trans = 0;
6543
6544   ping_background.red = 0;
6545   ping_background.green = 0;
6546   ping_background.blue = 0;
6547   ping_background.gray = 0;
6548   ping_background.index = 0;
6549
6550   ping_trans_color.red=0;
6551   ping_trans_color.green=0;
6552   ping_trans_color.blue=0;
6553   ping_trans_color.gray=0;
6554
6555   quantum_info = (QuantumInfo *) NULL;
6556   image_colors=image->colors;
6557   image_depth=image->depth;
6558   image_matte=image->matte;
6559
6560   if (image->colorspace != RGBColorspace)
6561     (void) TransformImageColorspace(image,RGBColorspace);
6562   mng_info->IsPalette=image->storage_class == PseudoClass && 
6563     image_colors <= 256 && !IsOpaqueImage(image,&image->exception);
6564   mng_info->optimize=image_info->type == OptimizeType;
6565
6566   /*
6567     Allocate the PNG structures
6568   */
6569 #ifdef PNG_USER_MEM_SUPPORTED
6570   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
6571     PNGErrorHandler,PNGWarningHandler,(void *) NULL,
6572     (png_malloc_ptr) png_IM_malloc,(png_free_ptr) png_IM_free);
6573 #else
6574   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
6575     PNGErrorHandler,PNGWarningHandler);
6576 #endif
6577   if (ping == (png_struct *) NULL)
6578     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6579   ping_info=png_create_info_struct(ping);
6580   if (ping_info == (png_info *) NULL)
6581     {
6582       png_destroy_write_struct(&ping,(png_info **) NULL);
6583       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6584     }
6585   png_set_write_fn(ping,image,png_put_data,png_flush_data);
6586   png_pixels=(unsigned char *) NULL;
6587
6588   if (setjmp(png_jmpbuf(ping)))
6589     {
6590       /*
6591         PNG write failed.
6592       */
6593 #ifdef PNG_DEBUG
6594      if (image_info->verbose)
6595         (void) printf("PNG write has failed.\n");
6596 #endif
6597       png_destroy_write_struct(&ping,&ping_info);
6598 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
6599       UnlockSemaphoreInfo(png_semaphore);
6600 #endif
6601       return(MagickFalse);
6602     }
6603   /*
6604     Prepare PNG for writing.
6605   */
6606 #if defined(PNG_MNG_FEATURES_SUPPORTED)
6607   if (mng_info->write_mng)
6608      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
6609 #else
6610 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
6611   if (mng_info->write_mng)
6612      png_permit_empty_plte(ping,MagickTrue);
6613 # endif
6614 #endif
6615   x=0;
6616   ping_width=(png_uint_32) image->columns;
6617   ping_height=(png_uint_32) image->rows;
6618   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
6619      image_depth=8;
6620   if (mng_info->write_png_depth != 0)
6621      image_depth=mng_info->write_png_depth;
6622   /* Adjust requested depth to next higher valid depth if necessary */
6623   if (image_depth > 8)
6624      image_depth=16;
6625   if ((image_depth > 4) && (image_depth < 8))
6626      image_depth=8;
6627   if (image_depth == 3)
6628      image_depth=4;
6629   if (logging != MagickFalse)
6630     {
6631      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6632         "    width=%.20g",(double) ping_width);
6633      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6634         "    height=%.20g",(double) ping_height);
6635      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6636         "    image_matte=%.20g",(double) image->matte);
6637      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6638         "    image_depth=%.20g",(double) image->depth);
6639      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6640         "    requested PNG image_depth=%.20g",(double) image->depth);
6641     }
6642   save_image_depth=image_depth;
6643   ping_bit_depth=(png_byte) save_image_depth;
6644 #if defined(PNG_pHYs_SUPPORTED)
6645   if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
6646       (!mng_info->write_mng || !mng_info->equal_physs))
6647     {
6648       int
6649         unit_type;
6650
6651       png_uint_32
6652         x_resolution,
6653         y_resolution;
6654
6655       if (image->units == PixelsPerInchResolution)
6656         {
6657           unit_type=PNG_RESOLUTION_METER;
6658           x_resolution=(png_uint_32) (100.0*image->x_resolution/2.54);
6659           y_resolution=(png_uint_32) (100.0*image->y_resolution/2.54);
6660         }
6661       else if (image->units == PixelsPerCentimeterResolution)
6662         {
6663           unit_type=PNG_RESOLUTION_METER;
6664           x_resolution=(png_uint_32) (100.0*image->x_resolution);
6665           y_resolution=(png_uint_32) (100.0*image->y_resolution);
6666         }
6667       else
6668         {
6669           unit_type=PNG_RESOLUTION_UNKNOWN;
6670           x_resolution=(png_uint_32) image->x_resolution;
6671           y_resolution=(png_uint_32) image->y_resolution;
6672         }
6673        png_set_pHYs(ping,ping_info,x_resolution,y_resolution,unit_type);
6674        if (logging != MagickFalse)
6675          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6676            "    Setting up pHYs chunk");
6677     }
6678 #endif
6679 #if defined(PNG_oFFs_SUPPORTED)
6680   if (image->page.x || image->page.y)
6681     {
6682        png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
6683           (png_int_32) image->page.y, 0);
6684        if (logging != MagickFalse)
6685          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6686            "    Setting up oFFs chunk");
6687     }
6688 #endif
6689   if (image_matte && (!mng_info->adjoin || !mng_info->equal_backgrounds))
6690     {
6691       png_color_16
6692         background;
6693
6694       if (image_depth < MAGICKCORE_QUANTUM_DEPTH)
6695         {
6696           size_t
6697              maxval;
6698
6699           maxval=(1UL << image_depth)-1;
6700           background.red=(png_uint_16)
6701             (QuantumScale*(maxval*image->background_color.red));
6702           background.green=(png_uint_16)
6703             (QuantumScale*(maxval*image->background_color.green));
6704           background.blue=(png_uint_16)
6705             (QuantumScale*(maxval*image->background_color.blue));
6706           background.gray=(png_uint_16)
6707             (QuantumScale*(maxval*PixelIntensity(&image->background_color)));
6708         }
6709       else
6710         {
6711           background.red=image->background_color.red;
6712           background.green=image->background_color.green;
6713           background.blue=image->background_color.blue;
6714           background.gray=
6715             (png_uint_16) PixelIntensity(&image->background_color);
6716         }
6717       background.index=(png_byte) background.gray;
6718       if (logging != MagickFalse)
6719         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6720           "    Setting up bKGd chunk");
6721       png_set_bKGD(ping,ping_info,&background);
6722     }
6723   /*
6724     Select the color type.
6725   */
6726   matte=image_matte;
6727   old_bit_depth=0;
6728   if ((mng_info->write_png_colortype-1) == PNG_COLOR_TYPE_PALETTE)
6729     mng_info->write_png8=MagickTrue;
6730   if (mng_info->write_png8)
6731     {
6732       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6733       ping_bit_depth=8;
6734       image_depth=ping_bit_depth;
6735         {
6736           /* TO DO: make this a function cause it's used twice, except
6737              for reducing the sample depth from 8. */
6738
6739           QuantizeInfo
6740             quantize_info;
6741
6742           size_t
6743              number_colors,
6744              save_number_colors;
6745
6746           number_colors=image_colors;
6747           if ((image->storage_class == DirectClass) || (number_colors > 256))
6748             {
6749               GetQuantizeInfo(&quantize_info);
6750               quantize_info.dither=IsPaletteImage(image,&image->exception) ==
6751                 MagickFalse ? MagickTrue : MagickFalse;
6752               quantize_info.number_colors= (matte != MagickFalse ? 255UL :
6753                 256UL);
6754               (void) QuantizeImage(&quantize_info,image);
6755               number_colors=image_colors;
6756               (void) SyncImage(image);
6757               if (logging != MagickFalse)
6758                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6759                   "    Colors quantized to %.20g",(double) number_colors);
6760             }
6761           if (matte)
6762             png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
6763           /*
6764             Set image palette.
6765           */
6766           ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
6767 #if defined(PNG_SORT_PALETTE)
6768           save_number_colors=image_colors;
6769           if (CompressColormapTransFirst(image) == MagickFalse)
6770             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6771           number_colors=image->colors;
6772           image_colors=save_number_colors;
6773 #endif
6774           palette=(png_color *) AcquireQuantumMemory(257,
6775             sizeof(*palette));
6776           if (palette == (png_color *) NULL)
6777             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
6778           if (logging != MagickFalse)
6779             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6780                 "  Setting up PLTE chunk with %d colors",
6781                 (int) number_colors);
6782           for (i=0; i < (ssize_t) number_colors; i++)
6783           {
6784             palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
6785             palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
6786             palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
6787             if (logging != MagickFalse)
6788               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6789 #if MAGICKCORE_QUANTUM_DEPTH == 8
6790                 "    %3ld (%3d,%3d,%3d)",
6791 #else
6792                 "    %5ld (%5d,%5d,%5d)",
6793 #endif
6794                 (long) i,palette[i].red,palette[i].green,palette[i].blue);
6795
6796           }
6797           if (matte)
6798             {
6799               number_colors++;
6800               palette[i].red=ScaleQuantumToChar((Quantum) QuantumRange);
6801               palette[i].green=ScaleQuantumToChar((Quantum) QuantumRange);
6802               palette[i].blue=ScaleQuantumToChar((Quantum) QuantumRange);
6803             }
6804           png_set_PLTE(ping,ping_info,palette,(int) number_colors);
6805           palette=(png_colorp) RelinquishMagickMemory(palette);
6806             image_depth=ping_bit_depth;
6807             ping_num_trans=0;
6808             if (matte)
6809             {
6810               ExceptionInfo
6811                 *exception;
6812
6813               int
6814                 trans_alpha[256];
6815
6816               /*
6817                 Identify which colormap entry is transparent.
6818               */
6819               assert(number_colors <= 256);
6820               for (i=0; i < (ssize_t) number_colors; i++)
6821                  trans_alpha[i]=255;
6822               exception=(&image->exception);
6823               for (y=0; y < (ssize_t) image->rows; y++)
6824               {
6825                 register const PixelPacket
6826                   *p;
6827
6828                 p=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6829                 if (p == (PixelPacket *) NULL)
6830                   break;
6831                 indexes=GetAuthenticIndexQueue(image);
6832                 for (x=0; x < (ssize_t) image->columns; x++)
6833                 {
6834                   if (p->opacity != OpaqueOpacity)
6835                     {
6836                       indexes[x]=(IndexPacket) (number_colors-1);
6837                       trans_alpha[(ssize_t) indexes[x]]=(png_byte) (255-
6838                         ScaleQuantumToChar(GetOpacityPixelComponent(p)));
6839                     }
6840                   p++;
6841                 }
6842                 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6843                   break;
6844               }
6845               for (i=0; i < (ssize_t) number_colors; i++)
6846                 if (trans_alpha[i] != 255)
6847                   ping_num_trans=(unsigned short) (i+1);
6848
6849               if (ping_num_trans == 0)
6850                  png_set_invalid(ping, ping_info, PNG_INFO_tRNS);
6851               if (!png_get_valid(ping, ping_info, PNG_INFO_tRNS))
6852                 ping_num_trans=0;
6853               if (ping_num_trans != 0)
6854                 {
6855                   for (i=0; i<256; i++)
6856                      ping_trans_alpha[i]=(png_byte) trans_alpha[i];
6857                 }
6858
6859               (void) png_set_tRNS(ping, ping_info,
6860                                   ping_trans_alpha,
6861                                   ping_num_trans,
6862                                   &ping_trans_color);
6863             }
6864           /*
6865             Identify which colormap entry is the background color.
6866           */
6867           for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
6868             if (IsPNGColorEqual(ping_background,image->colormap[i]))
6869               break;
6870           ping_background.index=(png_byte) i;
6871         }
6872       if (image_matte != MagickFalse)
6873         {
6874           /* TO DO: reduce to binary transparency */
6875         }
6876     } /* end of write_png8 */
6877   else if (mng_info->write_png24)
6878     {
6879       image_matte=MagickFalse;
6880       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6881     }
6882   else if (mng_info->write_png32)
6883     {
6884       image_matte=MagickTrue;
6885       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6886     }
6887   else
6888     {
6889       image_depth=ping_bit_depth;
6890       if (mng_info->write_png_colortype)
6891         {
6892           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
6893           image_matte=MagickFalse;
6894           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6895               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6896             image_matte=MagickTrue;
6897         }
6898       else
6899         {
6900           if (logging != MagickFalse)
6901              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6902              "Selecting PNG colortype");
6903           ping_color_type=(png_byte) ((matte == MagickTrue)?
6904           PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
6905           if(image_info->type == TrueColorType)
6906             {
6907               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
6908               image_matte=MagickFalse;
6909             }
6910           if(image_info->type == TrueColorMatteType)
6911             {
6912               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
6913               image_matte=MagickTrue;
6914             }
6915           if ((image_info->type == UndefinedType || 
6916              image_info->type == OptimizeType || 
6917              image_info->type == GrayscaleType) &&
6918              image_matte == MagickFalse && ImageIsGray(image))
6919             {
6920               ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
6921               image_matte=MagickFalse;
6922             }
6923           if ((image_info->type == UndefinedType ||
6924              image_info->type == OptimizeType || 
6925               image_info->type == GrayscaleMatteType) &&
6926               image_matte == MagickTrue && ImageIsGray(image))
6927             {
6928               ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
6929               image_matte=MagickTrue;
6930             } 
6931         }
6932       if (logging != MagickFalse)
6933          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6934          "Selected PNG colortype=%d",ping_color_type);
6935
6936       if (ping_bit_depth < 8)
6937        {
6938          if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
6939              ping_color_type == PNG_COLOR_TYPE_RGB ||
6940              ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
6941            ping_bit_depth=8;
6942        }
6943
6944       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
6945         {
6946           if (image->matte == MagickFalse && image->colors < 256)
6947             {
6948               if (ImageIsMonochrome(image))
6949                 {
6950                   ping_bit_depth=1;
6951                   if (ping_bit_depth < (int)mng_info->write_png_depth)
6952                     ping_bit_depth = mng_info->write_png_depth;
6953                 }
6954             }
6955         }
6956       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
6957         {
6958            size_t one = 1;
6959            ping_bit_depth=1;
6960
6961            if (image->colors == 0)
6962            {
6963               /* DO SOMETHING */       
6964               (void) ThrowMagickException(&image->exception,
6965                  GetMagickModule(),CoderError,
6966                 "image has 0 colors", "`%s'","");
6967            }
6968
6969               if (logging != MagickFalse)
6970                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6971                   "  SyncImage.2.");
6972            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
6973              ping_bit_depth <<= 1;
6974
6975            if (logging != MagickFalse)
6976              {
6977                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6978                 "    Number of colors: %.20g",(double) image_colors);
6979                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6980                 "    Tentative PNG bit depth: %d",ping_bit_depth);
6981              }
6982            if (mng_info->write_png_depth)
6983              {
6984                old_bit_depth=ping_bit_depth;
6985                if (ping_bit_depth < (int)mng_info->write_png_depth)
6986                  {
6987                    ping_bit_depth = mng_info->write_png_depth;
6988                    if (ping_bit_depth > 8)
6989                       ping_bit_depth = 8;
6990                    if (ping_bit_depth != (int) old_bit_depth)
6991                      {
6992                        if (logging != MagickFalse)
6993                          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6994                            "    Colors increased to %.20g",(double)
6995                            image_colors);
6996                      }
6997                  }
6998              }
6999         }
7000     }
7001   image_depth=ping_bit_depth;
7002   if (logging != MagickFalse)
7003     {
7004       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7005         "    Tentative PNG color type: %.20g",(double) ping_color_type);
7006       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7007         "    image_info->type: %.20g",(double) image_info->type);
7008       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7009         "    image_depth: %.20g",(double) image_depth);
7010       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7011         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
7012     }
7013
7014   if (matte && (mng_info->optimize || mng_info->IsPalette))
7015     {
7016       register const PixelPacket
7017         *p;
7018
7019       p=GetVirtualPixels(image,0,0,image->columns,1,&image->exception);
7020       ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
7021       for (y=0; y < (ssize_t) image->rows; y++)
7022       {
7023         p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7024         if (p == (const PixelPacket *) NULL)
7025           break;
7026         for (x=(ssize_t) image->columns-1; x >= 0; x--)
7027         {
7028           if (IsGray(p) == MagickFalse)
7029             {
7030               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
7031               break;
7032             }
7033           p++;
7034         }
7035       }
7036       /*
7037         Determine if there is any transparent color.
7038       */
7039       for (y=0; y < (ssize_t) image->rows; y++)
7040       {
7041         p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7042         if (p == (const PixelPacket *) NULL)
7043           break;
7044         for (x=(ssize_t) image->columns-1; x >= 0; x--)
7045         {
7046           if (p->opacity != OpaqueOpacity)
7047             break;
7048           p++;
7049         }
7050         if (x != 0)
7051           break;
7052       }
7053       if ((y == (ssize_t) image->rows) && (x == (ssize_t) image->columns))
7054         {
7055           /*
7056             No transparent pixels are present.  Change 4 or 6 to 0 or 2.
7057           */
7058           image_matte=MagickFalse;
7059           ping_color_type&=0x03;
7060         }
7061       else
7062         {
7063           unsigned int
7064             mask;
7065
7066           mask=0xffff;
7067           if (ping_bit_depth == 8)
7068              mask=0x00ff;
7069           if (ping_bit_depth == 4)
7070              mask=0x000f;
7071           if (ping_bit_depth == 2)
7072              mask=0x0003;
7073           if (ping_bit_depth == 1)
7074              mask=0x0001;
7075           ping_trans_color.red=(png_uint_16)
7076             (ScaleQuantumToShort(GetRedPixelComponent(p)) & mask);
7077           ping_trans_color.green=(png_uint_16)
7078             (ScaleQuantumToShort(GetGreenPixelComponent(p)) & mask);
7079           ping_trans_color.blue=(png_uint_16)
7080             (ScaleQuantumToShort(GetBluePixelComponent(p)) & mask);
7081           ping_trans_color.gray=(png_uint_16)
7082             (ScaleQuantumToShort(PixelIntensityToQuantum(p)) & mask);
7083           ping_trans_color.index=(png_byte)
7084             (ScaleQuantumToChar((Quantum) (GetAlphaPixelComponent(p))));
7085           (void) png_set_tRNS(ping, ping_info, NULL, 0,
7086              &ping_trans_color);
7087         }
7088       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7089         {
7090           /*
7091             Determine if there is one and only one transparent color
7092             and if so if it is fully transparent.
7093           */
7094           for (y=0; y < (ssize_t) image->rows; y++)
7095           {
7096             p=GetVirtualPixels(image,0,y,image->columns,1,
7097                &image->exception);
7098             x=0;
7099             if (p == (const PixelPacket *) NULL)
7100               break;
7101             for (x=(ssize_t) image->columns-1; x >= 0; x--)
7102             {
7103               if (p->opacity != OpaqueOpacity)
7104                 {
7105                   if (IsPNGColorEqual(ping_trans_color,*p) == 0)
7106                   {
7107                      break;  /* Can't use RGB + tRNS for multiple
7108                                 transparent colors.  */
7109                   }
7110                   if (p->opacity != (Quantum) TransparentOpacity)
7111                   {
7112                      break;  /* Can't use RGB + tRNS for
7113                                 semitransparency. */
7114                   }
7115                 }
7116                else
7117                 {
7118                   if (IsPNGColorEqual(ping_trans_color,*p))
7119                       break; /* Can't use RGB + tRNS when another pixel
7120                                 having the same RGB samples is
7121                                 transparent. */
7122                 }
7123             p++;
7124             }
7125             if (x != 0)
7126                break;
7127           }
7128           if (x != 0)
7129             png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7130         }
7131       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7132         {
7133           ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
7134           if (image_depth == 8)
7135             {
7136               ping_trans_color.red&=0xff;
7137               ping_trans_color.green&=0xff;
7138               ping_trans_color.blue&=0xff;
7139               ping_trans_color.gray&=0xff;
7140             }
7141         }
7142     }
7143     matte=image_matte;
7144     if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7145       image_matte=MagickFalse;
7146     if ((mng_info->optimize || mng_info->IsPalette) &&
7147         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
7148         ImageIsGray(image) && (!image_matte || image_depth >= 8))
7149       {
7150         size_t one=1;
7151         if (image_matte != MagickFalse)
7152           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
7153         else
7154           {
7155             ping_color_type=PNG_COLOR_TYPE_GRAY;
7156             if (save_image_depth == 16 && image_depth == 8)
7157               ping_trans_color.gray*=0x0101;
7158           }
7159         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
7160           image_depth=MAGICKCORE_QUANTUM_DEPTH;
7161         if (image_colors == 0 || image_colors-1 > MaxColormapSize)
7162           image_colors=one << image_depth;
7163         if (image_depth > 8)
7164           ping_bit_depth=16;
7165         else
7166           {
7167             ping_bit_depth=8;
7168             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
7169               {
7170                 if(!mng_info->write_png_depth)
7171                   {
7172                     ping_bit_depth=1;
7173                     while ((int) (one << ping_bit_depth)
7174                         < (ssize_t) image_colors)
7175                       ping_bit_depth <<= 1;
7176                   }
7177               }
7178             else if (mng_info->optimize && ping_color_type ==
7179                 PNG_COLOR_TYPE_GRAY && image_colors < 17 && 
7180                 mng_info->IsPalette)
7181               {
7182
7183               /* Check if grayscale is reducible */
7184                 int
7185                   depth_4_ok=MagickTrue,
7186                   depth_2_ok=MagickTrue,
7187                   depth_1_ok=MagickTrue;
7188
7189                 for (i=0; i < (ssize_t) image_colors; i++)
7190                 {
7191                    unsigned char
7192                      intensity;
7193
7194                    intensity=ScaleQuantumToChar(image->colormap[i].red);
7195
7196                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
7197                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
7198                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
7199                      depth_2_ok=depth_1_ok=MagickFalse;
7200                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
7201                      depth_1_ok=MagickFalse;
7202                 }
7203                 if (depth_1_ok && mng_info->write_png_depth <= 1)
7204                   ping_bit_depth=1;
7205                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
7206                   ping_bit_depth=2;
7207                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
7208                   ping_bit_depth=4;
7209               }
7210           }
7211           image_depth=ping_bit_depth;
7212       }
7213     else
7214       if (mng_info->IsPalette)
7215       {
7216         size_t
7217            number_colors;
7218
7219         number_colors=image_colors;
7220
7221         if (image_depth <= 8)
7222           {
7223             /*
7224               Set image palette.
7225             */
7226             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
7227             if (mng_info->have_write_global_plte && !matte)
7228               {
7229                 png_set_PLTE(ping,ping_info,NULL,0);
7230                 if (logging)
7231                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7232                     "  Setting up empty PLTE chunk");
7233               }
7234             else
7235               {
7236 #if defined(PNG_SORT_PALETTE)
7237                 size_t
7238                    save_number_colors;
7239
7240                 if (mng_info->optimize)
7241                   {
7242                     save_number_colors=image_colors;
7243                     if (CompressColormapTransFirst(image) == MagickFalse)
7244                        ThrowWriterException(ResourceLimitError,
7245                                             "MemoryAllocationFailed");
7246                     number_colors=image->colors;
7247                     image_colors=save_number_colors;
7248                   }
7249 #endif
7250                 palette=(png_color *) AcquireQuantumMemory(257,
7251                   sizeof(*palette));
7252                 if (palette == (png_color *) NULL)
7253                   ThrowWriterException(ResourceLimitError,
7254                      "MemoryAllocationFailed");
7255                 for (i=0; i < (ssize_t) number_colors; i++)
7256                 {
7257                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
7258                   palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
7259                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
7260                 }
7261                 if (logging)
7262                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7263                     "  Setting up PLTE chunk with %d colors",
7264                     (int) number_colors);
7265                 png_set_PLTE(ping,ping_info,palette,(int) number_colors);
7266                 palette=(png_colorp) RelinquishMagickMemory(palette);
7267               }
7268             /* color_type is PNG_COLOR_TYPE_PALETTE */
7269             if (!mng_info->write_png_depth)
7270               {
7271                 size_t
7272                   one;
7273
7274                 ping_bit_depth=1;
7275                 one=1;
7276                 while ((one << ping_bit_depth) < number_colors)
7277                   ping_bit_depth <<= 1;
7278               }
7279             ping_num_trans=0;
7280             if (matte)
7281             {
7282               ExceptionInfo
7283                 *exception;
7284
7285               register const PixelPacket
7286                 *p;
7287
7288               int
7289                 trans[256];
7290
7291               register const IndexPacket
7292                 *packet_indexes;
7293
7294               /*
7295                 Identify which colormap entry is transparent.
7296               */
7297               assert(number_colors <= 256);
7298               for (i=0; i < (ssize_t) number_colors; i++)
7299                 trans[i]=256;
7300               exception=(&image->exception);
7301               for (y=0; y < (ssize_t) image->rows; y++)
7302               {
7303                 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
7304                 if (p == (const PixelPacket *) NULL)
7305                   break;
7306                 packet_indexes=GetVirtualIndexQueue(image);
7307                 for (x=0; x < (ssize_t) image->columns; x++)
7308                 {
7309                   if (p->opacity != OpaqueOpacity)
7310                     {
7311                       IndexPacket
7312                         packet_index;
7313
7314                       packet_index=packet_indexes[x];
7315                       assert((size_t) packet_index < number_colors);
7316                       if (trans[(ssize_t) packet_index] != 256)
7317                         {
7318                           if (trans[(ssize_t) packet_index] != (png_byte) (255-
7319                              ScaleQuantumToChar(GetOpacityPixelComponent(p))))
7320                             {
7321                               ping_color_type=(png_byte)
7322                                 PNG_COLOR_TYPE_RGB_ALPHA;
7323                               break;
7324                             }
7325                         }
7326                       trans[(ssize_t) packet_index]=(png_byte) (255-
7327                         ScaleQuantumToChar(GetOpacityPixelComponent(p)));
7328                     }
7329                   p++;
7330                 }
7331                 if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
7332                 {
7333                   ping_num_trans=0;
7334                   png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7335                   png_set_invalid(ping,ping_info,PNG_INFO_PLTE);
7336                   mng_info->IsPalette=MagickFalse;
7337                   (void) SyncImage(image);
7338                   if (logging)
7339                     (void) LogMagickEvent(CoderEvent, GetMagickModule(),
7340                       "    Cannot write image as indexed PNG, writing RGBA.");
7341                   break;
7342                 }
7343               }
7344               if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7345               {
7346                 for (i=0; i < (ssize_t) number_colors; i++)
7347                 {
7348                   if (trans[i] == 256)
7349                     trans[i]=255;
7350                   if (trans[i] != 255)
7351                     ping_num_trans=(unsigned short) (i+1);
7352                 }
7353               }
7354               if (ping_num_trans == 0)
7355                 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7356               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
7357                 ping_num_trans=0;
7358               if (ping_num_trans != 0)
7359                 {
7360                   for (i=0; i < (ssize_t) number_colors; i++)
7361                       ping_trans_alpha[i]=(png_byte) trans[i];
7362                 }
7363             }
7364
7365           }
7366       }
7367     else
7368       {
7369         if (image_depth < 8)
7370           image_depth=8;
7371         if ((save_image_depth == 16) && (image_depth == 8))
7372           {
7373             ping_trans_color.red*=0x0101;
7374             ping_trans_color.green*=0x0101;
7375             ping_trans_color.blue*=0x0101;
7376             ping_trans_color.gray*=0x0101;
7377           }
7378       }
7379
7380     /*
7381       Adjust background and transparency samples in sub-8-bit grayscale files.
7382     */
7383     if (ping_bit_depth < 8 && ping_color_type ==
7384         PNG_COLOR_TYPE_GRAY)
7385       {
7386          png_uint_16
7387            maxval;
7388
7389          png_color_16
7390            background;
7391
7392          size_t
7393            one=1;
7394
7395          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
7396
7397
7398          background.gray=(png_uint_16)
7399            (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
7400
7401          if (logging != MagickFalse)
7402            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7403              "  Setting up bKGD chunk");
7404          png_set_bKGD(ping,ping_info,&background);
7405
7406          ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
7407            ping_trans_color.gray));
7408       }
7409
7410     if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
7411       {
7412         /*
7413            Identify which colormap entry is the background color.
7414         */
7415
7416         size_t
7417            number_colors;
7418
7419         number_colors=image_colors;
7420
7421         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
7422           if (IsPNGColorEqual(ping_background,image->colormap[i]))
7423             break;
7424
7425         ping_background.index=(png_byte) i;
7426
7427         if (logging)
7428           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7429             "  Setting up bKGD chunk with index=%d",(int) i);
7430
7431         png_set_bKGD(ping,ping_info,&ping_background);
7432       }
7433
7434   if (logging != MagickFalse)
7435     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7436       "    PNG color type: %d",ping_color_type);
7437   /*
7438     Initialize compression level and filtering.
7439   */
7440   if (logging != MagickFalse)
7441     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7442       "  Setting up deflate compression");
7443   if (logging != MagickFalse)
7444     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7445       "    Compression buffer size: 32768");
7446   png_set_compression_buffer_size(ping,32768L);
7447   if (logging != MagickFalse)
7448     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7449       "    Compression mem level: 9");
7450   png_set_compression_mem_level(ping, 9);
7451   quality=image->quality == UndefinedCompressionQuality ? 75UL :
7452      image->quality;
7453   if (quality > 9)
7454     {
7455       int
7456         level;
7457
7458       level=(int) MagickMin((ssize_t) quality/10,9);
7459       if (logging != MagickFalse)
7460         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7461           "    Compression level: %d",level);
7462       png_set_compression_level(ping,level);
7463     }
7464   else
7465     {
7466       if (logging != MagickFalse)
7467         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7468           "    Compression strategy: Z_HUFFMAN_ONLY");
7469       png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
7470     }
7471   if (logging != MagickFalse)
7472     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7473       "  Setting up filtering");
7474 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
7475
7476   /* This became available in libpng-1.0.9.  Output must be a MNG. */
7477   if (mng_info->write_mng && ((quality % 10) == 7))
7478     {
7479       if (logging != MagickFalse)
7480         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7481           "    Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
7482       ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
7483     }
7484   else
7485     if (logging != MagickFalse)
7486       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7487         "    Filter_type: 0");
7488 #endif
7489   {
7490     int
7491       base_filter;
7492
7493     if ((quality % 10) > 5)
7494       base_filter=PNG_ALL_FILTERS;
7495     else
7496       if ((quality % 10) != 5)
7497         base_filter=(int) quality % 10;
7498       else
7499         if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
7500             ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
7501             (quality < 50))
7502           base_filter=PNG_NO_FILTERS;
7503         else
7504           base_filter=PNG_ALL_FILTERS;
7505     if (logging != MagickFalse)
7506       {
7507         if (base_filter == PNG_ALL_FILTERS)
7508           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7509             "    Base filter method: ADAPTIVE");
7510         else
7511           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7512             "    Base filter method: NONE");
7513       }
7514     png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
7515   }
7516
7517   ResetImageProfileIterator(image);
7518   for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7519   {
7520     profile=GetImageProfile(image,name);
7521     if (profile != (StringInfo *) NULL)
7522       {
7523 #ifdef PNG_WRITE_iCCP_SUPPORTED
7524         if ((LocaleCompare(name,"ICC") == 0) ||
7525             (LocaleCompare(name,"ICM") == 0))
7526           png_set_iCCP(ping,ping_info,(const png_charp) name,0,(png_charp)
7527             GetStringInfoDatum(profile),
7528                      (png_uint_32) GetStringInfoLength(profile));
7529         else
7530 #endif
7531           png_write_raw_profile(image_info,ping,ping_info,(unsigned char *)
7532             name,(unsigned char *) name,GetStringInfoDatum(profile),
7533             (png_uint_32) GetStringInfoLength(profile));
7534       }
7535     if (logging != MagickFalse)
7536       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7537         "  Setting up text chunk with %s profile",name);
7538     name=GetNextImageProfile(image);
7539   }
7540
7541 #if defined(PNG_WRITE_sRGB_SUPPORTED)
7542   if ((mng_info->have_write_global_srgb == 0) &&
7543       ((image->rendering_intent != UndefinedIntent) ||
7544       (image->colorspace == sRGBColorspace)))
7545     {
7546       /*
7547         Note image rendering intent.
7548       */
7549       if (logging != MagickFalse)
7550         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7551             "  Setting up sRGB chunk");
7552       (void) png_set_sRGB(ping,ping_info,(
7553         PNG_RenderingIntent_from_Magick_RenderingIntent(
7554         image->rendering_intent)));
7555       png_set_gAMA(ping,ping_info,0.45455);
7556     }
7557   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
7558 #endif
7559     {
7560       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
7561         {
7562           /*
7563             Note image gamma.
7564             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7565           */
7566           if (logging != MagickFalse)
7567             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7568               "  Setting up gAMA chunk");
7569           png_set_gAMA(ping,ping_info,image->gamma);
7570         }
7571       if ((mng_info->have_write_global_chrm == 0) &&
7572           (image->chromaticity.red_primary.x != 0.0))
7573         {
7574           /*
7575             Note image chromaticity.
7576             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
7577           */
7578            PrimaryInfo
7579              bp,
7580              gp,
7581              rp,
7582              wp;
7583
7584            wp=image->chromaticity.white_point;
7585            rp=image->chromaticity.red_primary;
7586            gp=image->chromaticity.green_primary;
7587            bp=image->chromaticity.blue_primary;
7588
7589            if (logging != MagickFalse)
7590              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7591                "  Setting up cHRM chunk");
7592            png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
7593                bp.x,bp.y);
7594        }
7595     }
7596   ping_interlace_method=image_info->interlace != NoInterlace;
7597
7598   if (mng_info->write_mng)
7599     png_set_sig_bytes(ping,8);
7600
7601   /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
7602
7603   if (mng_info->write_png_colortype)
7604     {
7605      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
7606        if (ImageIsGray(image) == MagickFalse)
7607          {
7608            ping_color_type = PNG_COLOR_TYPE_RGB;
7609            if (ping_bit_depth < 8)
7610              ping_bit_depth=8;
7611          }
7612          
7613      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
7614        if (ImageIsGray(image) == MagickFalse)
7615          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
7616     }
7617
7618   if ((mng_info->write_png_depth &&
7619      (int) mng_info->write_png_depth != ping_bit_depth) ||
7620      (mng_info->write_png_colortype &&
7621      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
7622       mng_info->write_png_colortype != 7 &&
7623       !(mng_info->write_png_colortype == 5 && ping_color_type == 0))))
7624     {
7625       if (logging != MagickFalse)
7626         {
7627           if (mng_info->write_png_depth)
7628             {
7629               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7630                   "  Defined PNG:bit-depth=%u, Computed depth=%u",
7631                   mng_info->write_png_depth,
7632                   ping_bit_depth);
7633             }
7634           if (mng_info->write_png_colortype)
7635             {
7636               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7637                   "  Defined PNG:color-type=%u, Computed color type=%u",
7638                   mng_info->write_png_colortype-1,
7639                   ping_color_type);
7640             }
7641         }
7642       if (0) png_error(ping,
7643         "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
7644     }
7645
7646   if (image_matte && !image->matte)
7647     {
7648       /* Add an opaque matte channel */
7649       image->matte = MagickTrue;
7650       (void) SetImageOpacity(image,0);
7651       if (logging != MagickFalse)
7652         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7653           "  Added an opaque matte channel");
7654     }
7655
7656   if (image->matte == MagickTrue && ping_color_type < 4)
7657     {
7658       if (ping_color_type == 3 && ping_num_trans == 0)
7659         {
7660           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
7661           if (logging != MagickFalse)
7662             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7663               "  Ignoring request to write tRNS chunk with num_trans==0");
7664         }
7665       else
7666         (void) png_set_tRNS(ping, ping_info,
7667                             ping_trans_alpha,
7668                             ping_num_trans,
7669                             &ping_trans_color);
7670     }
7671
7672   if (logging != MagickFalse)
7673     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7674       "  Writing PNG header chunks");
7675
7676   png_set_IHDR(ping,ping_info,ping_width,ping_height,
7677                ping_bit_depth,ping_color_type,
7678                ping_interlace_method,ping_compression_method,
7679                ping_filter_method);
7680
7681   png_write_info_before_PLTE(ping, ping_info);
7682   /* write any png-chunk-b profiles */
7683   (void) png_write_chunk_from_profile(image,"PNG-chunk-b",(int) logging);
7684   png_write_info(ping,ping_info);
7685   /* write any PNG-chunk-m profiles */
7686   (void) png_write_chunk_from_profile(image,"PNG-chunk-m",(int) logging);
7687
7688   if (image->page.width || image->page.height)
7689     {
7690       unsigned char
7691         chunk[14];
7692
7693       (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
7694       PNGType(chunk,mng_vpAg);
7695       LogPNGChunk((int) logging,mng_vpAg,9L);
7696       PNGLong(chunk+4,(png_uint_32) image->page.width);
7697       PNGLong(chunk+8,(png_uint_32) image->page.height);
7698       chunk[12]=0;   /* unit = pixels */
7699       (void) WriteBlob(image,13,chunk);
7700       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
7701     }
7702
7703 #if (PNG_LIBPNG_VER == 10206)
7704     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
7705 #define PNG_HAVE_IDAT               0x04
7706     ping->mode |= PNG_HAVE_IDAT;
7707 #undef PNG_HAVE_IDAT
7708 #endif
7709
7710   png_set_packing(ping);
7711   /*
7712     Allocate memory.
7713   */
7714   rowbytes=image->columns;
7715   if (image_depth > 8)
7716     rowbytes*=2;
7717   switch (ping_color_type)
7718     {
7719       case PNG_COLOR_TYPE_RGB:
7720         rowbytes*=3;
7721         break;
7722       case PNG_COLOR_TYPE_GRAY_ALPHA:
7723         rowbytes*=2;
7724         break;
7725       case PNG_COLOR_TYPE_RGBA:
7726         rowbytes*=4;
7727         break;
7728       default:
7729         break;
7730     }
7731   if (logging)
7732     {
7733       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7734         "  Writing PNG image data");
7735       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7736         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
7737     }
7738   png_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
7739     sizeof(*png_pixels));
7740   if (png_pixels == (unsigned char *) NULL)
7741     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7742   /*
7743     Initialize image scanlines.
7744   */
7745   if (setjmp(png_jmpbuf(ping)))
7746     {
7747       /*
7748         PNG write failed.
7749       */
7750 #ifdef PNG_DEBUG
7751      if (image_info->verbose)
7752         (void) printf("PNG write has failed.\n");
7753 #endif
7754       png_destroy_write_struct(&ping,&ping_info);
7755       if (quantum_info != (QuantumInfo *) NULL)
7756         quantum_info=DestroyQuantumInfo(quantum_info);
7757       if (png_pixels != (unsigned char *) NULL)
7758         png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
7759 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
7760       UnlockSemaphoreInfo(png_semaphore);
7761 #endif
7762       return(MagickFalse);
7763     }
7764   quantum_info=AcquireQuantumInfo(image_info,image);
7765   if (quantum_info == (QuantumInfo *) NULL)
7766     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
7767   quantum_info->format=UndefinedQuantumFormat;
7768   quantum_info->depth=image_depth;
7769   num_passes=png_set_interlace_handling(ping);
7770   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
7771                       !mng_info->write_png32) &&
7772       (mng_info->optimize || mng_info->IsPalette ||
7773        (image_info->type == BilevelType)) &&
7774       !image_matte && ImageIsMonochrome(image))
7775     {
7776       register const PixelPacket
7777         *p;
7778
7779       quantum_info->depth=8;
7780       for (pass=0; pass < num_passes; pass++)
7781       {
7782         /*
7783           Convert PseudoClass image to a PNG monochrome image.
7784         */
7785         for (y=0; y < (ssize_t) image->rows; y++)
7786         {
7787           p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7788           if (p == (const PixelPacket *) NULL)
7789             break;
7790           if (mng_info->IsPalette)
7791             {
7792               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7793                 quantum_info,GrayQuantum,png_pixels,&image->exception);
7794               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
7795                   mng_info->write_png_depth &&
7796                   mng_info->write_png_depth != old_bit_depth)
7797                 {
7798                   /* Undo pixel scaling */
7799                   for (i=0; i < (ssize_t) image->columns; i++)
7800                      *(png_pixels+i)=(unsigned char) (*(png_pixels+i)
7801                      >> (8-old_bit_depth));
7802                 }
7803             }
7804           else
7805             {
7806               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7807                 quantum_info,RedQuantum,png_pixels,&image->exception);
7808             }
7809           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
7810             for (i=0; i < (ssize_t) image->columns; i++)
7811                *(png_pixels+i)=(unsigned char) ((*(png_pixels+i) > 127) ?
7812                       255 : 0);
7813           if (logging && y == 0)
7814             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7815                 "    Writing row of pixels (1)");
7816           png_write_row(ping,png_pixels);
7817         }
7818         if (image->previous == (Image *) NULL)
7819           {
7820             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7821             if (status == MagickFalse)
7822               break;
7823           }
7824       }
7825     }
7826   else
7827     for (pass=0; pass < num_passes; pass++)
7828     {
7829       register const PixelPacket
7830         *p;
7831
7832       if ((!mng_info->write_png8 && !mng_info->write_png24 && 
7833          !mng_info->write_png32) &&
7834          (image_matte ||
7835          (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
7836          (mng_info->optimize || mng_info->IsPalette) && ImageIsGray(image))
7837       {
7838         for (y=0; y < (ssize_t) image->rows; y++)
7839         {
7840           p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7841           if (p == (const PixelPacket *) NULL)
7842             break;
7843           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7844             {
7845               if (mng_info->IsPalette)
7846                 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7847                   quantum_info,GrayQuantum,png_pixels,&image->exception);
7848               else
7849                 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7850                   quantum_info,RedQuantum,png_pixels,&image->exception);
7851               if (logging && y == 0)
7852                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7853                      "    Writing GRAY PNG pixels (2)");
7854             }
7855           else /* PNG_COLOR_TYPE_GRAY_ALPHA */
7856             {
7857               if (logging && y == 0)
7858                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7859                        "    Writing GRAY_ALPHA PNG pixels (2)");
7860               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7861                 quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7862             }
7863           if (logging && y == 0)
7864             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7865                 "    Writing row of pixels (2)");
7866           png_write_row(ping,png_pixels);
7867         }
7868         if (image->previous == (Image *) NULL)
7869           {
7870             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7871             if (status == MagickFalse)
7872               break;
7873           }
7874       }
7875     else
7876       for (pass=0; pass < num_passes; pass++)
7877       {
7878         if ((image_depth > 8) || (mng_info->write_png24 ||
7879             mng_info->write_png32 ||
7880             (!mng_info->write_png8 && !mng_info->IsPalette)))
7881           for (y=0; y < (ssize_t) image->rows; y++)
7882           {
7883             p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7884             if (p == (const PixelPacket *) NULL)
7885               break;
7886             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7887               {
7888                 if (image->storage_class == DirectClass)
7889                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7890                     quantum_info,RedQuantum,png_pixels,&image->exception);
7891                 else
7892                   (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7893                     quantum_info,GrayQuantum,png_pixels,&image->exception);
7894               }
7895             else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7896               {
7897                 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7898                   quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7899                 if (logging && y == 0)
7900                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7901                        "    Writing GRAY_ALPHA PNG pixels (3)");
7902               }
7903             else if (image_matte != MagickFalse)
7904               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7905                 quantum_info,RGBAQuantum,png_pixels,&image->exception);
7906             else
7907               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7908                 quantum_info,RGBQuantum,png_pixels,&image->exception);
7909             if (logging && y == 0)
7910               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7911                   "    Writing row of pixels (3)");
7912             png_write_row(ping,png_pixels);
7913           }
7914       else
7915         /* not ((image_depth > 8) || (mng_info->write_png24 ||
7916             mng_info->write_png32 ||
7917             (!mng_info->write_png8 && !mng_info->IsPalette))) */
7918         {
7919           if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
7920               (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
7921             {
7922               if (logging)
7923                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7924                   "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
7925               quantum_info->depth=8;
7926               image_depth=8;
7927             }
7928           for (y=0; y < (ssize_t) image->rows; y++)
7929           {
7930             if (logging && y == 0)
7931               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7932                 "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
7933             p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
7934             if (p == (const PixelPacket *) NULL)
7935               break;
7936             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
7937               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7938                 quantum_info,GrayQuantum,png_pixels,&image->exception);
7939             else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
7940               {
7941                 if (logging && y == 0)
7942                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7943                        "  Writing GRAY_ALPHA PNG pixels (4)");
7944                 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7945                   quantum_info,GrayAlphaQuantum,png_pixels,&image->exception);
7946               }
7947             else
7948               (void) ExportQuantumPixels(image,(const CacheView *) NULL,
7949                 quantum_info,IndexQuantum,png_pixels,&image->exception);
7950             if (logging && y == 0)
7951               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7952                   "  Writing row of pixels (4)");
7953             png_write_row(ping,png_pixels);
7954           }
7955         }
7956         if (image->previous == (Image *) NULL)
7957           {
7958             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
7959             if (status == MagickFalse)
7960               break;
7961           }
7962      }
7963   }
7964   if (quantum_info != (QuantumInfo *) NULL)
7965     quantum_info=DestroyQuantumInfo(quantum_info);
7966
7967   if (logging != MagickFalse)
7968     {
7969       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7970         "  Wrote PNG image data");
7971       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7972         "    Width: %.20g",(double) ping_width);
7973       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7974         "    Height: %.20g",(double) ping_height);
7975       if (mng_info->write_png_depth)
7976         {
7977           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7978             "    Defined PNG:bit-depth: %d",mng_info->write_png_depth);
7979         }
7980       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7981         "    PNG bit-depth written: %d",ping_bit_depth);
7982       if (mng_info->write_png_colortype)
7983         {
7984           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7985             "    Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
7986         }
7987       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7988         "    PNG color-type written: %d",ping_color_type);
7989       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7990         "    PNG Interlace method: %d",ping_interlace_method);
7991     }
7992   /*
7993     Generate text chunks.
7994   */
7995   ResetImagePropertyIterator(image);
7996   property=GetNextImageProperty(image);
7997   while (property != (const char *) NULL)
7998   {
7999     png_textp
8000       text;
8001
8002     value=GetImageProperty(image,property);
8003     if (value != (const char *) NULL)
8004       {
8005         text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
8006         text[0].key=(char *) property;
8007         text[0].text=(char *) value;
8008         text[0].text_length=strlen(value);
8009         text[0].compression=image_info->compression == NoCompression ||
8010           (image_info->compression == UndefinedCompression &&
8011           text[0].text_length < 128) ? -1 : 0;
8012         if (logging != MagickFalse)
8013           {
8014             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8015               "  Setting up text chunk");
8016             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8017               "    keyword: %s",text[0].key);
8018           }
8019         png_set_text(ping,ping_info,text,1);
8020         png_free(ping,text);
8021       }
8022     property=GetNextImageProperty(image);
8023   }
8024
8025   /* write any PNG-chunk-e profiles */
8026   (void) png_write_chunk_from_profile(image,"PNG-chunk-e",(int) logging);
8027
8028   if (logging != MagickFalse)
8029     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8030       "  Writing PNG end info");
8031   png_write_end(ping,ping_info);
8032   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
8033     {
8034       if (mng_info->page.x || mng_info->page.y ||
8035           (ping_width != mng_info->page.width) ||
8036           (ping_height != mng_info->page.height))
8037         {
8038           unsigned char
8039             chunk[32];
8040
8041           /*
8042             Write FRAM 4 with clipping boundaries followed by FRAM 1.
8043           */
8044           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
8045           PNGType(chunk,mng_FRAM);
8046           LogPNGChunk((int) logging,mng_FRAM,27L);
8047           chunk[4]=4;
8048           chunk[5]=0;  /* frame name separator (no name) */
8049           chunk[6]=1;  /* flag for changing delay, for next frame only */
8050           chunk[7]=0;  /* flag for changing frame timeout */
8051           chunk[8]=1;  /* flag for changing frame clipping for next frame */
8052           chunk[9]=0;  /* flag for changing frame sync_id */
8053           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
8054           chunk[14]=0; /* clipping boundaries delta type */
8055           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
8056           PNGLong(chunk+19,
8057              (png_uint_32) (mng_info->page.x + ping_width));
8058           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
8059           PNGLong(chunk+27,
8060              (png_uint_32) (mng_info->page.y + ping_height));
8061           (void) WriteBlob(image,31,chunk);
8062           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
8063           mng_info->old_framing_mode=4;
8064           mng_info->framing_mode=1;
8065         }
8066       else
8067         mng_info->framing_mode=3;
8068     }
8069   if (mng_info->write_mng && !mng_info->need_fram &&
8070       ((int) image->dispose == 3))
8071      (void) ThrowMagickException(&image->exception,GetMagickModule(),
8072        CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
8073        "`%s'",image->filename);
8074   image_depth=save_image_depth;
8075
8076   /* Save depth actually written */
8077
8078   s[0]=(char) ping_bit_depth;
8079   s[1]='\0';
8080
8081   (void) SetImageProperty(image,"png:bit-depth-written",s);
8082
8083   /*
8084     Free PNG resources.
8085   */
8086
8087   png_destroy_write_struct(&ping,&ping_info);
8088
8089   png_pixels=(unsigned char *) RelinquishMagickMemory(png_pixels);
8090
8091 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8092   UnlockSemaphoreInfo(png_semaphore);
8093 #endif
8094
8095   if (logging != MagickFalse)
8096     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8097       "  exit WriteOnePNGImage()");
8098   return(MagickTrue);
8099 /*  End write one PNG image */
8100 }
8101
8102 /*
8103 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8104 %                                                                             %
8105 %                                                                             %
8106 %                                                                             %
8107 %   W r i t e P N G I m a g e                                                 %
8108 %                                                                             %
8109 %                                                                             %
8110 %                                                                             %
8111 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8112 %
8113 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
8114 %  Multiple-image Network Graphics (MNG) image file.
8115 %
8116 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
8117 %
8118 %  The format of the WritePNGImage method is:
8119 %
8120 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
8121 %
8122 %  A description of each parameter follows:
8123 %
8124 %    o image_info: the image info.
8125 %
8126 %    o image:  The image.
8127 %
8128 %  Returns MagickTrue on success, MagickFalse on failure.
8129 %
8130 %  Communicating with the PNG encoder:
8131 %
8132 %  While the datastream written is always in PNG format and normally would
8133 %  be given the "png" file extension, this method also writes the following
8134 %  pseudo-formats which are subsets of PNG:
8135 %
8136 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If transparency
8137 %               is present, the tRNS chunk must only have values 0 and 255
8138 %               (i.e., transparency is binary: fully opaque or fully
8139 %               transparent).  The pixels contain 8-bit indices even if
8140 %               they could be represented with 1, 2, or 4 bits. Note: grayscale
8141 %               images will be written as indexed PNG files even though the
8142 %               PNG grayscale type might be slightly more efficient.
8143 %
8144 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
8145 %               chunk can be present to convey binary transparency by naming
8146 %               one of the colors as transparent.
8147 %
8148 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
8149 %               transparency is permitted, i.e., the alpha sample for
8150 %               each pixel can have any value from 0 to 255. The alpha
8151 %               channel is present even if the image is fully opaque. 
8152 %
8153 %    o -define: For more precise control of the PNG output, you can use the
8154 %               Image options "png:bit-depth" and "png:color-type".  These
8155 %               can be set from the commandline with "-define" and also
8156 %               from the application programming interfaces.
8157 %
8158 %               png:color-type can be 0, 2, 3, 4, or 6.
8159 %
8160 %               When png:color-type is 0 (Grayscale), png:bit-depth can
8161 %               be 1, 2, 4, 8, or 16.
8162 %
8163 %               When png:color-type is 2 (RGB), png:bit-depth can
8164 %               be 8 or 16.
8165 %
8166 %               When png:color-type is 3 (Indexed), png:bit-depth can
8167 %               be 1, 2, 4, or 8.  This refers to the number of bits
8168 %               used to store the index.  The color samples always have
8169 %               bit-depth 8 in indexed PNG files.
8170 %
8171 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
8172 %               png:bit-depth can be 8 or 16.
8173 %
8174 %  If the image cannot be written without loss in the requested PNG8, PNG24,
8175 %  or PNG32 format or with the requested bit-depth and color-type without loss,
8176 %  a PNG file will not be written, and the encoder will return MagickFalse.
8177 %  Since image encoders should not be responsible for the "heavy lifting",
8178 %  the user should make sure that ImageMagick has already reduced the
8179 %  image depth and number of colors and limit transparency to binary
8180 %  transparency prior to attempting to write the image in a format that
8181 %  is subject to depth, color, or transparency limitations.
8182 %
8183 %  TODO: Enforce the previous paragraph.
8184 %
8185 %  TODO: Allow all other PNG subformats to be requested via new
8186 %        "-define png:bit-depth -define png:color-type" options.
8187 %
8188 %  Note that another definition, "png:bit-depth-written" exists, but it
8189 %  is not intended for external use.  It is only used internally by the
8190 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
8191 %
8192 %  It is possible to request that the PNG encoder write previously-formatted
8193 %  ancillary chunks in the output PNG file, using the "-profile" commandline
8194 %  option as shown below or by setting the profile via a programming
8195 %  interface:
8196 %
8197 %     -profile PNG-chunk-x:<file>
8198 %
8199 %  where x is a location flag and <file> is a file containing the chunk
8200 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
8201 %
8202 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
8203 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
8204 %  of the same type, then add a short unique string after the "x" to prevent
8205 %  subsequent profiles from overwriting the preceding ones:
8206 %
8207 %     -profile PNG-chunk-x01:file01 -profile PNG-chunk-x02:file02
8208 %
8209 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8210 */
8211 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
8212   Image *image)
8213 {
8214   MagickBooleanType
8215     status;
8216
8217   MngInfo
8218     *mng_info;
8219
8220   const char
8221     *value;
8222
8223   int
8224     have_mng_structure;
8225
8226   unsigned int
8227     logging;
8228
8229   /*
8230     Open image file.
8231   */
8232   assert(image_info != (const ImageInfo *) NULL);
8233   assert(image_info->signature == MagickSignature);
8234   assert(image != (Image *) NULL);
8235   assert(image->signature == MagickSignature);
8236   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8237   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WritePNGImage()");
8238   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8239   if (status == MagickFalse)
8240     return(MagickFalse);
8241   /*
8242     Allocate a MngInfo structure.
8243   */
8244   have_mng_structure=MagickFalse;
8245   mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
8246   if (mng_info == (MngInfo *) NULL)
8247     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8248   /*
8249     Initialize members of the MngInfo structure.
8250   */
8251   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8252   mng_info->image=image;
8253   have_mng_structure=MagickTrue;
8254
8255   /* See if user has requested a specific PNG subformat */
8256
8257   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
8258   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
8259   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
8260
8261   if (mng_info->write_png8)
8262     {
8263       mng_info->write_png_colortype = /* 3 */ 4;
8264       mng_info->write_png_depth = 8;
8265       image->depth = 8;
8266 #if 0 /* this does not work */
8267       if (image->matte == MagickTrue)
8268         (void) SetImageType(image,PaletteMatteType);
8269       else
8270         (void) SetImageType(image,PaletteType);
8271       (void) SyncImage(image);
8272 #endif
8273     }
8274
8275   if (mng_info->write_png24)
8276     {
8277       mng_info->write_png_colortype = /* 2 */ 3;
8278       mng_info->write_png_depth = 8;
8279       image->depth = 8;
8280       if (image->matte == MagickTrue)
8281         (void) SetImageType(image,TrueColorMatteType);
8282       else
8283         (void) SetImageType(image,TrueColorType);
8284       (void) SyncImage(image);
8285     }
8286
8287   if (mng_info->write_png32)
8288     {
8289       mng_info->write_png_colortype = /* 6 */  7;
8290       mng_info->write_png_depth = 8;
8291       image->depth = 8;
8292       if (image->matte == MagickTrue)
8293         (void) SetImageType(image,TrueColorMatteType);
8294       else
8295         (void) SetImageType(image,TrueColorType);
8296       (void) SyncImage(image);
8297     }
8298
8299   value=GetImageOption(image_info,"png:bit-depth");
8300   if (value != (char *) NULL)
8301     {
8302       if (LocaleCompare(value,"1") == 0)
8303         mng_info->write_png_depth = 1;
8304       else if (LocaleCompare(value,"2") == 0)
8305         mng_info->write_png_depth = 2;
8306       else if (LocaleCompare(value,"4") == 0)
8307         mng_info->write_png_depth = 4;
8308       else if (LocaleCompare(value,"8") == 0)
8309         mng_info->write_png_depth = 8;
8310       else if (LocaleCompare(value,"16") == 0)
8311         mng_info->write_png_depth = 16;
8312       if (logging != MagickFalse)
8313         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8314           "png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
8315     }
8316   value=GetImageOption(image_info,"png:color-type");
8317   if (value != (char *) NULL)
8318     {
8319       /* We must store colortype+1 because 0 is a valid colortype */
8320       if (LocaleCompare(value,"0") == 0)
8321         mng_info->write_png_colortype = 1;
8322       else if (LocaleCompare(value,"2") == 0)
8323         mng_info->write_png_colortype = 3;
8324       else if (LocaleCompare(value,"3") == 0)
8325         mng_info->write_png_colortype = 4;
8326       else if (LocaleCompare(value,"4") == 0)
8327         mng_info->write_png_colortype = 5;
8328       else if (LocaleCompare(value,"6") == 0)
8329         mng_info->write_png_colortype = 7;
8330       if (logging != MagickFalse)
8331         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8332           "png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
8333     }
8334
8335   status=WriteOnePNGImage(mng_info,image_info,image);
8336
8337   (void) CloseBlob(image);
8338
8339   MngInfoFreeStruct(mng_info,&have_mng_structure);
8340   if (logging != MagickFalse)
8341     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
8342   return(status);
8343 }
8344
8345 #if defined(JNG_SUPPORTED)
8346
8347 /* Write one JNG image */
8348 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
8349    const ImageInfo *image_info,Image *image)
8350 {
8351   Image
8352     *jpeg_image;
8353
8354   ImageInfo
8355     *jpeg_image_info;
8356
8357   MagickBooleanType
8358     status;
8359
8360   size_t
8361     length;
8362
8363   unsigned char
8364     *blob,
8365     chunk[80],
8366     *p;
8367
8368   unsigned int
8369     jng_alpha_compression_method,
8370     jng_alpha_sample_depth,
8371     jng_color_type,
8372     logging,
8373     transparent;
8374
8375   size_t
8376     jng_quality;
8377
8378   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8379     "  enter WriteOneJNGImage()");
8380
8381   blob=(unsigned char *) NULL;
8382   jpeg_image=(Image *) NULL;
8383   jpeg_image_info=(ImageInfo *) NULL;
8384
8385   status=MagickTrue;
8386   transparent=image_info->type==GrayscaleMatteType ||
8387      image_info->type==TrueColorMatteType;
8388   jng_color_type=10;
8389   jng_alpha_sample_depth=0;
8390   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
8391   jng_alpha_compression_method=0;
8392
8393   if (image->matte != MagickFalse)
8394     {
8395       /* if any pixels are transparent */
8396       transparent=MagickTrue;
8397       if (image_info->compression==JPEGCompression)
8398         jng_alpha_compression_method=8;
8399     }
8400
8401   if (transparent)
8402     {
8403       jng_color_type=14;
8404       /* Create JPEG blob, image, and image_info */
8405       if (logging != MagickFalse)
8406         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8407           "  Creating jpeg_image_info for opacity.");
8408       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8409       if (jpeg_image_info == (ImageInfo *) NULL)
8410         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8411       if (logging != MagickFalse)
8412         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8413           "  Creating jpeg_image.");
8414       jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8415       if (jpeg_image == (Image *) NULL)
8416         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8417       (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8418       status=SeparateImageChannel(jpeg_image,OpacityChannel);
8419       status=NegateImage(jpeg_image,MagickFalse);
8420       jpeg_image->matte=MagickFalse;
8421       if (jng_quality >= 1000)
8422         jpeg_image_info->quality=jng_quality/1000;
8423       else
8424         jpeg_image_info->quality=jng_quality;
8425       jpeg_image_info->type=GrayscaleType;
8426       (void) SetImageType(jpeg_image,GrayscaleType);
8427       (void) AcquireUniqueFilename(jpeg_image->filename);
8428       (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,
8429         "%s",jpeg_image->filename);
8430     }
8431
8432   /* To do: check bit depth of PNG alpha channel */
8433
8434   /* Check if image is grayscale. */
8435   if (image_info->type != TrueColorMatteType && image_info->type !=
8436     TrueColorType && ImageIsGray(image))
8437     jng_color_type-=2;
8438
8439   if (transparent)
8440     {
8441       if (jng_alpha_compression_method==0)
8442         {
8443           const char
8444             *value;
8445
8446           /* Encode opacity as a grayscale PNG blob */
8447           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8448             &image->exception);
8449           if (logging != MagickFalse)
8450             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8451               "  Creating PNG blob.");
8452           length=0;
8453
8454           (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
8455           (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
8456           jpeg_image_info->interlace=NoInterlace;
8457
8458           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8459             &image->exception);
8460
8461           /* Retrieve sample depth used */
8462           value=GetImageProperty(jpeg_image,"png:bit-depth-written");
8463           if (value != (char *) NULL)
8464             jng_alpha_sample_depth= (unsigned int) value[0];
8465         }
8466       else
8467         {
8468           /* Encode opacity as a grayscale JPEG blob */
8469
8470           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8471             &image->exception);
8472
8473           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8474           (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8475           jpeg_image_info->interlace=NoInterlace;
8476           if (logging != MagickFalse)
8477             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8478               "  Creating blob.");
8479           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
8480            &image->exception);
8481           jng_alpha_sample_depth=8;
8482           if (logging != MagickFalse)
8483             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8484               "  Successfully read jpeg_image into a blob, length=%.20g.",
8485               (double) length);
8486
8487         }
8488       /* Destroy JPEG image and image_info */
8489       jpeg_image=DestroyImage(jpeg_image);
8490       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8491       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8492     }
8493
8494   /* Write JHDR chunk */
8495   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
8496   PNGType(chunk,mng_JHDR);
8497   LogPNGChunk((int) logging,mng_JHDR,16L);
8498   PNGLong(chunk+4,(png_uint_32) image->columns);
8499   PNGLong(chunk+8,(png_uint_32) image->rows);
8500   chunk[12]=jng_color_type;
8501   chunk[13]=8;  /* sample depth */
8502   chunk[14]=8; /*jng_image_compression_method */
8503   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
8504   chunk[16]=jng_alpha_sample_depth;
8505   chunk[17]=jng_alpha_compression_method;
8506   chunk[18]=0; /*jng_alpha_filter_method */
8507   chunk[19]=0; /*jng_alpha_interlace_method */
8508   (void) WriteBlob(image,20,chunk);
8509   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
8510   if (logging != MagickFalse)
8511     {
8512       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8513         "    JNG width:%15lu",(unsigned long) image->columns);
8514       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8515         "    JNG height:%14lu",(unsigned long) image->rows);
8516       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8517         "    JNG color type:%10d",jng_color_type);
8518       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8519         "    JNG sample depth:%8d",8);
8520       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8521         "    JNG compression:%9d",8);
8522       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8523         "    JNG interlace:%11d",0);
8524       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8525         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
8526       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8527         "    JNG alpha compression:%3d",jng_alpha_compression_method);
8528       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8529         "    JNG alpha filter:%8d",0);
8530       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8531         "    JNG alpha interlace:%5d",0);
8532     }
8533
8534   /* Write any JNG-chunk-b profiles */ 
8535   (void) png_write_chunk_from_profile(image,"JNG-chunk-b",(int) logging);
8536
8537   /*
8538      Write leading ancillary chunks
8539   */
8540
8541   if (transparent)
8542   {
8543     /*
8544       Write JNG bKGD chunk
8545     */
8546
8547     unsigned char
8548       blue,
8549       green,
8550       red;
8551
8552     ssize_t
8553       num_bytes;
8554
8555     if (jng_color_type == 8 || jng_color_type == 12)
8556       num_bytes=6L;
8557     else
8558       num_bytes=10L;
8559     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
8560     PNGType(chunk,mng_bKGD);
8561     LogPNGChunk((int) logging,mng_bKGD,(size_t) (num_bytes-4L));
8562     red=ScaleQuantumToChar(image->background_color.red);
8563     green=ScaleQuantumToChar(image->background_color.green);
8564     blue=ScaleQuantumToChar(image->background_color.blue);
8565     *(chunk+4)=0;
8566     *(chunk+5)=red;
8567     *(chunk+6)=0;
8568     *(chunk+7)=green;
8569     *(chunk+8)=0;
8570     *(chunk+9)=blue;
8571     (void) WriteBlob(image,(size_t) num_bytes,chunk);
8572     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
8573   }
8574
8575   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
8576     {
8577       /*
8578         Write JNG sRGB chunk
8579       */
8580       (void) WriteBlobMSBULong(image,1L);
8581       PNGType(chunk,mng_sRGB);
8582       LogPNGChunk((int) logging,mng_sRGB,1L);
8583       if (image->rendering_intent != UndefinedIntent)
8584         chunk[4]=(unsigned char)
8585           PNG_RenderingIntent_from_Magick_RenderingIntent(
8586           (image->rendering_intent));
8587       else
8588         chunk[4]=(unsigned char)
8589           PNG_RenderingIntent_from_Magick_RenderingIntent(
8590           (PerceptualIntent));
8591       (void) WriteBlob(image,5,chunk);
8592       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
8593     }
8594   else
8595     {
8596       if (image->gamma != 0.0)
8597         {
8598           /*
8599              Write JNG gAMA chunk
8600           */
8601           (void) WriteBlobMSBULong(image,4L);
8602           PNGType(chunk,mng_gAMA);
8603           LogPNGChunk((int) logging,mng_gAMA,4L);
8604           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
8605           (void) WriteBlob(image,8,chunk);
8606           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
8607         }
8608       if ((mng_info->equal_chrms == MagickFalse) &&
8609           (image->chromaticity.red_primary.x != 0.0))
8610         {
8611           PrimaryInfo
8612             primary;
8613
8614           /*
8615              Write JNG cHRM chunk
8616           */
8617           (void) WriteBlobMSBULong(image,32L);
8618           PNGType(chunk,mng_cHRM);
8619           LogPNGChunk((int) logging,mng_cHRM,32L);
8620           primary=image->chromaticity.white_point;
8621           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
8622           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
8623           primary=image->chromaticity.red_primary;
8624           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
8625           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
8626           primary=image->chromaticity.green_primary;
8627           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
8628           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
8629           primary=image->chromaticity.blue_primary;
8630           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
8631           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
8632           (void) WriteBlob(image,36,chunk);
8633           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
8634         }
8635     }
8636   if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
8637     {
8638       /*
8639          Write JNG pHYs chunk
8640       */
8641       (void) WriteBlobMSBULong(image,9L);
8642       PNGType(chunk,mng_pHYs);
8643       LogPNGChunk((int) logging,mng_pHYs,9L);
8644       if (image->units == PixelsPerInchResolution)
8645         {
8646           PNGLong(chunk+4,(png_uint_32)
8647             (image->x_resolution*100.0/2.54+0.5));
8648           PNGLong(chunk+8,(png_uint_32)
8649             (image->y_resolution*100.0/2.54+0.5));
8650           chunk[12]=1;
8651         }
8652       else
8653         {
8654           if (image->units == PixelsPerCentimeterResolution)
8655             {
8656               PNGLong(chunk+4,(png_uint_32)
8657                 (image->x_resolution*100.0+0.5));
8658               PNGLong(chunk+8,(png_uint_32)
8659                 (image->y_resolution*100.0+0.5));
8660               chunk[12]=1;
8661             }
8662           else
8663             {
8664               PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
8665               PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
8666               chunk[12]=0;
8667             }
8668         }
8669       (void) WriteBlob(image,13,chunk);
8670       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8671     }
8672
8673   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
8674     {
8675       /*
8676          Write JNG oFFs chunk
8677       */
8678       (void) WriteBlobMSBULong(image,9L);
8679       PNGType(chunk,mng_oFFs);
8680       LogPNGChunk((int) logging,mng_oFFs,9L);
8681       PNGsLong(chunk+4,(ssize_t) (image->page.x));
8682       PNGsLong(chunk+8,(ssize_t) (image->page.y));
8683       chunk[12]=0;
8684       (void) WriteBlob(image,13,chunk);
8685       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8686     }
8687   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
8688     {
8689        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
8690        PNGType(chunk,mng_vpAg);
8691        LogPNGChunk((int) logging,mng_vpAg,9L);
8692        PNGLong(chunk+4,(png_uint_32) image->page.width);
8693        PNGLong(chunk+8,(png_uint_32) image->page.height);
8694        chunk[12]=0;   /* unit = pixels */
8695        (void) WriteBlob(image,13,chunk);
8696        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
8697     }
8698
8699
8700   if (transparent)
8701     {
8702       if (jng_alpha_compression_method==0)
8703         {
8704           register ssize_t
8705             i;
8706
8707           ssize_t
8708             len;
8709
8710           /* Write IDAT chunk header */
8711           if (logging != MagickFalse)
8712             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8713               "  Write IDAT chunks from blob, length=%.20g.",(double)
8714               length);
8715
8716           /* Copy IDAT chunks */
8717           len=0;
8718           p=blob+8;
8719           for (i=8; i<(ssize_t) length; i+=len+12)
8720           {
8721             len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
8722             p+=4;
8723             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
8724               {
8725                 /* Found an IDAT chunk. */
8726                 (void) WriteBlobMSBULong(image,(size_t) len);
8727                 LogPNGChunk((int) logging,mng_IDAT,(size_t) len);
8728                 (void) WriteBlob(image,(size_t) len+4,p);
8729                 (void) WriteBlobMSBULong(image,
8730                     crc32(0,p,(uInt) len+4));
8731               }
8732             else
8733               {
8734                 if (logging != MagickFalse)
8735                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8736                     "    Skipping %c%c%c%c chunk, length=%.20g.",
8737                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
8738               }
8739             p+=(8+len);
8740           }
8741         }
8742       else
8743         {
8744           /* Write JDAA chunk header */
8745           if (logging != MagickFalse)
8746             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8747               "  Write JDAA chunk, length=%.20g.",(double) length);
8748           (void) WriteBlobMSBULong(image,(size_t) length);
8749           PNGType(chunk,mng_JDAA);
8750           LogPNGChunk((int) logging,mng_JDAA,length);
8751           /* Write JDAT chunk(s) data */
8752           (void) WriteBlob(image,4,chunk);
8753           (void) WriteBlob(image,length,blob);
8754           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
8755              (uInt) length));
8756         }
8757       blob=(unsigned char *) RelinquishMagickMemory(blob);
8758     }
8759
8760   /* Encode image as a JPEG blob */
8761   if (logging != MagickFalse)
8762     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8763       "  Creating jpeg_image_info.");
8764   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
8765   if (jpeg_image_info == (ImageInfo *) NULL)
8766     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8767
8768   if (logging != MagickFalse)
8769     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8770       "  Creating jpeg_image.");
8771
8772   jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
8773   if (jpeg_image == (Image *) NULL)
8774     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8775   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8776
8777   (void) AcquireUniqueFilename(jpeg_image->filename);
8778   (void) FormatMagickString(jpeg_image_info->filename,MaxTextExtent,"%s",
8779     jpeg_image->filename);
8780
8781   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
8782     &image->exception);
8783
8784   if (logging != MagickFalse)
8785     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8786       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
8787       (double) jpeg_image->rows);
8788
8789   if (jng_color_type == 8 || jng_color_type == 12)
8790     jpeg_image_info->type=GrayscaleType;
8791   jpeg_image_info->quality=jng_quality % 1000;
8792   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
8793   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
8794   if (logging != MagickFalse)
8795     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8796       "  Creating blob.");
8797   blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
8798   if (logging != MagickFalse)
8799     {
8800       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8801         "  Successfully read jpeg_image into a blob, length=%.20g.",
8802         (double) length);
8803
8804       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8805         "  Write JDAT chunk, length=%.20g.",(double) length);
8806     }
8807   /* Write JDAT chunk(s) */
8808   (void) WriteBlobMSBULong(image,(size_t) length);
8809   PNGType(chunk,mng_JDAT);
8810   LogPNGChunk((int) logging,mng_JDAT,length);
8811   (void) WriteBlob(image,4,chunk);
8812   (void) WriteBlob(image,length,blob);
8813   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
8814
8815   jpeg_image=DestroyImage(jpeg_image);
8816   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
8817   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
8818   blob=(unsigned char *) RelinquishMagickMemory(blob);
8819
8820   /* Write any JNG-chunk-e profiles */
8821   (void) png_write_chunk_from_profile(image,"JNG-chunk-e",(int) logging);
8822
8823   /* Write IEND chunk */
8824   (void) WriteBlobMSBULong(image,0L);
8825   PNGType(chunk,mng_IEND);
8826   LogPNGChunk((int) logging,mng_IEND,0);
8827   (void) WriteBlob(image,4,chunk);
8828   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
8829
8830   if (logging != MagickFalse)
8831     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8832       "  exit WriteOneJNGImage()");
8833   return(status);
8834 }
8835
8836
8837 /*
8838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8839 %                                                                             %
8840 %                                                                             %
8841 %                                                                             %
8842 %   W r i t e J N G I m a g e                                                 %
8843 %                                                                             %
8844 %                                                                             %
8845 %                                                                             %
8846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8847 %
8848 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
8849 %
8850 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
8851 %
8852 %  The format of the WriteJNGImage method is:
8853 %
8854 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8855 %
8856 %  A description of each parameter follows:
8857 %
8858 %    o image_info: the image info.
8859 %
8860 %    o image:  The image.
8861 %
8862 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8863 */
8864 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
8865 {
8866   MagickBooleanType
8867     status;
8868
8869   MngInfo
8870     *mng_info;
8871
8872   int
8873     have_mng_structure;
8874
8875   unsigned int
8876     logging;
8877
8878   /*
8879     Open image file.
8880   */
8881   assert(image_info != (const ImageInfo *) NULL);
8882   assert(image_info->signature == MagickSignature);
8883   assert(image != (Image *) NULL);
8884   assert(image->signature == MagickSignature);
8885   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8886   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteJNGImage()");
8887   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8888   if (status == MagickFalse)
8889     return(status);
8890
8891   /*
8892     Allocate a MngInfo structure.
8893   */
8894   have_mng_structure=MagickFalse;
8895   mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
8896   if (mng_info == (MngInfo *) NULL)
8897     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8898   /*
8899     Initialize members of the MngInfo structure.
8900   */
8901   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8902   mng_info->image=image;
8903   have_mng_structure=MagickTrue;
8904
8905   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
8906
8907   status=WriteOneJNGImage(mng_info,image_info,image);
8908   (void) CloseBlob(image);
8909
8910   (void) CatchImageException(image);
8911   MngInfoFreeStruct(mng_info,&have_mng_structure);
8912   if (logging != MagickFalse)
8913     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
8914   return(status);
8915 }
8916 #endif
8917
8918
8919
8920 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
8921 {
8922   const char
8923     *option;
8924
8925   Image
8926     *next_image;
8927
8928   MagickBooleanType
8929     status;
8930
8931   MngInfo
8932     *mng_info;
8933
8934   int
8935     have_mng_structure,
8936     image_count,
8937     need_iterations,
8938     need_matte;
8939
8940   volatile int
8941 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
8942     defined(PNG_MNG_FEATURES_SUPPORTED)
8943     need_local_plte,
8944 #endif
8945     all_images_are_gray,
8946     logging,
8947     need_defi,
8948     optimize,
8949     use_global_plte;
8950
8951   register ssize_t
8952     i;
8953
8954   unsigned char
8955     chunk[800];
8956
8957   volatile unsigned int
8958     write_jng,
8959     write_mng;
8960
8961   volatile size_t
8962     scene;
8963
8964   size_t
8965     final_delay=0,
8966     initial_delay;
8967
8968 #if (PNG_LIBPNG_VER < 10200)
8969     if (image_info->verbose)
8970       printf("Your PNG library (libpng-%s) is rather old.\n",
8971          PNG_LIBPNG_VER_STRING);
8972 #endif
8973
8974   /*
8975     Open image file.
8976   */
8977   assert(image_info != (const ImageInfo *) NULL);
8978   assert(image_info->signature == MagickSignature);
8979   assert(image != (Image *) NULL);
8980   assert(image->signature == MagickSignature);
8981   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
8982   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"enter WriteMNGImage()");
8983   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
8984   if (status == MagickFalse)
8985     return(status);
8986
8987   /*
8988     Allocate a MngInfo structure.
8989   */
8990   have_mng_structure=MagickFalse;
8991   mng_info=(MngInfo *) AcquireAlignedMemory(1,sizeof(MngInfo));
8992   if (mng_info == (MngInfo *) NULL)
8993     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8994   /*
8995     Initialize members of the MngInfo structure.
8996   */
8997   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
8998   mng_info->image=image;
8999   have_mng_structure=MagickTrue;
9000   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
9001
9002   /*
9003    * See if user has requested a specific PNG subformat to be used
9004    * for all of the PNGs in the MNG being written, e.g.,
9005    *
9006    *    convert *.png png8:animation.mng
9007    *
9008    * To do: check -define png:bit_depth and png:color_type as well,
9009    * or perhaps use mng:bit_depth and mng:color_type instead for
9010    * global settings.
9011    */
9012
9013   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
9014   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
9015   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
9016
9017   write_jng=MagickFalse;
9018   if (image_info->compression == JPEGCompression)
9019     write_jng=MagickTrue;
9020
9021   mng_info->adjoin=image_info->adjoin &&
9022     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
9023
9024   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9025     optimize=MagickFalse;
9026   else
9027     optimize=(image_info->type == OptimizeType || image_info->type ==
9028       UndefinedType);
9029
9030   if (logging != MagickFalse)
9031     {
9032       /* Log some info about the input */
9033       Image
9034         *p;
9035
9036       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9037         "  Checking input image(s)");
9038       if (optimize)
9039         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9040           "    Optimize: TRUE");
9041       else
9042         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9043           "    Optimize: FALSE");
9044       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9045         "    Image_info depth: %.20g",(double) image_info->depth);
9046       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9047         "    Type: %d",image_info->type);
9048
9049       scene=0;
9050       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9051       {
9052         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9053           "    Scene: %.20g",(double) scene++);
9054         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9055           "      Image depth: %.20g",(double) p->depth);
9056         if (p->matte)
9057           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9058             "      Matte: True");
9059         else
9060           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9061             "      Matte: False");
9062         if (p->storage_class == PseudoClass)
9063           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9064             "      Storage class: PseudoClass");
9065         else
9066           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9067             "      Storage class: DirectClass");
9068         if (p->colors)
9069           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9070             "      Number of colors: %.20g",(double) p->colors);
9071         else
9072           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9073             "      Number of colors: unspecified");
9074         if (mng_info->adjoin == MagickFalse)
9075           break;
9076       }
9077     }
9078
9079   /*
9080     Sometimes we get PseudoClass images whose RGB values don't match
9081     the colors in the colormap.  This code syncs the RGB values.
9082   */
9083   {
9084     Image
9085       *p;
9086
9087     for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9088     {
9089       if (p->taint && p->storage_class == PseudoClass)
9090          (void) SyncImage(p);
9091       if (mng_info->adjoin == MagickFalse)
9092         break;
9093     }
9094   }
9095
9096 #ifdef PNG_BUILD_PALETTE
9097   if (optimize)
9098     {
9099       /*
9100         Sometimes we get DirectClass images that have 256 colors or fewer.
9101         This code will convert them to PseudoClass and build a colormap.
9102       */
9103       Image
9104         *p;
9105
9106       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
9107       {
9108         if (p->storage_class != PseudoClass)
9109           {
9110             p->colors=GetNumberColors(p,(FILE *) NULL,&p->exception);
9111             if (p->colors <= 256)
9112               {
9113                 p->colors=0;
9114                 if (p->matte != MagickFalse)
9115                   (void) SetImageType(p,PaletteMatteType);
9116                 else
9117                   (void) SetImageType(p,PaletteType);
9118               }
9119           }
9120         if (mng_info->adjoin == MagickFalse)
9121           break;
9122       }
9123     }
9124 #endif
9125
9126   use_global_plte=MagickFalse;
9127   all_images_are_gray=MagickFalse;
9128 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9129   need_local_plte=MagickTrue;
9130 #endif
9131   need_defi=MagickFalse;
9132   need_matte=MagickFalse;
9133   mng_info->framing_mode=1;
9134   mng_info->old_framing_mode=1;
9135
9136   if (write_mng)
9137       if (image_info->page != (char *) NULL)
9138         {
9139           /*
9140             Determine image bounding box.
9141           */
9142           SetGeometry(image,&mng_info->page);
9143           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
9144             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
9145         }
9146   if (write_mng)
9147     {
9148       unsigned int
9149         need_geom;
9150
9151       unsigned short
9152         red,
9153         green,
9154         blue;
9155
9156       mng_info->page=image->page;
9157       need_geom=MagickTrue;
9158       if (mng_info->page.width || mng_info->page.height)
9159          need_geom=MagickFalse;
9160       /*
9161         Check all the scenes.
9162       */
9163       initial_delay=image->delay;
9164       need_iterations=MagickFalse;
9165       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
9166       mng_info->equal_physs=MagickTrue,
9167       mng_info->equal_gammas=MagickTrue;
9168       mng_info->equal_srgbs=MagickTrue;
9169       mng_info->equal_backgrounds=MagickTrue;
9170       image_count=0;
9171 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9172     defined(PNG_MNG_FEATURES_SUPPORTED)
9173       all_images_are_gray=MagickTrue;
9174       mng_info->equal_palettes=MagickFalse;
9175       need_local_plte=MagickFalse;
9176 #endif
9177       for (next_image=image; next_image != (Image *) NULL; )
9178       {
9179         if (need_geom)
9180           {
9181             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
9182               mng_info->page.width=next_image->columns+next_image->page.x;
9183             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
9184               mng_info->page.height=next_image->rows+next_image->page.y;
9185           }
9186         if (next_image->page.x || next_image->page.y)
9187           need_defi=MagickTrue;
9188         if (next_image->matte)
9189           need_matte=MagickTrue;
9190         if ((int) next_image->dispose >= BackgroundDispose)
9191           if (next_image->matte || next_image->page.x || next_image->page.y ||
9192               ((next_image->columns < mng_info->page.width) &&
9193                (next_image->rows < mng_info->page.height)))
9194             mng_info->need_fram=MagickTrue;
9195         if (next_image->iterations)
9196           need_iterations=MagickTrue;
9197         final_delay=next_image->delay;
9198         if (final_delay != initial_delay || final_delay > 1UL*
9199            next_image->ticks_per_second)
9200           mng_info->need_fram=1;
9201 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9202     defined(PNG_MNG_FEATURES_SUPPORTED)
9203         /*
9204           check for global palette possibility.
9205         */
9206         if (image->matte != MagickFalse)
9207            need_local_plte=MagickTrue;
9208         if (need_local_plte == 0)
9209           {
9210             if (ImageIsGray(image) == MagickFalse)
9211               all_images_are_gray=MagickFalse;
9212             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
9213             if (use_global_plte == 0)
9214               use_global_plte=mng_info->equal_palettes;
9215             need_local_plte=!mng_info->equal_palettes;
9216           }
9217 #endif
9218         if (GetNextImageInList(next_image) != (Image *) NULL)
9219           {
9220             if (next_image->background_color.red !=
9221                 next_image->next->background_color.red ||
9222                 next_image->background_color.green !=
9223                 next_image->next->background_color.green ||
9224                 next_image->background_color.blue !=
9225                 next_image->next->background_color.blue)
9226               mng_info->equal_backgrounds=MagickFalse;
9227             if (next_image->gamma != next_image->next->gamma)
9228               mng_info->equal_gammas=MagickFalse;
9229             if (next_image->rendering_intent !=
9230                 next_image->next->rendering_intent)
9231               mng_info->equal_srgbs=MagickFalse;
9232             if ((next_image->units != next_image->next->units) ||
9233                 (next_image->x_resolution != next_image->next->x_resolution) ||
9234                 (next_image->y_resolution != next_image->next->y_resolution))
9235               mng_info->equal_physs=MagickFalse;
9236             if (mng_info->equal_chrms)
9237               {
9238                 if (next_image->chromaticity.red_primary.x !=
9239                     next_image->next->chromaticity.red_primary.x ||
9240                     next_image->chromaticity.red_primary.y !=
9241                     next_image->next->chromaticity.red_primary.y ||
9242                     next_image->chromaticity.green_primary.x !=
9243                     next_image->next->chromaticity.green_primary.x ||
9244                     next_image->chromaticity.green_primary.y !=
9245                     next_image->next->chromaticity.green_primary.y ||
9246                     next_image->chromaticity.blue_primary.x !=
9247                     next_image->next->chromaticity.blue_primary.x ||
9248                     next_image->chromaticity.blue_primary.y !=
9249                     next_image->next->chromaticity.blue_primary.y ||
9250                     next_image->chromaticity.white_point.x !=
9251                     next_image->next->chromaticity.white_point.x ||
9252                     next_image->chromaticity.white_point.y !=
9253                     next_image->next->chromaticity.white_point.y)
9254                   mng_info->equal_chrms=MagickFalse;
9255               }
9256           }
9257         image_count++;
9258         next_image=GetNextImageInList(next_image);
9259       }
9260       if (image_count < 2)
9261         {
9262           mng_info->equal_backgrounds=MagickFalse;
9263           mng_info->equal_chrms=MagickFalse;
9264           mng_info->equal_gammas=MagickFalse;
9265           mng_info->equal_srgbs=MagickFalse;
9266           mng_info->equal_physs=MagickFalse;
9267           use_global_plte=MagickFalse;
9268 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9269           need_local_plte=MagickTrue;
9270 #endif
9271           need_iterations=MagickFalse;
9272         }
9273      if (mng_info->need_fram == MagickFalse)
9274        {
9275          /*
9276            Only certain framing rates 100/n are exactly representable without
9277            the FRAM chunk but we'll allow some slop in VLC files
9278          */
9279          if (final_delay == 0)
9280            {
9281              if (need_iterations != MagickFalse)
9282                {
9283                  /*
9284                    It's probably a GIF with loop; don't run it *too* fast.
9285                  */
9286                  if (mng_info->adjoin)
9287                    {
9288                      final_delay=10;
9289                      (void) ThrowMagickException(&image->exception,
9290                         GetMagickModule(),CoderWarning,
9291                        "input has zero delay between all frames; assuming",
9292                        " 10 cs `%s'","");
9293                    }
9294                }
9295              else
9296                mng_info->ticks_per_second=0;
9297            }
9298          if (final_delay != 0)
9299            mng_info->ticks_per_second=(png_uint_32) (image->ticks_per_second/final_delay);
9300          if (final_delay > 50)
9301            mng_info->ticks_per_second=2;
9302          if (final_delay > 75)
9303            mng_info->ticks_per_second=1;
9304          if (final_delay > 125)
9305            mng_info->need_fram=MagickTrue;
9306          if (need_defi && final_delay > 2 && (final_delay != 4) &&
9307             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
9308             (final_delay != 25) && (final_delay != 50) && (final_delay !=
9309                1UL*image->ticks_per_second))
9310            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
9311        }
9312      if (mng_info->need_fram != MagickFalse)
9313         mng_info->ticks_per_second=1UL*image->ticks_per_second;
9314      /*
9315         If pseudocolor, we should also check to see if all the
9316         palettes are identical and write a global PLTE if they are.
9317         ../glennrp Feb 99.
9318      */
9319      /*
9320         Write the MNG version 1.0 signature and MHDR chunk.
9321      */
9322      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
9323      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
9324      PNGType(chunk,mng_MHDR);
9325      LogPNGChunk((int) logging,mng_MHDR,28L);
9326      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
9327      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
9328      PNGLong(chunk+12,mng_info->ticks_per_second);
9329      PNGLong(chunk+16,0L);  /* layer count=unknown */
9330      PNGLong(chunk+20,0L);  /* frame count=unknown */
9331      PNGLong(chunk+24,0L);  /* play time=unknown   */
9332      if (write_jng)
9333        {
9334          if (need_matte)
9335            {
9336              if (need_defi || mng_info->need_fram || use_global_plte)
9337                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
9338              else
9339                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
9340            }
9341          else
9342            {
9343              if (need_defi || mng_info->need_fram || use_global_plte)
9344                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
9345              else
9346                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
9347            }
9348        }
9349      else
9350        {
9351          if (need_matte)
9352            {
9353              if (need_defi || mng_info->need_fram || use_global_plte)
9354                PNGLong(chunk+28,11L);    /* simplicity=LC */
9355              else
9356                PNGLong(chunk+28,9L);    /* simplicity=VLC */
9357            }
9358          else
9359            {
9360              if (need_defi || mng_info->need_fram || use_global_plte)
9361                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
9362              else
9363                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
9364            }
9365        }
9366      (void) WriteBlob(image,32,chunk);
9367      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
9368      option=GetImageOption(image_info,"mng:need-cacheoff");
9369      if (option != (const char *) NULL)
9370        {
9371          size_t
9372            length;
9373
9374          /*
9375            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
9376          */
9377          PNGType(chunk,mng_nEED);
9378          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
9379          (void) WriteBlobMSBULong(image,(size_t) length);
9380          LogPNGChunk((int) logging,mng_nEED,(size_t) length);
9381          length+=4;
9382          (void) WriteBlob(image,length,chunk);
9383          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
9384        }
9385      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
9386          (GetNextImageInList(image) != (Image *) NULL) &&
9387          (image->iterations != 1))
9388        {
9389          /*
9390            Write MNG TERM chunk
9391          */
9392          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
9393          PNGType(chunk,mng_TERM);
9394          LogPNGChunk((int) logging,mng_TERM,10L);
9395          chunk[4]=3;  /* repeat animation */
9396          chunk[5]=0;  /* show last frame when done */
9397          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
9398             final_delay/MagickMax(image->ticks_per_second,1)));
9399          if (image->iterations == 0)
9400            PNGLong(chunk+10,PNG_UINT_31_MAX);
9401          else
9402            PNGLong(chunk+10,(png_uint_32) image->iterations);
9403          if (logging != MagickFalse)
9404            {
9405              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9406                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
9407               final_delay/MagickMax(image->ticks_per_second,1)));
9408              if (image->iterations == 0)
9409                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9410                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
9411              else
9412                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9413                  "     Image iterations: %.20g",(double) image->iterations);
9414            }
9415          (void) WriteBlob(image,14,chunk);
9416          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9417        }
9418      /*
9419        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9420      */
9421      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
9422           mng_info->equal_srgbs)
9423        {
9424          /*
9425            Write MNG sRGB chunk
9426          */
9427          (void) WriteBlobMSBULong(image,1L);
9428          PNGType(chunk,mng_sRGB);
9429          LogPNGChunk((int) logging,mng_sRGB,1L);
9430          if (image->rendering_intent != UndefinedIntent)
9431            chunk[4]=(unsigned char)
9432              PNG_RenderingIntent_from_Magick_RenderingIntent(
9433              (image->rendering_intent));
9434          else
9435            chunk[4]=(unsigned char)
9436              PNG_RenderingIntent_from_Magick_RenderingIntent(
9437              (PerceptualIntent));
9438          (void) WriteBlob(image,5,chunk);
9439          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9440          mng_info->have_write_global_srgb=MagickTrue;
9441        }
9442      else
9443        {
9444          if (image->gamma && mng_info->equal_gammas)
9445            {
9446              /*
9447                 Write MNG gAMA chunk
9448              */
9449              (void) WriteBlobMSBULong(image,4L);
9450              PNGType(chunk,mng_gAMA);
9451              LogPNGChunk((int) logging,mng_gAMA,4L);
9452              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
9453              (void) WriteBlob(image,8,chunk);
9454              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
9455              mng_info->have_write_global_gama=MagickTrue;
9456            }
9457          if (mng_info->equal_chrms)
9458            {
9459              PrimaryInfo
9460                primary;
9461
9462              /*
9463                 Write MNG cHRM chunk
9464              */
9465              (void) WriteBlobMSBULong(image,32L);
9466              PNGType(chunk,mng_cHRM);
9467              LogPNGChunk((int) logging,mng_cHRM,32L);
9468              primary=image->chromaticity.white_point;
9469              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
9470              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
9471              primary=image->chromaticity.red_primary;
9472              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
9473              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
9474              primary=image->chromaticity.green_primary;
9475              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
9476              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
9477              primary=image->chromaticity.blue_primary;
9478              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
9479              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
9480              (void) WriteBlob(image,36,chunk);
9481              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
9482              mng_info->have_write_global_chrm=MagickTrue;
9483            }
9484        }
9485      if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
9486        {
9487          /*
9488             Write MNG pHYs chunk
9489          */
9490          (void) WriteBlobMSBULong(image,9L);
9491          PNGType(chunk,mng_pHYs);
9492          LogPNGChunk((int) logging,mng_pHYs,9L);
9493          if (image->units == PixelsPerInchResolution)
9494            {
9495              PNGLong(chunk+4,(png_uint_32)
9496                (image->x_resolution*100.0/2.54+0.5));
9497              PNGLong(chunk+8,(png_uint_32)
9498                (image->y_resolution*100.0/2.54+0.5));
9499              chunk[12]=1;
9500            }
9501          else
9502            {
9503              if (image->units == PixelsPerCentimeterResolution)
9504                {
9505                  PNGLong(chunk+4,(png_uint_32)
9506                    (image->x_resolution*100.0+0.5));
9507                  PNGLong(chunk+8,(png_uint_32)
9508                    (image->y_resolution*100.0+0.5));
9509                  chunk[12]=1;
9510                }
9511              else
9512                {
9513                  PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
9514                  PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
9515                  chunk[12]=0;
9516                }
9517            }
9518          (void) WriteBlob(image,13,chunk);
9519          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
9520        }
9521      /*
9522        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
9523        or does not cover the entire frame.
9524      */
9525      if (write_mng && (image->matte || image->page.x > 0 ||
9526          image->page.y > 0 || (image->page.width &&
9527          (image->page.width+image->page.x < mng_info->page.width))
9528          || (image->page.height && (image->page.height+image->page.y
9529          < mng_info->page.height))))
9530        {
9531          (void) WriteBlobMSBULong(image,6L);
9532          PNGType(chunk,mng_BACK);
9533          LogPNGChunk((int) logging,mng_BACK,6L);
9534          red=ScaleQuantumToShort(image->background_color.red);
9535          green=ScaleQuantumToShort(image->background_color.green);
9536          blue=ScaleQuantumToShort(image->background_color.blue);
9537          PNGShort(chunk+4,red);
9538          PNGShort(chunk+6,green);
9539          PNGShort(chunk+8,blue);
9540          (void) WriteBlob(image,10,chunk);
9541          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9542          if (mng_info->equal_backgrounds)
9543            {
9544              (void) WriteBlobMSBULong(image,6L);
9545              PNGType(chunk,mng_bKGD);
9546              LogPNGChunk((int) logging,mng_bKGD,6L);
9547              (void) WriteBlob(image,10,chunk);
9548              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
9549            }
9550        }
9551
9552 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9553      if ((need_local_plte == MagickFalse) &&
9554          (image->storage_class == PseudoClass) &&
9555          (all_images_are_gray == MagickFalse))
9556        {
9557          size_t
9558            data_length;
9559
9560          /*
9561            Write MNG PLTE chunk
9562          */
9563          data_length=3*image->colors;
9564          (void) WriteBlobMSBULong(image,data_length);
9565          PNGType(chunk,mng_PLTE);
9566          LogPNGChunk((int) logging,mng_PLTE,data_length);
9567          for (i=0; i < (ssize_t) image->colors; i++)
9568          {
9569            chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
9570            chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
9571            chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
9572          }
9573          (void) WriteBlob(image,data_length+4,chunk);
9574          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
9575          mng_info->have_write_global_plte=MagickTrue;
9576        }
9577 #endif
9578     }
9579   scene=0;
9580   mng_info->delay=0;
9581 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9582     defined(PNG_MNG_FEATURES_SUPPORTED)
9583   mng_info->equal_palettes=MagickFalse;
9584 #endif
9585   do
9586   {
9587     if (mng_info->adjoin)
9588     {
9589 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
9590     defined(PNG_MNG_FEATURES_SUPPORTED)
9591     /*
9592       If we aren't using a global palette for the entire MNG, check to
9593       see if we can use one for two or more consecutive images.
9594     */
9595     if (need_local_plte && use_global_plte && !all_images_are_gray)
9596       {
9597         if (mng_info->IsPalette)
9598           {
9599             /*
9600               When equal_palettes is true, this image has the same palette
9601               as the previous PseudoClass image
9602             */
9603             mng_info->have_write_global_plte=mng_info->equal_palettes;
9604             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
9605             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
9606               {
9607                 /*
9608                   Write MNG PLTE chunk
9609                 */
9610                 size_t
9611                   data_length;
9612
9613                 data_length=3*image->colors;
9614                 (void) WriteBlobMSBULong(image,data_length);
9615                 PNGType(chunk,mng_PLTE);
9616                 LogPNGChunk((int) logging,mng_PLTE,data_length);
9617                 for (i=0; i < (ssize_t) image->colors; i++)
9618                 {
9619                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
9620                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
9621                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
9622                 }
9623                 (void) WriteBlob(image,data_length+4,chunk);
9624                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
9625                    (uInt) (data_length+4)));
9626                 mng_info->have_write_global_plte=MagickTrue;
9627               }
9628           }
9629         else
9630           mng_info->have_write_global_plte=MagickFalse;
9631       }
9632 #endif
9633     if (need_defi)
9634       {
9635         ssize_t
9636           previous_x,
9637           previous_y;
9638
9639         if (scene)
9640           {
9641             previous_x=mng_info->page.x;
9642             previous_y=mng_info->page.y;
9643           }
9644         else
9645           {
9646             previous_x=0;
9647             previous_y=0;
9648           }
9649         mng_info->page=image->page;
9650         if ((mng_info->page.x !=  previous_x) ||
9651             (mng_info->page.y != previous_y))
9652           {
9653              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
9654              PNGType(chunk,mng_DEFI);
9655              LogPNGChunk((int) logging,mng_DEFI,12L);
9656              chunk[4]=0; /* object 0 MSB */
9657              chunk[5]=0; /* object 0 LSB */
9658              chunk[6]=0; /* visible  */
9659              chunk[7]=0; /* abstract */
9660              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
9661              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
9662              (void) WriteBlob(image,16,chunk);
9663              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
9664           }
9665       }
9666     }
9667
9668    mng_info->write_mng=write_mng;
9669
9670    if ((int) image->dispose >= 3)
9671      mng_info->framing_mode=3;
9672
9673    if (mng_info->need_fram && mng_info->adjoin &&
9674        ((image->delay != mng_info->delay) ||
9675         (mng_info->framing_mode != mng_info->old_framing_mode)))
9676      {
9677        if (image->delay == mng_info->delay)
9678          {
9679            /*
9680              Write a MNG FRAM chunk with the new framing mode.
9681            */
9682            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
9683            PNGType(chunk,mng_FRAM);
9684            LogPNGChunk((int) logging,mng_FRAM,1L);
9685            chunk[4]=(unsigned char) mng_info->framing_mode;
9686            (void) WriteBlob(image,5,chunk);
9687            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
9688          }
9689        else
9690          {
9691            /*
9692              Write a MNG FRAM chunk with the delay.
9693            */
9694            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
9695            PNGType(chunk,mng_FRAM);
9696            LogPNGChunk((int) logging,mng_FRAM,10L);
9697            chunk[4]=(unsigned char) mng_info->framing_mode;
9698            chunk[5]=0;  /* frame name separator (no name) */
9699            chunk[6]=2;  /* flag for changing default delay */
9700            chunk[7]=0;  /* flag for changing frame timeout */
9701            chunk[8]=0;  /* flag for changing frame clipping */
9702            chunk[9]=0;  /* flag for changing frame sync_id */
9703            PNGLong(chunk+10,(png_uint_32)
9704              ((mng_info->ticks_per_second*
9705              image->delay)/MagickMax(image->ticks_per_second,1)));
9706            (void) WriteBlob(image,14,chunk);
9707            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
9708            mng_info->delay=(png_uint_32) image->delay;
9709          }
9710        mng_info->old_framing_mode=mng_info->framing_mode;
9711      }
9712
9713 #if defined(JNG_SUPPORTED)
9714    if (image_info->compression == JPEGCompression)
9715      {
9716        ImageInfo
9717          *write_info;
9718
9719        if (logging != MagickFalse)
9720          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9721            "  Writing JNG object.");
9722        /* To do: specify the desired alpha compression method. */
9723        write_info=CloneImageInfo(image_info);
9724        write_info->compression=UndefinedCompression;
9725        status=WriteOneJNGImage(mng_info,write_info,image);
9726        write_info=DestroyImageInfo(write_info);
9727      }
9728    else
9729 #endif
9730      {
9731        if (logging != MagickFalse)
9732          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9733            "  Writing PNG object.");
9734        status=WriteOnePNGImage(mng_info,image_info,image);
9735      }
9736
9737     if (status == MagickFalse)
9738       {
9739         MngInfoFreeStruct(mng_info,&have_mng_structure);
9740         (void) CloseBlob(image);
9741         return(MagickFalse);
9742       }
9743     (void) CatchImageException(image);
9744     if (GetNextImageInList(image) == (Image *) NULL)
9745       break;
9746     image=SyncNextImageInList(image);
9747     status=SetImageProgress(image,SaveImagesTag,scene++,
9748       GetImageListLength(image));
9749     if (status == MagickFalse)
9750       break;
9751   } while (mng_info->adjoin);
9752   if (write_mng)
9753     {
9754       while (GetPreviousImageInList(image) != (Image *) NULL)
9755         image=GetPreviousImageInList(image);
9756       /*
9757         Write the MEND chunk.
9758       */
9759       (void) WriteBlobMSBULong(image,0x00000000L);
9760       PNGType(chunk,mng_MEND);
9761       LogPNGChunk((int) logging,mng_MEND,0L);
9762       (void) WriteBlob(image,4,chunk);
9763       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
9764     }
9765   /*
9766     Relinquish resources.
9767   */
9768   (void) CloseBlob(image);
9769   MngInfoFreeStruct(mng_info,&have_mng_structure);
9770   if (logging != MagickFalse)
9771     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
9772   return(MagickTrue);
9773 }
9774 #else /* PNG_LIBPNG_VER > 10011 */
9775 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
9776 {
9777   image=image;
9778   printf("Your PNG library is too old: You have libpng-%s\n",
9779      PNG_LIBPNG_VER_STRING);
9780   ThrowBinaryException(CoderError,"PNG library is too old",
9781      image_info->filename);
9782 }
9783 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
9784 {
9785   return(WritePNGImage(image_info,image));
9786 }
9787 #endif /* PNG_LIBPNG_VER > 10011 */
9788 #endif