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