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