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