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