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