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