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