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