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