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