]> 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***PixelChannel() */
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***PixelChannel() */
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                s=q;
8136                for (x=0; x < (ssize_t) image->columns; x++)
8137                {
8138                  if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8139                      GetPixelRed(image,s) != GetPixelBlue(image,s))
8140                    {
8141                       ping_have_color=MagickTrue;
8142                       ping_have_non_bw=MagickTrue;
8143                       break;
8144                    }
8145                  s+=GetPixelChannels(image);
8146                }
8147
8148                if (ping_have_color != MagickFalse)
8149                  break;
8150
8151                /* Worst case is black-and-white; we are looking at every
8152                 * pixel twice.
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                          break;
8165                        }
8166                      s+=GetPixelChannels(image);
8167                    }
8168                }
8169              }
8170            }
8171        }
8172
8173      if (image_colors < 257)
8174        {
8175          PixelPacket
8176            colormap[260];
8177
8178          /*
8179           * Initialize image colormap.
8180           */
8181
8182          if (logging != MagickFalse)
8183             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8184                   "      Sort the new colormap");
8185
8186         /* Sort palette, transparent first */;
8187
8188          n = 0;
8189
8190          for (i=0; i<number_transparent; i++)
8191             colormap[n++] = transparent[i];
8192
8193          for (i=0; i<number_semitransparent; i++)
8194             colormap[n++] = semitransparent[i];
8195
8196          for (i=0; i<number_opaque; i++)
8197             colormap[n++] = opaque[i];
8198
8199          ping_background.index +=
8200            (number_transparent + number_semitransparent);
8201
8202          /* image_colors < 257; search the colormap instead of the pixels
8203           * to get ping_have_color and ping_have_non_bw
8204           */
8205          for (i=0; i<n; i++)
8206          {
8207            if (ping_have_color == MagickFalse)
8208              {
8209                 if (colormap[i].red != colormap[i].green ||
8210                     colormap[i].red != colormap[i].blue)
8211                   {
8212                      ping_have_color=MagickTrue;
8213                      ping_have_non_bw=MagickTrue;
8214                      break;
8215                   }
8216               }
8217
8218            if (ping_have_non_bw == MagickFalse)
8219              {
8220                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8221                    ping_have_non_bw=MagickTrue;
8222              }
8223           }
8224
8225         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8226             (number_transparent == 0 && number_semitransparent == 0)) &&
8227             (((mng_info->write_png_colortype-1) ==
8228             PNG_COLOR_TYPE_PALETTE) ||
8229             (mng_info->write_png_colortype == 0)))
8230           {
8231             if (logging != MagickFalse)
8232               {
8233                 if (n !=  (ssize_t) image_colors)
8234                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8235                    "   image_colors (%d) and n (%d)  don't match",
8236                    image_colors, n);
8237
8238                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8239                    "      AcquireImageColormap");
8240               }
8241
8242             image->colors = image_colors;
8243
8244             if (AcquireImageColormap(image,image_colors) ==
8245                 MagickFalse)
8246                ThrowWriterException(ResourceLimitError,
8247                    "MemoryAllocationFailed");
8248
8249             for (i=0; i< (ssize_t) image_colors; i++)
8250                image->colormap[i] = colormap[i];
8251
8252             if (logging != MagickFalse)
8253               {
8254                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8255                       "      image->colors=%d (%d)",
8256                       (int) image->colors, image_colors);
8257
8258                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8259                       "      Update the pixel indexes");
8260               }
8261
8262             /* Sync the pixel indices with the new colormap */
8263
8264             for (y=0; y < (ssize_t) image->rows; y++)
8265             {
8266               q=GetAuthenticPixels(image,0,y,image->columns,1,
8267                   exception);
8268
8269               if (q == (const Quantum *) NULL)
8270                 break;
8271
8272
8273               for (x=0; x < (ssize_t) image->columns; x++)
8274               {
8275                 for (i=0; i< (ssize_t) image_colors; i++)
8276                 {
8277                   if ((image->matte == MagickFalse ||
8278                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8279                       image->colormap[i].red == GetPixelRed(image,q) &&
8280                       image->colormap[i].green == GetPixelGreen(image,q) &&
8281                       image->colormap[i].blue == GetPixelBlue(image,q))
8282                   {
8283                     SetPixelIndex(image,i,q);
8284                     break;
8285                   }
8286                 }
8287                 q+=GetPixelChannels(image);
8288               }
8289
8290               if (SyncAuthenticPixels(image,exception) == MagickFalse)
8291                  break;
8292             }
8293           }
8294        }
8295
8296      if (logging != MagickFalse)
8297        {
8298          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8299             "      image->colors=%d", (int) image->colors);
8300
8301          if (image->colormap != NULL)
8302            {
8303              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8304                  "       i     (red,green,blue,alpha)");
8305
8306              for (i=0; i < (ssize_t) image->colors; i++)
8307              {
8308                if (i < 300 || i >= (ssize_t) image->colors - 10)
8309                  {
8310                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8311                        "       %d     (%d,%d,%d,%d)",
8312                         (int) i,
8313                         (int) image->colormap[i].red,
8314                         (int) image->colormap[i].green,
8315                         (int) image->colormap[i].blue,
8316                         (int) image->colormap[i].alpha);
8317                  }
8318              }
8319            }
8320
8321            if (number_transparent < 257)
8322              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8323                    "      number_transparent     = %d",
8324                    number_transparent);
8325            else
8326
8327              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8328                    "      number_transparent     > 256");
8329
8330            if (number_opaque < 257)
8331              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8332                    "      number_opaque          = %d",
8333                    number_opaque);
8334
8335            else
8336              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8337                    "      number_opaque          > 256");
8338
8339            if (number_semitransparent < 257)
8340              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8341                    "      number_semitransparent = %d",
8342                    number_semitransparent);
8343
8344            else
8345              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8346                    "      number_semitransparent > 256");
8347
8348            if (ping_have_non_bw == MagickFalse)
8349               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8350                     "      All pixels and the background are black or white");
8351
8352            else if (ping_have_color == MagickFalse)
8353               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8354                     "      All pixels and the background are gray");
8355
8356            else
8357               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8358                     "      At least one pixel or the background is non-gray");
8359
8360            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8361                "    Exit BUILD_PALETTE:");
8362        }
8363
8364    if (mng_info->write_png8 == MagickFalse)
8365       break;
8366
8367    /* Make any reductions necessary for the PNG8 format */
8368     if (image_colors <= 256 &&
8369         image_colors != 0 && image->colormap != NULL &&
8370         number_semitransparent == 0 &&
8371         number_transparent <= 1)
8372       break;
8373
8374     /* PNG8 can't have semitransparent colors so we threshold the
8375      * alpha to 0 or OpaqueAlpha
8376      */
8377     if (number_semitransparent != 0)
8378       {
8379         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8380             "    Thresholding the alpha channel to binary");
8381
8382         for (y=0; y < (ssize_t) image->rows; y++)
8383         {
8384           r=GetAuthenticPixels(image,0,y,image->columns,1,
8385               exception);
8386
8387           if (r == (Quantum *) NULL)
8388             break;
8389
8390           for (x=0; x < (ssize_t) image->columns; x++)
8391           {
8392               if (GetPixelAlpha(image,r) > TransparentAlpha/2)
8393                 {
8394                   SetPixelPacket(image,&image->background_color,r);
8395                   SetPixelAlpha(image,TransparentAlpha,r);
8396                 }
8397               else
8398                   SetPixelAlpha(image,OpaqueAlpha,r);
8399               r+=GetPixelChannels(image);
8400           }
8401
8402           if (SyncAuthenticPixels(image,exception) == MagickFalse)
8403              break;
8404
8405           if (image_colors != 0 && image_colors <= 256 &&
8406              image->colormap != NULL)
8407             for (i=0; i<image_colors; i++)
8408                 image->colormap[i].alpha =
8409                     (image->colormap[i].alpha > TransparentAlpha/2 ?
8410                     TransparentAlpha : OpaqueAlpha);
8411         }
8412       continue;
8413     }
8414
8415     /* PNG8 can't have more than 256 colors so we quantize the pixels and
8416      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
8417      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8418      * colors or less.
8419      */
8420     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8421       {
8422         if (logging != MagickFalse)
8423            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8424                "    Quantizing the background color to 4-4-4");
8425
8426         tried_444 = MagickTrue;
8427
8428         LBR04PacketRGB(image->background_color);
8429
8430         if (logging != MagickFalse)
8431           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8432               "    Quantizing the pixel colors to 4-4-4");
8433
8434         if (image->colormap == NULL)
8435         {
8436           for (y=0; y < (ssize_t) image->rows; y++)
8437           {
8438             r=GetAuthenticPixels(image,0,y,image->columns,1,
8439                 exception);
8440
8441             if (r == (Quantum *) NULL)
8442               break;
8443
8444             for (x=0; x < (ssize_t) image->columns; x++)
8445             {
8446               if (GetPixelAlpha(image,r) == OpaqueAlpha)
8447                   LBR04RGB(r);
8448               r++;
8449             }
8450
8451             if (SyncAuthenticPixels(image,exception) == MagickFalse)
8452                break;
8453           }
8454         }
8455
8456         else /* Should not reach this; colormap already exists and
8457                 must be <= 256 */
8458         {
8459           if (logging != MagickFalse)
8460               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8461               "    Quantizing the colormap to 4-4-4");
8462
8463           for (i=0; i<image_colors; i++)
8464           {
8465             LBR04PacketRGB(image->colormap[i]);
8466           }
8467         }
8468         continue;
8469       }
8470
8471     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8472       {
8473         if (logging != MagickFalse)
8474            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8475                "    Quantizing the background color to 3-3-3");
8476
8477         tried_333 = MagickTrue;
8478
8479         LBR03PacketRGB(image->background_color);
8480
8481         if (logging != MagickFalse)
8482           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8483               "    Quantizing the pixel colors to 3-3-3-1");
8484
8485         if (image->colormap == NULL)
8486         {
8487           for (y=0; y < (ssize_t) image->rows; y++)
8488           {
8489             r=GetAuthenticPixels(image,0,y,image->columns,1,
8490                 exception);
8491
8492             if (r == (Quantum *) NULL)
8493               break;
8494
8495             for (x=0; x < (ssize_t) image->columns; x++)
8496             {
8497               if (GetPixelAlpha(image,r) == OpaqueAlpha)
8498                   LBR03RGB(r);
8499               r++;
8500             }
8501
8502             if (SyncAuthenticPixels(image,exception) == MagickFalse)
8503                break;
8504           }
8505         }
8506
8507         else /* Should not reach this; colormap already exists and
8508                 must be <= 256 */
8509         {
8510           if (logging != MagickFalse)
8511               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8512               "    Quantizing the colormap to 3-3-3-1");
8513           for (i=0; i<image_colors; i++)
8514           {
8515               LBR03PacketRGB(image->colormap[i]);
8516           }
8517         }
8518         continue;
8519       }
8520
8521     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
8522       {
8523         if (logging != MagickFalse)
8524            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8525                "    Quantizing the background color to 3-3-2");
8526
8527         tried_332 = MagickTrue;
8528
8529         /* Red and green were already done so we only quantize the blue
8530          * channel
8531          */
8532
8533         LBR02PacketBlue(image->background_color);
8534
8535         if (logging != MagickFalse)
8536           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8537               "    Quantizing the pixel colors to 3-3-2-1");
8538
8539         if (image->colormap == NULL)
8540         {
8541           for (y=0; y < (ssize_t) image->rows; y++)
8542           {
8543             r=GetAuthenticPixels(image,0,y,image->columns,1,
8544                 exception);
8545
8546             if (r == (Quantum *) NULL)
8547               break;
8548
8549             for (x=0; x < (ssize_t) image->columns; x++)
8550             {
8551               if (GetPixelAlpha(image,r) == OpaqueAlpha)
8552                   LBR02Blue(r);
8553               r++;
8554             }
8555
8556             if (SyncAuthenticPixels(image,exception) == MagickFalse)
8557                break;
8558           }
8559         }
8560
8561         else /* Should not reach this; colormap already exists and
8562                 must be <= 256 */
8563         {
8564           if (logging != MagickFalse)
8565               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8566               "    Quantizing the colormap to 3-3-2-1");
8567           for (i=0; i<image_colors; i++)
8568           {
8569               LBR02PacketBlue(image->colormap[i]);
8570           }
8571       }
8572       continue;
8573     }
8574     break;
8575
8576     if (image_colors == 0 || image_colors > 256)
8577     {
8578       /* Take care of special case with 256 colors + 1 transparent
8579        * color.  We don't need to quantize to 2-3-2-1; we only need to
8580        * eliminate one color, so we'll merge the two darkest red
8581        * colors (0x49, 0, 0) -> (0x24, 0, 0).
8582        */
8583       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8584           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8585           ScaleQuantumToChar(image->background_color.blue) == 0x00)
8586       {
8587          image->background_color.red=ScaleCharToQuantum(0x24);
8588       }
8589
8590       if (image->colormap == NULL)
8591       {
8592         for (y=0; y < (ssize_t) image->rows; y++)
8593         {
8594           r=GetAuthenticPixels(image,0,y,image->columns,1,
8595               exception);
8596
8597           if (r == (Quantum *) NULL)
8598             break;
8599
8600           for (x=0; x < (ssize_t) image->columns; x++)
8601           {
8602             if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8603                 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8604                 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8605                 GetPixelAlpha(image,r) == OpaqueAlpha)
8606               {
8607                 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
8608               }
8609             r+=GetPixelChannels(image);
8610           }
8611
8612           if (SyncAuthenticPixels(image,exception) == MagickFalse)
8613              break;
8614
8615         }
8616       }
8617
8618       else
8619       {
8620          for (i=0; i<image_colors; i++)
8621          {
8622             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8623                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8624                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8625             {
8626                image->colormap[i].red=ScaleCharToQuantum(0x24);
8627             }
8628          }
8629       }
8630     }
8631   }
8632   /* END OF BUILD_PALETTE */
8633
8634   /* If we are excluding the tRNS chunk and there is transparency,
8635    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8636    * PNG.
8637    */
8638   if (mng_info->ping_exclude_tRNS != MagickFalse &&
8639      (number_transparent != 0 || number_semitransparent != 0))
8640     {
8641       unsigned int colortype=mng_info->write_png_colortype;
8642
8643       if (ping_have_color == MagickFalse)
8644         mng_info->write_png_colortype = 5;
8645
8646       else
8647         mng_info->write_png_colortype = 7;
8648
8649       if (colortype != 0 &&
8650          mng_info->write_png_colortype != colortype)
8651         ping_need_colortype_warning=MagickTrue;
8652
8653     }
8654
8655   /* See if cheap transparency is possible.  It is only possible
8656    * when there is a single transparent color, no semitransparent
8657    * color, and no opaque color that has the same RGB components
8658    * as the transparent color.  We only need this information if
8659    * we are writing a PNG with colortype 0 or 2, and we have not
8660    * excluded the tRNS chunk.
8661    */
8662   if (number_transparent == 1 &&
8663       mng_info->write_png_colortype < 4)
8664     {
8665        ping_have_cheap_transparency = MagickTrue;
8666
8667        if (number_semitransparent != 0)
8668          ping_have_cheap_transparency = MagickFalse;
8669
8670        else if (image_colors == 0 || image_colors > 256 ||
8671            image->colormap == NULL)
8672          {
8673            ExceptionInfo
8674              *exception;
8675
8676            register const Quantum
8677              *q;
8678
8679            exception=(&image->exception);
8680
8681            for (y=0; y < (ssize_t) image->rows; y++)
8682            {
8683              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8684
8685              if (q == (const Quantum *) NULL)
8686                break;
8687
8688              for (x=0; x < (ssize_t) image->columns; x++)
8689              {
8690                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
8691                      (unsigned short) GetPixelRed(image,q) ==
8692                                      ping_trans_color.red &&
8693                      (unsigned short) GetPixelGreen(image,q) ==
8694                                      ping_trans_color.green &&
8695                      (unsigned short) GetPixelBlue(image,q) ==
8696                                      ping_trans_color.blue)
8697                    {
8698                      ping_have_cheap_transparency = MagickFalse;
8699                      break;
8700                    }
8701
8702                  q+=GetPixelChannels(image);
8703              }
8704
8705              if (ping_have_cheap_transparency == MagickFalse)
8706                 break;
8707            }
8708          }
8709        else
8710          {
8711             /* Assuming that image->colormap[0] is the one transparent color
8712              * and that all others are opaque.
8713              */
8714             if (image_colors > 1)
8715               for (i=1; i<image_colors; i++)
8716                 if (image->colormap[i].red == image->colormap[0].red &&
8717                     image->colormap[i].green == image->colormap[0].green &&
8718                     image->colormap[i].blue == image->colormap[0].blue)
8719                   {
8720                      ping_have_cheap_transparency = MagickFalse;
8721                      break;
8722                   }
8723          }
8724
8725        if (logging != MagickFalse)
8726          {
8727            if (ping_have_cheap_transparency == MagickFalse)
8728              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8729                  "   Cheap transparency is not possible.");
8730
8731            else
8732              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8733                  "   Cheap transparency is possible.");
8734          }
8735      }
8736   else
8737     ping_have_cheap_transparency = MagickFalse;
8738
8739   image_depth=image->depth;
8740
8741   quantum_info = (QuantumInfo *) NULL;
8742   number_colors=0;
8743   image_colors=(int) image->colors;
8744   image_matte=image->matte;
8745
8746   mng_info->IsPalette=image->storage_class == PseudoClass &&
8747     image_colors <= 256 && image->colormap != NULL;
8748
8749   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8750      (image->colors == 0 || image->colormap == NULL))
8751     {
8752       image_info=DestroyImageInfo(image_info);
8753       image=DestroyImage(image);
8754       (void) ThrowMagickException(&IMimage->exception,
8755           GetMagickModule(),CoderError,
8756           "Cannot write PNG8 or color-type 3; colormap is NULL",
8757           "`%s'",IMimage->filename);
8758 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8759       UnlockSemaphoreInfo(ping_semaphore);
8760 #endif
8761       return(MagickFalse);
8762     }
8763
8764   /*
8765     Allocate the PNG structures
8766   */
8767 #ifdef PNG_USER_MEM_SUPPORTED
8768   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
8769     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8770     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
8771
8772 #else
8773   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
8774     MagickPNGErrorHandler,MagickPNGWarningHandler);
8775
8776 #endif
8777   if (ping == (png_struct *) NULL)
8778     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8779
8780   ping_info=png_create_info_struct(ping);
8781
8782   if (ping_info == (png_info *) NULL)
8783     {
8784       png_destroy_write_struct(&ping,(png_info **) NULL);
8785       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8786     }
8787
8788   png_set_write_fn(ping,image,png_put_data,png_flush_data);
8789   ping_pixels=(unsigned char *) NULL;
8790
8791   if (setjmp(png_jmpbuf(ping)))
8792     {
8793       /*
8794         PNG write failed.
8795       */
8796 #ifdef PNG_DEBUG
8797      if (image_info->verbose)
8798         (void) printf("PNG write has failed.\n");
8799 #endif
8800       png_destroy_write_struct(&ping,&ping_info);
8801 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8802       UnlockSemaphoreInfo(ping_semaphore);
8803 #endif
8804       if (ping_have_blob != MagickFalse)
8805           (void) CloseBlob(image);
8806       image_info=DestroyImageInfo(image_info);
8807       image=DestroyImage(image);
8808       return(MagickFalse);
8809     }
8810   /*
8811     Prepare PNG for writing.
8812   */
8813 #if defined(PNG_MNG_FEATURES_SUPPORTED)
8814   if (mng_info->write_mng)
8815      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
8816
8817 #else
8818 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8819   if (mng_info->write_mng)
8820      png_permit_empty_plte(ping,MagickTrue);
8821
8822 # endif
8823 #endif
8824
8825   x=0;
8826
8827   ping_width=(png_uint_32) image->columns;
8828   ping_height=(png_uint_32) image->rows;
8829
8830   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8831      image_depth=8;
8832
8833   if (mng_info->write_png_depth != 0)
8834      image_depth=mng_info->write_png_depth;
8835
8836   /* Adjust requested depth to next higher valid depth if necessary */
8837   if (image_depth > 8)
8838      image_depth=16;
8839
8840   if ((image_depth > 4) && (image_depth < 8))
8841      image_depth=8;
8842
8843   if (image_depth == 3)
8844      image_depth=4;
8845
8846   if (logging != MagickFalse)
8847     {
8848      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8849         "    width=%.20g",(double) ping_width);
8850      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8851         "    height=%.20g",(double) ping_height);
8852      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8853         "    image_matte=%.20g",(double) image->matte);
8854      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8855         "    image->depth=%.20g",(double) image->depth);
8856      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8857         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
8858     }
8859
8860   save_image_depth=image_depth;
8861   ping_bit_depth=(png_byte) save_image_depth;
8862
8863
8864 #if defined(PNG_pHYs_SUPPORTED)
8865   if (ping_exclude_pHYs == MagickFalse)
8866   {
8867   if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8868       (!mng_info->write_mng || !mng_info->equal_physs))
8869     {
8870       if (logging != MagickFalse)
8871         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8872             "    Setting up pHYs chunk");
8873
8874       if (image->units == PixelsPerInchResolution)
8875         {
8876           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8877           ping_pHYs_x_resolution=
8878              (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8879           ping_pHYs_y_resolution=
8880              (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
8881         }
8882
8883       else if (image->units == PixelsPerCentimeterResolution)
8884         {
8885           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
8886           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8887           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
8888         }
8889
8890       else
8891         {
8892           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8893           ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8894           ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
8895         }
8896
8897       if (logging != MagickFalse)
8898         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8899           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8900           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8901           (int) ping_pHYs_unit_type);
8902        ping_have_pHYs = MagickTrue;
8903     }
8904   }
8905 #endif
8906
8907   if (ping_exclude_bKGD == MagickFalse)
8908   {
8909   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
8910     {
8911        unsigned int
8912          mask;
8913
8914        mask=0xffff;
8915        if (ping_bit_depth == 8)
8916           mask=0x00ff;
8917
8918        if (ping_bit_depth == 4)
8919           mask=0x000f;
8920
8921        if (ping_bit_depth == 2)
8922           mask=0x0003;
8923
8924        if (ping_bit_depth == 1)
8925           mask=0x0001;
8926
8927        ping_background.red=(png_uint_16)
8928          (ScaleQuantumToShort(image->background_color.red) & mask);
8929
8930        ping_background.green=(png_uint_16)
8931          (ScaleQuantumToShort(image->background_color.green) & mask);
8932
8933        ping_background.blue=(png_uint_16)
8934          (ScaleQuantumToShort(image->background_color.blue) & mask);
8935
8936        ping_background.gray=(png_uint_16) ping_background.green;
8937     }
8938
8939   if (logging != MagickFalse)
8940     {
8941       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8942           "    Setting up bKGD chunk (1)");
8943       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8944           "      background_color index is %d",
8945           (int) ping_background.index);
8946
8947       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8948           "    ping_bit_depth=%d",ping_bit_depth);
8949     }
8950
8951   ping_have_bKGD = MagickTrue;
8952   }
8953
8954   /*
8955     Select the color type.
8956   */
8957   matte=image_matte;
8958   old_bit_depth=0;
8959
8960   if (mng_info->IsPalette && mng_info->write_png8)
8961     {
8962
8963       /* To do: make this a function cause it's used twice, except
8964          for reducing the sample depth from 8. */
8965
8966       number_colors=image_colors;
8967
8968       ping_have_tRNS=MagickFalse;
8969
8970       /*
8971         Set image palette.
8972       */
8973       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8974
8975       if (logging != MagickFalse)
8976         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8977             "  Setting up PLTE chunk with %d colors (%d)",
8978             number_colors, image_colors);
8979
8980       for (i=0; i < (ssize_t) number_colors; i++)
8981       {
8982         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8983         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8984         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8985         if (logging != MagickFalse)
8986           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8987 #if MAGICKCORE_QUANTUM_DEPTH == 8
8988             "    %3ld (%3d,%3d,%3d)",
8989 #else
8990             "    %5ld (%5d,%5d,%5d)",
8991 #endif
8992             (long) i,palette[i].red,palette[i].green,palette[i].blue);
8993
8994       }
8995
8996       ping_have_PLTE=MagickTrue;
8997       image_depth=ping_bit_depth;
8998       ping_num_trans=0;
8999
9000       if (matte != MagickFalse)
9001       {
9002           /*
9003             Identify which colormap entry is transparent.
9004           */
9005           assert(number_colors <= 256);
9006           assert(image->colormap != NULL);
9007
9008           for (i=0; i < (ssize_t) number_transparent; i++)
9009              ping_trans_alpha[i]=0;
9010
9011
9012           ping_num_trans=(unsigned short) (number_transparent +
9013              number_semitransparent);
9014
9015           if (ping_num_trans == 0)
9016              ping_have_tRNS=MagickFalse;
9017
9018           else
9019              ping_have_tRNS=MagickTrue;
9020       }
9021
9022       if (ping_exclude_bKGD == MagickFalse)
9023       {
9024        /*
9025         * Identify which colormap entry is the background color.
9026         */
9027
9028         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9029           if (IsPNGColorEqual(ping_background,image->colormap[i]))
9030             break;
9031
9032         ping_background.index=(png_byte) i;
9033
9034         if (logging != MagickFalse)
9035           {
9036             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9037                  "      background_color index is %d",
9038                  (int) ping_background.index);
9039           }
9040       }
9041     } /* end of write_png8 */
9042
9043   else if (mng_info->write_png24)
9044     {
9045       image_matte=MagickFalse;
9046       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9047     }
9048
9049   else if (mng_info->write_png32)
9050     {
9051       image_matte=MagickTrue;
9052       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9053     }
9054
9055   else /* mng_info->write_pngNN not specified */
9056     {
9057       image_depth=ping_bit_depth;
9058
9059       if (mng_info->write_png_colortype != 0)
9060         {
9061           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9062
9063           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9064               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9065             image_matte=MagickTrue;
9066
9067           else
9068             image_matte=MagickFalse;
9069
9070           if (logging != MagickFalse)
9071              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9072              "   PNG colortype %d was specified:",(int) ping_color_type);
9073         }
9074
9075       else /* write_png_colortype not specified */
9076         {
9077           if (logging != MagickFalse)
9078              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9079              "  Selecting PNG colortype:");
9080
9081           ping_color_type=(png_byte) ((matte != MagickFalse)?
9082             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9083
9084           if (image_info->type == TrueColorType)
9085             {
9086               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9087               image_matte=MagickFalse;
9088             }
9089
9090           if (image_info->type == TrueColorMatteType)
9091             {
9092               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9093               image_matte=MagickTrue;
9094             }
9095
9096           if (image_info->type == PaletteType ||
9097               image_info->type == PaletteMatteType)
9098             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9099
9100           if (mng_info->write_png_colortype == 0 &&
9101              (image_info->type == UndefinedType ||
9102              image_info->type == OptimizeType))
9103             {
9104               if (ping_have_color == MagickFalse)
9105                 {
9106                   if (image_matte == MagickFalse)
9107                     {
9108                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9109                       image_matte=MagickFalse;
9110                     }
9111
9112                   else
9113                     {
9114                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9115                       image_matte=MagickTrue;
9116                     }
9117                 }
9118               else
9119                 {
9120                   if (image_matte == MagickFalse)
9121                     {
9122                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9123                       image_matte=MagickFalse;
9124                     }
9125
9126                   else
9127                     {
9128                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9129                       image_matte=MagickTrue;
9130                     }
9131                  }
9132             }
9133
9134         }
9135
9136       if (logging != MagickFalse)
9137          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9138          "    Selected PNG colortype=%d",ping_color_type);
9139
9140       if (ping_bit_depth < 8)
9141         {
9142           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9143               ping_color_type == PNG_COLOR_TYPE_RGB ||
9144               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9145             ping_bit_depth=8;
9146         }
9147
9148       old_bit_depth=ping_bit_depth;
9149
9150       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9151         {
9152           if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9153              ping_bit_depth=1;
9154         }
9155
9156       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9157         {
9158            size_t one = 1;
9159            ping_bit_depth=1;
9160
9161            if (image->colors == 0)
9162            {
9163               /* DO SOMETHING */
9164               (void) ThrowMagickException(&image->exception,
9165                  GetMagickModule(),CoderError,
9166                 "image has 0 colors", "`%s'","");
9167            }
9168
9169            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9170              ping_bit_depth <<= 1;
9171         }
9172
9173       if (logging != MagickFalse)
9174          {
9175            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9176             "    Number of colors: %.20g",(double) image_colors);
9177
9178            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9179             "    Tentative PNG bit depth: %d",ping_bit_depth);
9180          }
9181
9182       if (ping_bit_depth < (int) mng_info->write_png_depth)
9183          ping_bit_depth = mng_info->write_png_depth;
9184     }
9185
9186   image_depth=ping_bit_depth;
9187
9188   if (logging != MagickFalse)
9189     {
9190       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9191         "    Tentative PNG color type: %.20g",(double) ping_color_type);
9192
9193       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9194         "    image_info->type: %.20g",(double) image_info->type);
9195
9196       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9197         "    image_depth: %.20g",(double) image_depth);
9198
9199       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9200
9201         "    image->depth: %.20g",(double) image->depth);
9202
9203       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9204         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
9205     }
9206
9207   if (matte != MagickFalse)
9208     {
9209       if (mng_info->IsPalette)
9210         {
9211           if (mng_info->write_png_colortype == 0)
9212             {
9213               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9214
9215               if (ping_have_color != MagickFalse)
9216                  ping_color_type=PNG_COLOR_TYPE_RGBA;
9217             }
9218
9219           /*
9220            * Determine if there is any transparent color.
9221           */
9222           if (number_transparent + number_semitransparent == 0)
9223             {
9224               /*
9225                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
9226               */
9227
9228               image_matte=MagickFalse;
9229
9230               if (mng_info->write_png_colortype == 0)
9231                 ping_color_type&=0x03;
9232             }
9233
9234           else
9235             {
9236               unsigned int
9237                 mask;
9238
9239               mask=0xffff;
9240
9241               if (ping_bit_depth == 8)
9242                  mask=0x00ff;
9243
9244               if (ping_bit_depth == 4)
9245                  mask=0x000f;
9246
9247               if (ping_bit_depth == 2)
9248                  mask=0x0003;
9249
9250               if (ping_bit_depth == 1)
9251                  mask=0x0001;
9252
9253               ping_trans_color.red=(png_uint_16)
9254                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9255
9256               ping_trans_color.green=(png_uint_16)
9257                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9258
9259               ping_trans_color.blue=(png_uint_16)
9260                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9261
9262               ping_trans_color.gray=(png_uint_16)
9263                 (ScaleQuantumToShort(GetPixelPacketIntensity(
9264                    image->colormap)) & mask);
9265
9266               ping_trans_color.index=(png_byte) 0;
9267
9268               ping_have_tRNS=MagickTrue;
9269             }
9270
9271           if (ping_have_tRNS != MagickFalse)
9272             {
9273               /*
9274                * Determine if there is one and only one transparent color
9275                * and if so if it is fully transparent.
9276                */
9277               if (ping_have_cheap_transparency == MagickFalse)
9278                 ping_have_tRNS=MagickFalse;
9279             }
9280
9281           if (ping_have_tRNS != MagickFalse)
9282             {
9283               if (mng_info->write_png_colortype == 0)
9284                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
9285
9286               if (image_depth == 8)
9287                 {
9288                   ping_trans_color.red&=0xff;
9289                   ping_trans_color.green&=0xff;
9290                   ping_trans_color.blue&=0xff;
9291                   ping_trans_color.gray&=0xff;
9292                 }
9293             }
9294         }
9295       else
9296         {
9297           if (image_depth == 8)
9298             {
9299               ping_trans_color.red&=0xff;
9300               ping_trans_color.green&=0xff;
9301               ping_trans_color.blue&=0xff;
9302               ping_trans_color.gray&=0xff;
9303             }
9304         }
9305     }
9306
9307     matte=image_matte;
9308
9309     if (ping_have_tRNS != MagickFalse)
9310       image_matte=MagickFalse;
9311
9312     if ((mng_info->IsPalette) &&
9313         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9314         ping_have_color == MagickFalse &&
9315         (image_matte == MagickFalse || image_depth >= 8))
9316       {
9317         size_t one=1;
9318
9319         if (image_matte != MagickFalse)
9320           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9321
9322         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9323           {
9324             ping_color_type=PNG_COLOR_TYPE_GRAY;
9325
9326             if (save_image_depth == 16 && image_depth == 8)
9327               {
9328                 if (logging != MagickFalse)
9329                   {
9330                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9331                         "  Scaling ping_trans_color (0)");
9332                   }
9333                     ping_trans_color.gray*=0x0101;
9334               }
9335           }
9336
9337         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9338           image_depth=MAGICKCORE_QUANTUM_DEPTH;
9339
9340         if ((image_colors == 0) ||
9341              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9342           image_colors=(int) (one << image_depth);
9343
9344         if (image_depth > 8)
9345           ping_bit_depth=16;
9346
9347         else
9348           {
9349             ping_bit_depth=8;
9350             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9351               {
9352                 if(!mng_info->write_png_depth)
9353                   {
9354                     ping_bit_depth=1;
9355
9356                     while ((int) (one << ping_bit_depth)
9357                         < (ssize_t) image_colors)
9358                       ping_bit_depth <<= 1;
9359                   }
9360               }
9361
9362             else if (ping_color_type ==
9363                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9364                 mng_info->IsPalette)
9365               {
9366               /* Check if grayscale is reducible */
9367
9368                 int
9369                   depth_4_ok=MagickTrue,
9370                   depth_2_ok=MagickTrue,
9371                   depth_1_ok=MagickTrue;
9372
9373                 for (i=0; i < (ssize_t) image_colors; i++)
9374                 {
9375                    unsigned char
9376                      intensity;
9377
9378                    intensity=ScaleQuantumToChar(image->colormap[i].red);
9379
9380                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9381                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9382                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9383                      depth_2_ok=depth_1_ok=MagickFalse;
9384                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9385                      depth_1_ok=MagickFalse;
9386                 }
9387
9388                 if (depth_1_ok && mng_info->write_png_depth <= 1)
9389                   ping_bit_depth=1;
9390
9391                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9392                   ping_bit_depth=2;
9393
9394                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9395                   ping_bit_depth=4;
9396               }
9397           }
9398
9399           image_depth=ping_bit_depth;
9400       }
9401
9402     else
9403
9404       if (mng_info->IsPalette)
9405       {
9406         number_colors=image_colors;
9407
9408         if (image_depth <= 8)
9409           {
9410             /*
9411               Set image palette.
9412             */
9413             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9414
9415             if (mng_info->have_write_global_plte && matte == MagickFalse)
9416               {
9417                 png_set_PLTE(ping,ping_info,NULL,0);
9418
9419                 if (logging != MagickFalse)
9420                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9421                     "  Setting up empty PLTE chunk");
9422               }
9423
9424             else
9425               {
9426                 for (i=0; i < (ssize_t) number_colors; i++)
9427                 {
9428                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9429                   palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9430                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9431                 }
9432
9433                 if (logging != MagickFalse)
9434                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9435                     "  Setting up PLTE chunk with %d colors",
9436                     number_colors);
9437
9438                 ping_have_PLTE=MagickTrue;
9439               }
9440
9441             /* color_type is PNG_COLOR_TYPE_PALETTE */
9442             if (mng_info->write_png_depth == 0)
9443               {
9444                 size_t
9445                   one;
9446
9447                 ping_bit_depth=1;
9448                 one=1;
9449
9450                 while ((one << ping_bit_depth) < (ssize_t) number_colors)
9451                   ping_bit_depth <<= 1;
9452               }
9453
9454             ping_num_trans=0;
9455
9456             if (matte != MagickFalse)
9457               {
9458                 /*
9459                  * Set up trans_colors array.
9460                  */
9461                 assert(number_colors <= 256);
9462
9463                 ping_num_trans=(unsigned short) (number_transparent +
9464                   number_semitransparent);
9465
9466                 if (ping_num_trans == 0)
9467                   ping_have_tRNS=MagickFalse;
9468
9469                 else
9470                   {
9471                     if (logging != MagickFalse)
9472                       {
9473                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9474                           "  Scaling ping_trans_color (1)");
9475                       }
9476                     ping_have_tRNS=MagickTrue;
9477
9478                     for (i=0; i < ping_num_trans; i++)
9479                     {
9480                        ping_trans_alpha[i]= (png_byte) 
9481                          ScaleQuantumToChar(image->colormap[i].alpha);
9482                     }
9483                   }
9484               }
9485           }
9486       }
9487
9488     else
9489       {
9490
9491         if (image_depth < 8)
9492           image_depth=8;
9493
9494         if ((save_image_depth == 16) && (image_depth == 8))
9495           {
9496             if (logging != MagickFalse)
9497               {
9498                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9499                   "    Scaling ping_trans_color from (%d,%d,%d)",
9500                   (int) ping_trans_color.red,
9501                   (int) ping_trans_color.green,
9502                   (int) ping_trans_color.blue);
9503               }
9504
9505             ping_trans_color.red*=0x0101;
9506             ping_trans_color.green*=0x0101;
9507             ping_trans_color.blue*=0x0101;
9508             ping_trans_color.gray*=0x0101;
9509
9510             if (logging != MagickFalse)
9511               {
9512                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9513                   "    to (%d,%d,%d)",
9514                   (int) ping_trans_color.red,
9515                   (int) ping_trans_color.green,
9516                   (int) ping_trans_color.blue);
9517               }
9518           }
9519       }
9520
9521     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
9522          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
9523
9524     /*
9525       Adjust background and transparency samples in sub-8-bit grayscale files.
9526     */
9527     if (ping_bit_depth < 8 && ping_color_type ==
9528         PNG_COLOR_TYPE_GRAY)
9529       {
9530          png_uint_16
9531            maxval;
9532
9533          size_t
9534            one=1;
9535
9536          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
9537
9538          if (ping_exclude_bKGD == MagickFalse)
9539          {
9540
9541          ping_background.gray=(png_uint_16)
9542            ((maxval/255.)*((GetPixelPacketIntensity(&image->background_color)))
9543                                     +.5);
9544
9545          if (logging != MagickFalse)
9546            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9547              "  Setting up bKGD chunk (2)");
9548          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9549              "      background_color index is %d",
9550              (int) ping_background.index);
9551
9552          ping_have_bKGD = MagickTrue;
9553          }
9554
9555          if (logging != MagickFalse)
9556            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9557              "  Scaling ping_trans_color.gray from %d",
9558              (int)ping_trans_color.gray);
9559
9560          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
9561            ping_trans_color.gray)+.5);
9562
9563          if (logging != MagickFalse)
9564            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9565              "      to %d", (int)ping_trans_color.gray);
9566       }
9567
9568   if (ping_exclude_bKGD == MagickFalse)
9569   {
9570     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9571       {
9572         /*
9573            Identify which colormap entry is the background color.
9574         */
9575
9576         number_colors=image_colors;
9577
9578         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9579           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
9580             break;
9581
9582         ping_background.index=(png_byte) i;
9583
9584         if (logging != MagickFalse)
9585           {
9586             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9587               "  Setting up bKGD chunk with index=%d",(int) i);
9588           }
9589
9590         if (i < (ssize_t) number_colors)
9591           {
9592             ping_have_bKGD = MagickTrue;
9593
9594             if (logging != MagickFalse)
9595               {
9596                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9597                   "     background   =(%d,%d,%d)",
9598                         (int) ping_background.red,
9599                         (int) ping_background.green,
9600                         (int) ping_background.blue);
9601               }
9602           }
9603
9604         else  /* Can't happen */
9605           {
9606             if (logging != MagickFalse)
9607               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9608                   "      No room in PLTE to add bKGD color");
9609             ping_have_bKGD = MagickFalse;
9610           }
9611       }
9612   }
9613
9614   if (logging != MagickFalse)
9615     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9616       "    PNG color type: %d",ping_color_type);
9617   /*
9618     Initialize compression level and filtering.
9619   */
9620   if (logging != MagickFalse)
9621     {
9622       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9623         "  Setting up deflate compression");
9624
9625       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9626         "    Compression buffer size: 32768");
9627     }
9628
9629   png_set_compression_buffer_size(ping,32768L);
9630
9631   if (logging != MagickFalse)
9632     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9633       "    Compression mem level: 9");
9634
9635   /* Untangle the "-quality" setting:
9636
9637      Undefined is 0; the default is used.
9638      Default is 75
9639
9640      10's digit:
9641
9642         0: Use Z_HUFFMAN_ONLY strategy with the
9643            zlib default compression level
9644
9645         1-9: the zlib compression level
9646
9647      1's digit:
9648
9649         0-4: the PNG filter method
9650
9651         5:   libpng adaptive filtering if compression level > 5
9652              libpng filter type "none" if compression level <= 5
9653                 or if image is grayscale or palette
9654              
9655         6:   libpng adaptive filtering
9656
9657         7:   "LOCO" filtering (intrapixel differing) if writing
9658              a MNG, othewise "none".  Did not work in IM-6.7.0-9
9659              and earlier because of a missing "else".
9660
9661         8:   Z_RLE strategy, all filters
9662              Unused prior to IM-6.7.0-10, was same as 6
9663
9664         9:   Z_RLE strategy, no PNG filters
9665              Unused prior to IM-6.7.0-10, was same as 6
9666
9667     Note that using the -quality option, not all combinations of
9668     PNG filter type, zlib compression level, and zlib compression
9669     strategy are possible.  This will be addressed soon in a
9670     release that accomodates "-define PNG:compression-strategy",
9671     etc.
9672
9673    */
9674
9675   quality=image->quality == UndefinedCompressionQuality ? 75UL :
9676      image->quality;
9677
9678   if (quality <= 9)
9679     {
9680       if (mng_info->write_png_compression_strategy == 0)
9681         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9682     }
9683   
9684   else if (mng_info->write_png_compression_level == 0)
9685     {
9686       int
9687         level;
9688
9689       level=(int) MagickMin((ssize_t) quality/10,9);
9690
9691       mng_info->write_png_compression_level = level+1;
9692     }
9693
9694   if (mng_info->write_png_compression_strategy == 0)
9695     {
9696         if ((quality %10) == 8 || (quality %10) == 9)
9697             mng_info->write_png_compression_strategy=Z_RLE;
9698     }
9699
9700   if (mng_info->write_png_compression_filter == 0)
9701         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9702
9703   if (logging != MagickFalse)
9704     {
9705      if (mng_info->write_png_compression_level)
9706         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9707           "    Compression level:    %d",
9708             (int) mng_info->write_png_compression_level-1);
9709
9710      if (mng_info->write_png_compression_strategy)
9711         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9712           "    Compression strategy: %d",
9713             (int) mng_info->write_png_compression_strategy-1);
9714
9715         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9716           "  Setting up filtering");
9717
9718         if (mng_info->write_png_compression_filter == PNG_ALL_FILTERS+1)
9719           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9720             "    Base filter method: ADAPTIVE");
9721         else if (mng_info->write_png_compression_filter == PNG_NO_FILTERS+1)
9722           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9723             "    Base filter method: NONE");
9724         else
9725           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9726             "    Base filter method: %d",
9727             (int) mng_info->write_png_compression_filter-1);
9728     }
9729
9730   if (mng_info->write_png_compression_level != 0)
9731     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9732
9733   if (mng_info->write_png_compression_filter == 6)
9734     {
9735       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9736          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9737          (quality < 50))
9738         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9739       else
9740         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9741      }
9742
9743   if (mng_info->write_png_compression_filter == 7 ||
9744       mng_info->write_png_compression_filter == 10)
9745     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9746
9747   else if (mng_info->write_png_compression_filter == 8)
9748     {
9749 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9750       if (mng_info->write_mng)
9751       {
9752          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9753              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9754         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9755       }
9756 #endif
9757       png_set_filter(ping,PNG_FILTER_TYPE_BASE,0);
9758     }
9759
9760   else if (mng_info->write_png_compression_filter == 9)
9761     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9762
9763   else if (mng_info->write_png_compression_filter != 0)
9764     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9765        mng_info->write_png_compression_filter-1);
9766
9767   if (mng_info->write_png_compression_strategy != 0)
9768     png_set_compression_strategy(ping,
9769        mng_info->write_png_compression_strategy-1);
9770
9771
9772   if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9773      (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
9774     {
9775       ResetImageProfileIterator(image);
9776       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
9777       {
9778         profile=GetImageProfile(image,name);
9779
9780         if (profile != (StringInfo *) NULL)
9781           {
9782 #ifdef PNG_WRITE_iCCP_SUPPORTED
9783             if ((LocaleCompare(name,"ICC") == 0) ||
9784                 (LocaleCompare(name,"ICM") == 0))
9785              {
9786
9787                if (ping_exclude_iCCP == MagickFalse)
9788                  {
9789                        png_set_iCCP(ping,ping_info,(const png_charp) name,0,
9790 #if (PNG_LIBPNG_VER < 10500)
9791                          (png_charp) GetStringInfoDatum(profile),
9792 #else
9793                          (png_const_bytep) GetStringInfoDatum(profile),
9794 #endif
9795                          (png_uint_32) GetStringInfoLength(profile));
9796                  }
9797              }
9798
9799             else
9800 #endif
9801               if (ping_exclude_zCCP == MagickFalse)
9802                 {
9803                   Magick_png_write_raw_profile(image_info,ping,ping_info,
9804                     (unsigned char *) name,(unsigned char *) name,
9805                     GetStringInfoDatum(profile),
9806                     (png_uint_32) GetStringInfoLength(profile));
9807                 }
9808           }
9809
9810           if (logging != MagickFalse)
9811             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9812               "  Setting up text chunk with %s profile",name);
9813
9814         name=GetNextImageProfile(image);
9815       }
9816   }
9817
9818 #if defined(PNG_WRITE_sRGB_SUPPORTED)
9819   if ((mng_info->have_write_global_srgb == 0) &&
9820       ((image->rendering_intent != UndefinedIntent) ||
9821       (image->colorspace == sRGBColorspace)))
9822     {
9823       if (ping_exclude_sRGB == MagickFalse)
9824         {
9825           /*
9826             Note image rendering intent.
9827           */
9828           if (logging != MagickFalse)
9829             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9830                 "  Setting up sRGB chunk");
9831
9832           (void) png_set_sRGB(ping,ping_info,(
9833             Magick_RenderingIntent_to_PNG_RenderingIntent(
9834               image->rendering_intent)));
9835
9836           if (ping_exclude_gAMA == MagickFalse)
9837             png_set_gAMA(ping,ping_info,0.45455);
9838         }
9839     }
9840
9841   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
9842 #endif
9843     {
9844       if (ping_exclude_gAMA == MagickFalse &&
9845           (ping_exclude_sRGB == MagickFalse ||
9846           (image->gamma < .45 || image->gamma > .46)))
9847       {
9848       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9849         {
9850           /*
9851             Note image gamma.
9852             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9853           */
9854           if (logging != MagickFalse)
9855             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9856               "  Setting up gAMA chunk");
9857
9858           png_set_gAMA(ping,ping_info,image->gamma);
9859         }
9860       }
9861
9862       if (ping_exclude_cHRM == MagickFalse)
9863         {
9864           if ((mng_info->have_write_global_chrm == 0) &&
9865               (image->chromaticity.red_primary.x != 0.0))
9866             {
9867               /*
9868                 Note image chromaticity.
9869                 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9870               */
9871                PrimaryInfo
9872                  bp,
9873                  gp,
9874                  rp,
9875                  wp;
9876
9877                wp=image->chromaticity.white_point;
9878                rp=image->chromaticity.red_primary;
9879                gp=image->chromaticity.green_primary;
9880                bp=image->chromaticity.blue_primary;
9881
9882                if (logging != MagickFalse)
9883                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9884                    "  Setting up cHRM chunk");
9885
9886                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9887                    bp.x,bp.y);
9888            }
9889         }
9890     }
9891
9892   ping_interlace_method=image_info->interlace != NoInterlace;
9893
9894   if (mng_info->write_mng)
9895     png_set_sig_bytes(ping,8);
9896
9897   /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9898
9899   if (mng_info->write_png_colortype != 0)
9900     {
9901      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
9902        if (ping_have_color != MagickFalse)
9903          {
9904            ping_color_type = PNG_COLOR_TYPE_RGB;
9905
9906            if (ping_bit_depth < 8)
9907              ping_bit_depth=8;
9908          }
9909
9910      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
9911        if (ping_have_color != MagickFalse)
9912          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
9913     }
9914
9915   if (ping_need_colortype_warning != MagickFalse ||
9916      ((mng_info->write_png_depth &&
9917      (int) mng_info->write_png_depth != ping_bit_depth) ||
9918      (mng_info->write_png_colortype &&
9919      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
9920       mng_info->write_png_colortype != 7 &&
9921       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
9922     {
9923       if (logging != MagickFalse)
9924         {
9925           if (ping_need_colortype_warning != MagickFalse)
9926             {
9927               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9928                  "  Image has transparency but tRNS chunk was excluded");
9929             }
9930
9931           if (mng_info->write_png_depth)
9932             {
9933               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9934                   "  Defined PNG:bit-depth=%u, Computed depth=%u",
9935                   mng_info->write_png_depth,
9936                   ping_bit_depth);
9937             }
9938
9939           if (mng_info->write_png_colortype)
9940             {
9941               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9942                   "  Defined PNG:color-type=%u, Computed color type=%u",
9943                   mng_info->write_png_colortype-1,
9944                   ping_color_type);
9945             }
9946         }
9947
9948       png_warning(ping,
9949         "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9950     }
9951
9952   if (image_matte != MagickFalse && image->matte == MagickFalse)
9953     {
9954       /* Add an opaque matte channel */
9955       image->matte = MagickTrue;
9956       (void) SetImageOpacity(image,0);
9957
9958       if (logging != MagickFalse)
9959         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9960           "  Added an opaque matte channel");
9961     }
9962
9963   if (number_transparent != 0 || number_semitransparent != 0)
9964     {
9965       if (ping_color_type < 4)
9966         {
9967            ping_have_tRNS=MagickTrue;
9968            if (logging != MagickFalse)
9969              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9970                "  Setting ping_have_tRNS=MagickTrue.");
9971         }
9972     }
9973
9974   if (logging != MagickFalse)
9975     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9976       "  Writing PNG header chunks");
9977
9978   png_set_IHDR(ping,ping_info,ping_width,ping_height,
9979                ping_bit_depth,ping_color_type,
9980                ping_interlace_method,ping_compression_method,
9981                ping_filter_method);
9982
9983   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9984     {
9985       png_set_PLTE(ping,ping_info,palette,number_colors);
9986
9987       if (logging != MagickFalse)
9988         {
9989           for (i=0; i< (ssize_t) number_colors; i++)
9990           {
9991             if (i < ping_num_trans)
9992               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9993                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9994                       (int) i,
9995                       (int) palette[i].red,
9996                       (int) palette[i].green,
9997                       (int) palette[i].blue,
9998                       (int) i,
9999                       (int) ping_trans_alpha[i]);
10000              else
10001               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10002                 "     PLTE[%d] = (%d,%d,%d)",
10003                       (int) i,
10004                       (int) palette[i].red,
10005                       (int) palette[i].green,
10006                       (int) palette[i].blue);
10007            }
10008          }
10009     }
10010
10011   if (ping_exclude_bKGD == MagickFalse)
10012     {
10013       if (ping_have_bKGD != MagickFalse)
10014         {
10015           png_set_bKGD(ping,ping_info,&ping_background);
10016           if (logging)
10017             {
10018               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10019                    "    Setting up bKGD chunk");
10020               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10021                    "      background color = (%d,%d,%d)",
10022                         (int) ping_background.red,
10023                         (int) ping_background.green,
10024                         (int) ping_background.blue);
10025               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10026                    "      index = %d, gray=%d",
10027                         (int) ping_background.index,
10028                         (int) ping_background.gray);
10029             }
10030          }
10031     }
10032
10033   if (ping_exclude_pHYs == MagickFalse)
10034     {
10035       if (ping_have_pHYs != MagickFalse)
10036         {
10037           png_set_pHYs(ping,ping_info,
10038              ping_pHYs_x_resolution,
10039              ping_pHYs_y_resolution,
10040              ping_pHYs_unit_type);
10041
10042           if (logging)
10043             {
10044               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10045                    "    Setting up pHYs chunk");
10046               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10047                    "      x_resolution=%lu",
10048                    (unsigned long) ping_pHYs_x_resolution);
10049               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10050                    "      y_resolution=%lu",
10051                    (unsigned long) ping_pHYs_y_resolution);
10052               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10053                    "      unit_type=%lu",
10054                    (unsigned long) ping_pHYs_unit_type);
10055             }
10056         }
10057     }
10058
10059 #if defined(PNG_oFFs_SUPPORTED)
10060   if (ping_exclude_oFFs == MagickFalse)
10061     {
10062       if (image->page.x || image->page.y)
10063         {
10064            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10065               (png_int_32) image->page.y, 0);
10066
10067            if (logging != MagickFalse)
10068              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10069                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
10070                  (int) image->page.x, (int) image->page.y);
10071         }
10072     }
10073 #endif
10074
10075   if (mng_info->need_blob != MagickFalse)
10076   {
10077     if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10078        MagickFalse)
10079        png_error(ping,"WriteBlob Failed");
10080
10081      ping_have_blob=MagickTrue;
10082   }
10083
10084   png_write_info_before_PLTE(ping, ping_info);
10085
10086   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10087     {
10088       if (logging != MagickFalse)
10089         {
10090           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10091               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10092         }
10093
10094       if (ping_color_type == 3)
10095          (void) png_set_tRNS(ping, ping_info,
10096                 ping_trans_alpha,
10097                 ping_num_trans,
10098                 NULL);
10099
10100       else
10101         {
10102            (void) png_set_tRNS(ping, ping_info,
10103                   NULL,
10104                   0,
10105                   &ping_trans_color);
10106
10107            if (logging != MagickFalse)
10108              {
10109                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10110                  "     tRNS color   =(%d,%d,%d)",
10111                        (int) ping_trans_color.red,
10112                        (int) ping_trans_color.green,
10113                        (int) ping_trans_color.blue);
10114              }
10115          }
10116     }
10117
10118   /* write any png-chunk-b profiles */
10119   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10120
10121   png_write_info(ping,ping_info);
10122
10123   /* write any PNG-chunk-m profiles */
10124   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10125
10126   if (ping_exclude_vpAg == MagickFalse)
10127     {
10128       if ((image->page.width != 0 && image->page.width != image->columns) ||
10129           (image->page.height != 0 && image->page.height != image->rows))
10130         {
10131           unsigned char
10132             chunk[14];
10133
10134           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
10135           PNGType(chunk,mng_vpAg);
10136           LogPNGChunk(logging,mng_vpAg,9L);
10137           PNGLong(chunk+4,(png_uint_32) image->page.width);
10138           PNGLong(chunk+8,(png_uint_32) image->page.height);
10139           chunk[12]=0;   /* unit = pixels */
10140           (void) WriteBlob(image,13,chunk);
10141           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10142         }
10143     }
10144
10145 #if (PNG_LIBPNG_VER == 10206)
10146     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10147 #define PNG_HAVE_IDAT               0x04
10148     ping->mode |= PNG_HAVE_IDAT;
10149 #undef PNG_HAVE_IDAT
10150 #endif
10151
10152   png_set_packing(ping);
10153   /*
10154     Allocate memory.
10155   */
10156   rowbytes=image->columns;
10157   if (image_depth > 8)
10158     rowbytes*=2;
10159   switch (ping_color_type)
10160     {
10161       case PNG_COLOR_TYPE_RGB:
10162         rowbytes*=3;
10163         break;
10164
10165       case PNG_COLOR_TYPE_GRAY_ALPHA:
10166         rowbytes*=2;
10167         break;
10168
10169       case PNG_COLOR_TYPE_RGBA:
10170         rowbytes*=4;
10171         break;
10172
10173       default:
10174         break;
10175     }
10176
10177   if (logging != MagickFalse)
10178     {
10179       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10180         "  Writing PNG image data");
10181
10182       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10183         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10184     }
10185   ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10186     sizeof(*ping_pixels));
10187
10188   if (ping_pixels == (unsigned char *) NULL)
10189     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10190
10191   /*
10192     Initialize image scanlines.
10193   */
10194   if (setjmp(png_jmpbuf(ping)))
10195     {
10196       /*
10197         PNG write failed.
10198       */
10199 #ifdef PNG_DEBUG
10200      if (image_info->verbose)
10201         (void) printf("PNG write has failed.\n");
10202 #endif
10203       png_destroy_write_struct(&ping,&ping_info);
10204       if (quantum_info != (QuantumInfo *) NULL)
10205         quantum_info=DestroyQuantumInfo(quantum_info);
10206       if (ping_pixels != (unsigned char *) NULL)
10207         ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10208 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10209       UnlockSemaphoreInfo(ping_semaphore);
10210 #endif
10211       if (ping_have_blob != MagickFalse)
10212           (void) CloseBlob(image);
10213       image_info=DestroyImageInfo(image_info);
10214       image=DestroyImage(image);
10215       return(MagickFalse);
10216     }
10217   quantum_info=AcquireQuantumInfo(image_info,image);
10218   if (quantum_info == (QuantumInfo *) NULL)
10219     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10220   quantum_info->format=UndefinedQuantumFormat;
10221   quantum_info->depth=image_depth;
10222   num_passes=png_set_interlace_handling(ping);
10223
10224   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10225        !mng_info->write_png32) &&
10226        (mng_info->IsPalette ||
10227        (image_info->type == BilevelType)) &&
10228        image_matte == MagickFalse &&
10229        ping_have_non_bw == MagickFalse)
10230     {
10231       /* Palette, Bilevel, or Opaque Monochrome */
10232       register const Quantum
10233         *p;
10234
10235       quantum_info->depth=8;
10236       for (pass=0; pass < num_passes; pass++)
10237       {
10238         /*
10239           Convert PseudoClass image to a PNG monochrome image.
10240         */
10241         for (y=0; y < (ssize_t) image->rows; y++)
10242         {
10243           if (logging != MagickFalse && y == 0)
10244              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10245                  "    Writing row of pixels (0)");
10246
10247           p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
10248
10249           if (p == (const Quantum *) NULL)
10250             break;
10251
10252           if (mng_info->IsPalette)
10253             {
10254               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10255                 quantum_info,GrayQuantum,ping_pixels,&image->exception);
10256               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10257                   mng_info->write_png_depth &&
10258                   mng_info->write_png_depth != old_bit_depth)
10259                 {
10260                   /* Undo pixel scaling */
10261                   for (i=0; i < (ssize_t) image->columns; i++)
10262                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10263                      >> (8-old_bit_depth));
10264                 }
10265             }
10266
10267           else
10268             {
10269               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10270                 quantum_info,RedQuantum,ping_pixels,&image->exception);
10271             }
10272
10273           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10274             for (i=0; i < (ssize_t) image->columns; i++)
10275                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10276                       255 : 0);
10277
10278           if (logging != MagickFalse && y == 0)
10279             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10280                 "    Writing row of pixels (1)");
10281
10282           png_write_row(ping,ping_pixels);
10283         }
10284         if (image->previous == (Image *) NULL)
10285           {
10286             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10287             if (status == MagickFalse)
10288               break;
10289           }
10290       }
10291     }
10292
10293   else   /* Not Palette, Bilevel, or Opaque Monochrome */
10294     {
10295       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10296          !mng_info->write_png32) &&
10297          (image_matte != MagickFalse ||
10298          (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10299          (mng_info->IsPalette) && ping_have_color == MagickFalse)
10300         {
10301           register const Quantum
10302             *p;
10303
10304           for (pass=0; pass < num_passes; pass++)
10305           {
10306
10307           for (y=0; y < (ssize_t) image->rows; y++)
10308           {
10309             p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
10310
10311             if (p == (const Quantum *) NULL)
10312               break;
10313
10314             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10315               {
10316                 if (mng_info->IsPalette)
10317                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10318                     quantum_info,GrayQuantum,ping_pixels,&image->exception);
10319
10320                 else
10321                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10322                     quantum_info,RedQuantum,ping_pixels,&image->exception);
10323
10324                 if (logging != MagickFalse && y == 0)
10325                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10326                        "    Writing GRAY PNG pixels (2)");
10327               }
10328
10329             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10330               {
10331                 if (logging != MagickFalse && y == 0)
10332                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10333                          "    Writing GRAY_ALPHA PNG pixels (2)");
10334
10335                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10336                   quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
10337               }
10338
10339             if (logging != MagickFalse && y == 0)
10340               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10341                   "    Writing row of pixels (2)");
10342
10343             png_write_row(ping,ping_pixels);
10344           }
10345
10346           if (image->previous == (Image *) NULL)
10347             {
10348               status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10349               if (status == MagickFalse)
10350                 break;
10351             }
10352           }
10353         }
10354
10355       else
10356         {
10357           register const Quantum
10358             *p;
10359
10360           for (pass=0; pass < num_passes; pass++)
10361           {
10362             if ((image_depth > 8) || (mng_info->write_png24 ||
10363                 mng_info->write_png32 ||
10364                 (!mng_info->write_png8 && !mng_info->IsPalette)))
10365             {
10366               for (y=0; y < (ssize_t) image->rows; y++)
10367               {
10368                 p=GetVirtualPixels(image,0,y,image->columns,1,
10369                    &image->exception);
10370
10371                 if (p == (const Quantum *) NULL)
10372                   break;
10373
10374                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10375                   {
10376                     if (image->storage_class == DirectClass)
10377                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
10378                         quantum_info,RedQuantum,ping_pixels,&image->exception);
10379
10380                     else
10381                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
10382                         quantum_info,GrayQuantum,ping_pixels,&image->exception);
10383                   }
10384
10385                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10386                   {
10387                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
10388                       quantum_info,GrayAlphaQuantum,ping_pixels,
10389                       &image->exception);
10390
10391                     if (logging != MagickFalse && y == 0)
10392                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10393                            "    Writing GRAY_ALPHA PNG pixels (3)");
10394                   }
10395
10396                 else if (image_matte != MagickFalse)
10397                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10398                     quantum_info,RGBAQuantum,ping_pixels,&image->exception);
10399
10400                 else
10401                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10402                     quantum_info,RGBQuantum,ping_pixels,&image->exception);
10403
10404                 if (logging != MagickFalse && y == 0)
10405                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10406                       "    Writing row of pixels (3)");
10407
10408                 png_write_row(ping,ping_pixels);
10409               }
10410             }
10411
10412           else
10413             /* not ((image_depth > 8) || (mng_info->write_png24 ||
10414                 mng_info->write_png32 ||
10415                 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10416             {
10417               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10418                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10419                 {
10420                   if (logging != MagickFalse)
10421                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10422                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
10423
10424                   quantum_info->depth=8;
10425                   image_depth=8;
10426                 }
10427
10428               for (y=0; y < (ssize_t) image->rows; y++)
10429               {
10430                 if (logging != MagickFalse && y == 0)
10431                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10432                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
10433
10434                 p=GetVirtualPixels(image,0,y,image->columns,1,
10435                    &image->exception);
10436
10437                 if (p == (const Quantum *) NULL)
10438                   break;
10439
10440                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10441                   {
10442                     quantum_info->depth=image->depth;
10443
10444                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
10445                        quantum_info,GrayQuantum,ping_pixels,&image->exception);
10446                   }
10447
10448                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10449                   {
10450                     if (logging != MagickFalse && y == 0)
10451                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10452                            "  Writing GRAY_ALPHA PNG pixels (4)");
10453
10454                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
10455                          quantum_info,GrayAlphaQuantum,ping_pixels,
10456                          &image->exception);
10457                   }
10458
10459                 else
10460                   {
10461                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
10462                       quantum_info,IndexQuantum,ping_pixels,&image->exception);
10463
10464                     if (logging != MagickFalse && y <= 2)
10465                     {
10466                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10467                           "  Writing row of non-gray pixels (4)");
10468
10469                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10470                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
10471                           (int)ping_pixels[0],(int)ping_pixels[1]);
10472                     }
10473                   }
10474                 png_write_row(ping,ping_pixels);
10475               }
10476             }
10477
10478             if (image->previous == (Image *) NULL)
10479               {
10480                 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10481                 if (status == MagickFalse)
10482                   break;
10483               }
10484           }
10485         }
10486     }
10487
10488   if (quantum_info != (QuantumInfo *) NULL)
10489     quantum_info=DestroyQuantumInfo(quantum_info);
10490
10491   if (logging != MagickFalse)
10492     {
10493       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10494         "  Wrote PNG image data");
10495
10496       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10497         "    Width: %.20g",(double) ping_width);
10498
10499       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10500         "    Height: %.20g",(double) ping_height);
10501
10502       if (mng_info->write_png_depth)
10503         {
10504           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10505             "    Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10506         }
10507
10508       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10509         "    PNG bit-depth written: %d",ping_bit_depth);
10510
10511       if (mng_info->write_png_colortype)
10512         {
10513           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10514             "    Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10515         }
10516
10517       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10518         "    PNG color-type written: %d",ping_color_type);
10519
10520       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10521         "    PNG Interlace method: %d",ping_interlace_method);
10522     }
10523   /*
10524     Generate text chunks after IDAT.
10525   */
10526   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
10527   {
10528     ResetImagePropertyIterator(image);
10529     property=GetNextImageProperty(image);
10530     while (property != (const char *) NULL)
10531     {
10532       png_textp
10533         text;
10534
10535       value=GetImageProperty(image,property);
10536
10537       /* Don't write any "png:" properties; those are just for "identify" */
10538       if (LocaleNCompare(property,"png:",4) != 0 &&
10539
10540           /* Suppress density and units if we wrote a pHYs chunk */
10541           (ping_exclude_pHYs != MagickFalse      ||
10542           LocaleCompare(property,"density") != 0 ||
10543           LocaleCompare(property,"units") != 0) &&
10544
10545           /* Suppress the IM-generated Date:create and Date:modify */
10546           (ping_exclude_date == MagickFalse      ||
10547           LocaleNCompare(property, "Date:",5) != 0))
10548         {
10549         if (value != (const char *) NULL)
10550           {
10551             text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10552             text[0].key=(char *) property;
10553             text[0].text=(char *) value;
10554             text[0].text_length=strlen(value);
10555
10556             if (ping_exclude_tEXt != MagickFalse)
10557                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10558
10559             else if (ping_exclude_zTXt != MagickFalse)
10560                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10561
10562             else
10563             {
10564                text[0].compression=image_info->compression == NoCompression ||
10565                  (image_info->compression == UndefinedCompression &&
10566                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10567                  PNG_TEXT_COMPRESSION_zTXt ;
10568             }
10569
10570             if (logging != MagickFalse)
10571               {
10572                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10573                   "  Setting up text chunk");
10574
10575                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10576                   "    keyword: %s",text[0].key);
10577               }
10578
10579             png_set_text(ping,ping_info,text,1);
10580             png_free(ping,text);
10581           }
10582         }
10583       property=GetNextImageProperty(image);
10584     }
10585   }
10586
10587   /* write any PNG-chunk-e profiles */
10588   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
10589
10590   if (logging != MagickFalse)
10591     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10592       "  Writing PNG end info");
10593
10594   png_write_end(ping,ping_info);
10595
10596   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10597     {
10598       if (mng_info->page.x || mng_info->page.y ||
10599           (ping_width != mng_info->page.width) ||
10600           (ping_height != mng_info->page.height))
10601         {
10602           unsigned char
10603             chunk[32];
10604
10605           /*
10606             Write FRAM 4 with clipping boundaries followed by FRAM 1.
10607           */
10608           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
10609           PNGType(chunk,mng_FRAM);
10610           LogPNGChunk(logging,mng_FRAM,27L);
10611           chunk[4]=4;
10612           chunk[5]=0;  /* frame name separator (no name) */
10613           chunk[6]=1;  /* flag for changing delay, for next frame only */
10614           chunk[7]=0;  /* flag for changing frame timeout */
10615           chunk[8]=1;  /* flag for changing frame clipping for next frame */
10616           chunk[9]=0;  /* flag for changing frame sync_id */
10617           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10618           chunk[14]=0; /* clipping boundaries delta type */
10619           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10620           PNGLong(chunk+19,
10621              (png_uint_32) (mng_info->page.x + ping_width));
10622           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10623           PNGLong(chunk+27,
10624              (png_uint_32) (mng_info->page.y + ping_height));
10625           (void) WriteBlob(image,31,chunk);
10626           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10627           mng_info->old_framing_mode=4;
10628           mng_info->framing_mode=1;
10629         }
10630
10631       else
10632         mng_info->framing_mode=3;
10633     }
10634   if (mng_info->write_mng && !mng_info->need_fram &&
10635       ((int) image->dispose == 3))
10636      (void) ThrowMagickException(&image->exception,GetMagickModule(),
10637        CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
10638        "`%s'",image->filename);
10639
10640   /*
10641     Free PNG resources.
10642   */
10643
10644   png_destroy_write_struct(&ping,&ping_info);
10645
10646   ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
10647
10648 #if defined(PNG_SETJMP_NOT_THREAD_SAFE)
10649   UnlockSemaphoreInfo(ping_semaphore);
10650 #endif
10651
10652   if (ping_have_blob != MagickFalse)
10653      (void) CloseBlob(image);
10654
10655   image_info=DestroyImageInfo(image_info);
10656   image=DestroyImage(image);
10657
10658   /* Store bit depth actually written */
10659   s[0]=(char) ping_bit_depth;
10660   s[1]='\0';
10661
10662   (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10663
10664   if (logging != MagickFalse)
10665     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10666       "  exit WriteOnePNGImage()");
10667
10668   return(MagickTrue);
10669 /*  End write one PNG image */
10670 }
10671
10672 /*
10673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10674 %                                                                             %
10675 %                                                                             %
10676 %                                                                             %
10677 %   W r i t e P N G I m a g e                                                 %
10678 %                                                                             %
10679 %                                                                             %
10680 %                                                                             %
10681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10682 %
10683 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
10684 %  Multiple-image Network Graphics (MNG) image file.
10685 %
10686 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
10687 %
10688 %  The format of the WritePNGImage method is:
10689 %
10690 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10691 %
10692 %  A description of each parameter follows:
10693 %
10694 %    o image_info: the image info.
10695 %
10696 %    o image:  The image.
10697 %
10698 %  Returns MagickTrue on success, MagickFalse on failure.
10699 %
10700 %  Communicating with the PNG encoder:
10701 %
10702 %  While the datastream written is always in PNG format and normally would
10703 %  be given the "png" file extension, this method also writes the following
10704 %  pseudo-formats which are subsets of PNG:
10705 %
10706 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
10707 %               a depth greater than 8, the depth is reduced. If transparency
10708 %               is present, the tRNS chunk must only have values 0 and 255
10709 %               (i.e., transparency is binary: fully opaque or fully
10710 %               transparent).  If other values are present they will be
10711 %               50%-thresholded to binary transparency.  If more than 256
10712 %               colors are present, they will be quantized to the 4-4-4-1,
10713 %               3-3-3-1, or  3-3-2-1 palette.
10714 %
10715 %               If you want better quantization or dithering of the colors
10716 %               or alpha than that, you need to do it before calling the
10717 %               PNG encoder. The pixels contain 8-bit indices even if
10718 %               they could be represented with 1, 2, or 4 bits.  Grayscale
10719 %               images will be written as indexed PNG files even though the
10720 %               PNG grayscale type might be slightly more efficient.  Please
10721 %               note that writing to the PNG8 format may result in loss
10722 %               of color and alpha data.
10723 %
10724 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
10725 %               chunk can be present to convey binary transparency by naming
10726 %               one of the colors as transparent.  The only loss incurred
10727 %               is reduction of sample depth to 8.  If the image has more
10728 %               than one transparent color, has semitransparent pixels, or
10729 %               has an opaque pixel with the same RGB components as the
10730 %               transparent color, an image is not written.
10731 %
10732 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
10733 %               transparency is permitted, i.e., the alpha sample for
10734 %               each pixel can have any value from 0 to 255. The alpha
10735 %               channel is present even if the image is fully opaque.
10736 %               The only loss in data is the reduction of the sample depth
10737 %               to 8.
10738 %
10739 %    o -define: For more precise control of the PNG output, you can use the
10740 %               Image options "png:bit-depth" and "png:color-type".  These
10741 %               can be set from the commandline with "-define" and also
10742 %               from the application programming interfaces.  The options
10743 %               are case-independent and are converted to lowercase before
10744 %               being passed to this encoder.
10745 %
10746 %               png:color-type can be 0, 2, 3, 4, or 6.
10747 %
10748 %               When png:color-type is 0 (Grayscale), png:bit-depth can
10749 %               be 1, 2, 4, 8, or 16.
10750 %
10751 %               When png:color-type is 2 (RGB), png:bit-depth can
10752 %               be 8 or 16.
10753 %
10754 %               When png:color-type is 3 (Indexed), png:bit-depth can
10755 %               be 1, 2, 4, or 8.  This refers to the number of bits
10756 %               used to store the index.  The color samples always have
10757 %               bit-depth 8 in indexed PNG files.
10758 %
10759 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10760 %               png:bit-depth can be 8 or 16.
10761 %
10762 %  If the image cannot be written without loss with the requested bit-depth
10763 %  and color-type, a PNG file will not be written, and the encoder will
10764 %  return MagickFalse.
10765 %
10766 %  Since image encoders should not be responsible for the "heavy lifting",
10767 %  the user should make sure that ImageMagick has already reduced the
10768 %  image depth and number of colors and limit transparency to binary
10769 %  transparency prior to attempting to write the image with depth, color,
10770 %  or transparency limitations.
10771 %
10772 %  Note that another definition, "png:bit-depth-written" exists, but it
10773 %  is not intended for external use.  It is only used internally by the
10774 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10775 %
10776 %  It is possible to request that the PNG encoder write previously-formatted
10777 %  ancillary chunks in the output PNG file, using the "-profile" commandline
10778 %  option as shown below or by setting the profile via a programming
10779 %  interface:
10780 %
10781 %     -profile PNG-chunk-x:<file>
10782 %
10783 %  where x is a location flag and <file> is a file containing the chunk
10784 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
10785 %  This encoder will compute the chunk length and CRC, so those must not
10786 %  be included in the file.
10787 %
10788 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10789 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
10790 %  of the same type, then add a short unique string after the "x" to prevent
10791 %  subsequent profiles from overwriting the preceding ones, e.g.,
10792 %
10793 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
10794 %
10795 %  As of version 6.6.6 the following optimizations are always done:
10796 %
10797 %   o  32-bit depth is reduced to 16.
10798 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
10799 %      high byte and low byte are identical.
10800 %   o  Palette is sorted to remove unused entries and to put a
10801 %      transparent color first, if BUILD_PNG_PALETTE is defined.
10802 %   o  Opaque matte channel is removed when writing an indexed PNG.
10803 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
10804 %      this can be done without loss and a larger bit depth N was not
10805 %      requested via the "-define PNG:bit-depth=N" option.
10806 %   o  If matte channel is present but only one transparent color is
10807 %      present, RGB+tRNS is written instead of RGBA
10808 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
10809 %      was requested when converting an opaque image).
10810 %
10811 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10812 */
10813 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10814   Image *image)
10815 {
10816   MagickBooleanType
10817     excluding,
10818     logging,
10819     have_mng_structure,
10820     status;
10821
10822   MngInfo
10823     *mng_info;
10824
10825   const char
10826     *value;
10827
10828   int
10829     i,
10830     source;
10831
10832   /*
10833     Open image file.
10834   */
10835   assert(image_info != (const ImageInfo *) NULL);
10836   assert(image_info->signature == MagickSignature);
10837   assert(image != (Image *) NULL);
10838   assert(image->signature == MagickSignature);
10839   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
10840   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
10841   /*
10842     Allocate a MngInfo structure.
10843   */
10844   have_mng_structure=MagickFalse;
10845   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
10846
10847   if (mng_info == (MngInfo *) NULL)
10848     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
10849
10850   /*
10851     Initialize members of the MngInfo structure.
10852   */
10853   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10854   mng_info->image=image;
10855   mng_info->equal_backgrounds=MagickTrue;
10856   have_mng_structure=MagickTrue;
10857
10858   /* See if user has requested a specific PNG subformat */
10859
10860   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10861   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10862   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10863
10864   if (mng_info->write_png8)
10865     {
10866       mng_info->write_png_colortype = /* 3 */ 4;
10867       mng_info->write_png_depth = 8;
10868       image->depth = 8;
10869     }
10870
10871   if (mng_info->write_png24)
10872     {
10873       mng_info->write_png_colortype = /* 2 */ 3;
10874       mng_info->write_png_depth = 8;
10875       image->depth = 8;
10876
10877       if (image->matte == MagickTrue)
10878         (void) SetImageType(image,TrueColorMatteType);
10879
10880       else
10881         (void) SetImageType(image,TrueColorType);
10882
10883       (void) SyncImage(image);
10884     }
10885
10886   if (mng_info->write_png32)
10887     {
10888       mng_info->write_png_colortype = /* 6 */  7;
10889       mng_info->write_png_depth = 8;
10890       image->depth = 8;
10891
10892       if (image->matte == MagickTrue)
10893         (void) SetImageType(image,TrueColorMatteType);
10894
10895       else
10896         (void) SetImageType(image,TrueColorType);
10897
10898       (void) SyncImage(image);
10899     }
10900
10901   value=GetImageOption(image_info,"png:bit-depth");
10902
10903   if (value != (char *) NULL)
10904     {
10905       if (LocaleCompare(value,"1") == 0)
10906         mng_info->write_png_depth = 1;
10907
10908       else if (LocaleCompare(value,"2") == 0)
10909         mng_info->write_png_depth = 2;
10910
10911       else if (LocaleCompare(value,"4") == 0)
10912         mng_info->write_png_depth = 4;
10913
10914       else if (LocaleCompare(value,"8") == 0)
10915         mng_info->write_png_depth = 8;
10916
10917       else if (LocaleCompare(value,"16") == 0)
10918         mng_info->write_png_depth = 16;
10919
10920       else
10921         (void) ThrowMagickException(&image->exception,
10922              GetMagickModule(),CoderWarning,
10923              "ignoring invalid defined png:bit-depth",
10924              "=%s",value);
10925
10926       if (logging != MagickFalse)
10927         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10928           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
10929     }
10930
10931   value=GetImageOption(image_info,"png:color-type");
10932
10933   if (value != (char *) NULL)
10934     {
10935       /* We must store colortype+1 because 0 is a valid colortype */
10936       if (LocaleCompare(value,"0") == 0)
10937         mng_info->write_png_colortype = 1;
10938
10939       else if (LocaleCompare(value,"2") == 0)
10940         mng_info->write_png_colortype = 3;
10941
10942       else if (LocaleCompare(value,"3") == 0)
10943         mng_info->write_png_colortype = 4;
10944
10945       else if (LocaleCompare(value,"4") == 0)
10946         mng_info->write_png_colortype = 5;
10947
10948       else if (LocaleCompare(value,"6") == 0)
10949         mng_info->write_png_colortype = 7;
10950
10951       else
10952         (void) ThrowMagickException(&image->exception,
10953              GetMagickModule(),CoderWarning,
10954              "ignoring invalid defined png:color-type",
10955              "=%s",value);
10956
10957       if (logging != MagickFalse)
10958         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10959           "  png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
10960     }
10961
10962   /* Check for chunks to be excluded:
10963    *
10964    * The default is to not exclude any known chunks except for any
10965    * listed in the "unused_chunks" array, above.
10966    *
10967    * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10968    * define (in the image properties or in the image artifacts)
10969    * or via a mng_info member.  For convenience, in addition
10970    * to or instead of a comma-separated list of chunks, the
10971    * "exclude-chunk" string can be simply "all" or "none".
10972    *
10973    * The exclude-chunk define takes priority over the mng_info.
10974    *
10975    * A "PNG:include-chunk" define takes  priority over both the
10976    * mng_info and the "PNG:exclude-chunk" define.  Like the
10977    * "exclude-chunk" string, it can define "all" or "none" as
10978    * well as a comma-separated list.  Chunks that are unknown to
10979    * ImageMagick are always excluded, regardless of their "copy-safe"
10980    * status according to the PNG specification, and even if they
10981    * appear in the "include-chunk" list.
10982    *
10983    * Finally, all chunks listed in the "unused_chunks" array are
10984    * automatically excluded, regardless of the other instructions
10985    * or lack thereof.
10986    *
10987    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10988    * will not be written and the gAMA chunk will only be written if it
10989    * is not between .45 and .46, or approximately (1.0/2.2).
10990    *
10991    * If you exclude tRNS and the image has transparency, the colortype
10992    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10993    *
10994    * The -strip option causes StripImage() to set the png:include-chunk
10995    * artifact to "none,gama".
10996    */
10997
10998   mng_info->ping_exclude_bKGD=MagickFalse;
10999   mng_info->ping_exclude_cHRM=MagickFalse;
11000   mng_info->ping_exclude_date=MagickFalse;
11001   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11002   mng_info->ping_exclude_gAMA=MagickFalse;
11003   mng_info->ping_exclude_iCCP=MagickFalse;
11004   /* mng_info->ping_exclude_iTXt=MagickFalse; */
11005   mng_info->ping_exclude_oFFs=MagickFalse;
11006   mng_info->ping_exclude_pHYs=MagickFalse;
11007   mng_info->ping_exclude_sRGB=MagickFalse;
11008   mng_info->ping_exclude_tEXt=MagickFalse;
11009   mng_info->ping_exclude_tRNS=MagickFalse;
11010   mng_info->ping_exclude_vpAg=MagickFalse;
11011   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11012   mng_info->ping_exclude_zTXt=MagickFalse;
11013
11014   mng_info->ping_preserve_colormap=MagickFalse;
11015
11016   value=GetImageArtifact(image,"png:preserve-colormap");
11017   if (value == NULL)
11018      value=GetImageOption(image_info,"png:preserve-colormap");
11019   if (value != NULL)
11020      mng_info->ping_preserve_colormap=MagickTrue;
11021
11022   /* Thes compression-level, compression-strategy, and compression-filter
11023    * defines take precedence over values from the -quality option.
11024    */
11025   value=GetImageArtifact(image,"png:compression-level");
11026   if (value == NULL)
11027      value=GetImageOption(image_info,"png:compression-level");
11028   if (value != NULL)
11029   {
11030       /* We have to add 1 to everything because 0 is a valid input,
11031        * and we want to use 0 (the default) to mean undefined.
11032        */
11033       if (LocaleCompare(value,"0") == 0)
11034         mng_info->write_png_compression_level = 1;
11035
11036       if (LocaleCompare(value,"1") == 0)
11037         mng_info->write_png_compression_level = 2;
11038
11039       else if (LocaleCompare(value,"2") == 0)
11040         mng_info->write_png_compression_level = 3;
11041
11042       else if (LocaleCompare(value,"3") == 0)
11043         mng_info->write_png_compression_level = 4;
11044
11045       else if (LocaleCompare(value,"4") == 0)
11046         mng_info->write_png_compression_level = 5;
11047
11048       else if (LocaleCompare(value,"5") == 0)
11049         mng_info->write_png_compression_level = 6;
11050
11051       else if (LocaleCompare(value,"6") == 0)
11052         mng_info->write_png_compression_level = 7;
11053
11054       else if (LocaleCompare(value,"7") == 0)
11055         mng_info->write_png_compression_level = 8;
11056
11057       else if (LocaleCompare(value,"8") == 0)
11058         mng_info->write_png_compression_level = 9;
11059
11060       else if (LocaleCompare(value,"9") == 0)
11061         mng_info->write_png_compression_level = 10;
11062
11063       else
11064         (void) ThrowMagickException(&image->exception,
11065              GetMagickModule(),CoderWarning,
11066              "ignoring invalid defined png:compression-level",
11067              "=%s",value);
11068     }
11069
11070   value=GetImageArtifact(image,"png:compression-strategy");
11071   if (value == NULL)
11072      value=GetImageOption(image_info,"png:compression-strategy");
11073   if (value != NULL)
11074   {
11075
11076       if (LocaleCompare(value,"0") == 0)
11077         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11078
11079       else if (LocaleCompare(value,"1") == 0)
11080         mng_info->write_png_compression_strategy = Z_FILTERED+1;
11081
11082       else if (LocaleCompare(value,"2") == 0)
11083         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11084
11085       else if (LocaleCompare(value,"3") == 0)
11086 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
11087         mng_info->write_png_compression_strategy = Z_RLE+1;
11088 #else
11089         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11090 #endif
11091
11092       else if (LocaleCompare(value,"4") == 0)
11093 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
11094         mng_info->write_png_compression_strategy = Z_FIXED+1;
11095 #else
11096         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11097 #endif
11098
11099       else
11100         (void) ThrowMagickException(&image->exception,
11101              GetMagickModule(),CoderWarning,
11102              "ignoring invalid defined png:compression-strategy",
11103              "=%s",value);
11104     }
11105
11106   value=GetImageArtifact(image,"png:compression-filter");
11107   if (value == NULL)
11108      value=GetImageOption(image_info,"png:compression-filter");
11109   if (value != NULL)
11110   {
11111
11112       /* To do: combinations of filters allowed by libpng
11113        * masks 0x08 through 0xf8
11114        *
11115        * Implement this as a comma-separated list of 0,1,2,3,4,5
11116        * where 5 is a special case meaning PNG_ALL_FILTERS.
11117        */
11118
11119       if (LocaleCompare(value,"0") == 0)
11120         mng_info->write_png_compression_filter = 1;
11121
11122       if (LocaleCompare(value,"1") == 0)
11123         mng_info->write_png_compression_filter = 2;
11124
11125       else if (LocaleCompare(value,"2") == 0)
11126         mng_info->write_png_compression_filter = 3;
11127
11128       else if (LocaleCompare(value,"3") == 0)
11129         mng_info->write_png_compression_filter = 4;
11130
11131       else if (LocaleCompare(value,"4") == 0)
11132         mng_info->write_png_compression_filter = 5;
11133
11134       else if (LocaleCompare(value,"5") == 0)
11135         mng_info->write_png_compression_filter = 6;
11136
11137       else if (LocaleCompare(value,"6") == 0)
11138         mng_info->write_png_compression_filter = 7;
11139
11140       else if (LocaleCompare(value,"7") == 0)
11141         mng_info->write_png_compression_filter = 8;
11142
11143       else if (LocaleCompare(value,"8") == 0)
11144         mng_info->write_png_compression_filter = 9;
11145
11146       else if (LocaleCompare(value,"9") == 0)
11147         mng_info->write_png_compression_filter = 10;
11148
11149       else
11150         (void) ThrowMagickException(&image->exception,
11151              GetMagickModule(),CoderWarning,
11152              "ignoring invalid defined png:compression-filter",
11153              "=%s",value);
11154     }
11155
11156   excluding=MagickFalse;
11157
11158   for (source=0; source<1; source++)
11159   {
11160     if (source==0)
11161       {
11162        value=GetImageArtifact(image,"png:exclude-chunk");
11163
11164        if (value == NULL)
11165          value=GetImageArtifact(image,"png:exclude-chunks");
11166       }
11167     else
11168       {
11169        value=GetImageOption(image_info,"png:exclude-chunk");
11170
11171        if (value == NULL)
11172          value=GetImageOption(image_info,"png:exclude-chunks");
11173       }
11174
11175     if (value != NULL)
11176     {
11177
11178     size_t
11179       last;
11180
11181     excluding=MagickTrue;
11182
11183     if (logging != MagickFalse)
11184       {
11185         if (source == 0)
11186            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11187               "  png:exclude-chunk=%s found in image artifacts.\n", value);
11188         else
11189            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11190               "  png:exclude-chunk=%s found in image properties.\n", value);
11191       }
11192
11193     last=strlen(value);
11194
11195     for (i=0; i<(int) last; i+=5)
11196     {
11197
11198       if (LocaleNCompare(value+i,"all",3) == 0)
11199       {
11200         mng_info->ping_exclude_bKGD=MagickTrue;
11201         mng_info->ping_exclude_cHRM=MagickTrue;
11202         mng_info->ping_exclude_date=MagickTrue;
11203         mng_info->ping_exclude_EXIF=MagickTrue;
11204         mng_info->ping_exclude_gAMA=MagickTrue;
11205         mng_info->ping_exclude_iCCP=MagickTrue;
11206         /* mng_info->ping_exclude_iTXt=MagickTrue; */
11207         mng_info->ping_exclude_oFFs=MagickTrue;
11208         mng_info->ping_exclude_pHYs=MagickTrue;
11209         mng_info->ping_exclude_sRGB=MagickTrue;
11210         mng_info->ping_exclude_tEXt=MagickTrue;
11211         mng_info->ping_exclude_tRNS=MagickTrue;
11212         mng_info->ping_exclude_vpAg=MagickTrue;
11213         mng_info->ping_exclude_zCCP=MagickTrue;
11214         mng_info->ping_exclude_zTXt=MagickTrue;
11215         i--;
11216       }
11217
11218       if (LocaleNCompare(value+i,"none",4) == 0)
11219       {
11220         mng_info->ping_exclude_bKGD=MagickFalse;
11221         mng_info->ping_exclude_cHRM=MagickFalse;
11222         mng_info->ping_exclude_date=MagickFalse;
11223         mng_info->ping_exclude_EXIF=MagickFalse;
11224         mng_info->ping_exclude_gAMA=MagickFalse;
11225         mng_info->ping_exclude_iCCP=MagickFalse;
11226         /* mng_info->ping_exclude_iTXt=MagickFalse; */
11227         mng_info->ping_exclude_oFFs=MagickFalse;
11228         mng_info->ping_exclude_pHYs=MagickFalse;
11229         mng_info->ping_exclude_sRGB=MagickFalse;
11230         mng_info->ping_exclude_tEXt=MagickFalse;
11231         mng_info->ping_exclude_tRNS=MagickFalse;
11232         mng_info->ping_exclude_vpAg=MagickFalse;
11233         mng_info->ping_exclude_zCCP=MagickFalse;
11234         mng_info->ping_exclude_zTXt=MagickFalse;
11235       }
11236
11237       if (LocaleNCompare(value+i,"bkgd",4) == 0)
11238         mng_info->ping_exclude_bKGD=MagickTrue;
11239
11240       if (LocaleNCompare(value+i,"chrm",4) == 0)
11241         mng_info->ping_exclude_cHRM=MagickTrue;
11242
11243       if (LocaleNCompare(value+i,"date",4) == 0)
11244         mng_info->ping_exclude_date=MagickTrue;
11245
11246       if (LocaleNCompare(value+i,"exif",4) == 0)
11247         mng_info->ping_exclude_EXIF=MagickTrue;
11248
11249       if (LocaleNCompare(value+i,"gama",4) == 0)
11250         mng_info->ping_exclude_gAMA=MagickTrue;
11251
11252       if (LocaleNCompare(value+i,"iccp",4) == 0)
11253         mng_info->ping_exclude_iCCP=MagickTrue;
11254
11255     /*
11256       if (LocaleNCompare(value+i,"itxt",4) == 0)
11257         mng_info->ping_exclude_iTXt=MagickTrue;
11258      */
11259
11260       if (LocaleNCompare(value+i,"gama",4) == 0)
11261         mng_info->ping_exclude_gAMA=MagickTrue;
11262
11263       if (LocaleNCompare(value+i,"offs",4) == 0)
11264         mng_info->ping_exclude_oFFs=MagickTrue;
11265
11266       if (LocaleNCompare(value+i,"phys",4) == 0)
11267         mng_info->ping_exclude_pHYs=MagickTrue;
11268
11269       if (LocaleNCompare(value+i,"srgb",4) == 0)
11270         mng_info->ping_exclude_sRGB=MagickTrue;
11271
11272       if (LocaleNCompare(value+i,"text",4) == 0)
11273         mng_info->ping_exclude_tEXt=MagickTrue;
11274
11275       if (LocaleNCompare(value+i,"trns",4) == 0)
11276         mng_info->ping_exclude_tRNS=MagickTrue;
11277
11278       if (LocaleNCompare(value+i,"vpag",4) == 0)
11279         mng_info->ping_exclude_vpAg=MagickTrue;
11280
11281       if (LocaleNCompare(value+i,"zccp",4) == 0)
11282         mng_info->ping_exclude_zCCP=MagickTrue;
11283
11284       if (LocaleNCompare(value+i,"ztxt",4) == 0)
11285         mng_info->ping_exclude_zTXt=MagickTrue;
11286
11287       }
11288     }
11289   }
11290
11291   for (source=0; source<1; source++)
11292   {
11293     if (source==0)
11294       {
11295        value=GetImageArtifact(image,"png:include-chunk");
11296
11297        if (value == NULL)
11298          value=GetImageArtifact(image,"png:include-chunks");
11299       }
11300     else
11301       {
11302        value=GetImageOption(image_info,"png:include-chunk");
11303
11304        if (value == NULL)
11305          value=GetImageOption(image_info,"png:include-chunks");
11306       }
11307
11308     if (value != NULL)
11309     {
11310     size_t
11311       last;
11312
11313     excluding=MagickTrue;
11314
11315     if (logging != MagickFalse)
11316       {
11317         if (source == 0)
11318            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11319               "  png:include-chunk=%s found in image artifacts.\n", value);
11320         else
11321            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11322               "  png:include-chunk=%s found in image properties.\n", value);
11323       }
11324
11325     last=strlen(value);
11326
11327     for (i=0; i<(int) last; i+=5)
11328       {
11329       if (LocaleNCompare(value+i,"all",3) == 0)
11330         {
11331           mng_info->ping_exclude_bKGD=MagickFalse;
11332           mng_info->ping_exclude_cHRM=MagickFalse;
11333           mng_info->ping_exclude_date=MagickFalse;
11334           mng_info->ping_exclude_EXIF=MagickFalse;
11335           mng_info->ping_exclude_gAMA=MagickFalse;
11336           mng_info->ping_exclude_iCCP=MagickFalse;
11337           /* mng_info->ping_exclude_iTXt=MagickFalse; */
11338           mng_info->ping_exclude_oFFs=MagickFalse;
11339           mng_info->ping_exclude_pHYs=MagickFalse;
11340           mng_info->ping_exclude_sRGB=MagickFalse;
11341           mng_info->ping_exclude_tEXt=MagickFalse;
11342           mng_info->ping_exclude_tRNS=MagickFalse;
11343           mng_info->ping_exclude_vpAg=MagickFalse;
11344           mng_info->ping_exclude_zCCP=MagickFalse;
11345           mng_info->ping_exclude_zTXt=MagickFalse;
11346           i--;
11347         }
11348
11349       if (LocaleNCompare(value+i,"none",4) == 0)
11350         {
11351           mng_info->ping_exclude_bKGD=MagickTrue;
11352           mng_info->ping_exclude_cHRM=MagickTrue;
11353           mng_info->ping_exclude_date=MagickTrue;
11354           mng_info->ping_exclude_EXIF=MagickTrue;
11355           mng_info->ping_exclude_gAMA=MagickTrue;
11356           mng_info->ping_exclude_iCCP=MagickTrue;
11357           /* mng_info->ping_exclude_iTXt=MagickTrue; */
11358           mng_info->ping_exclude_oFFs=MagickTrue;
11359           mng_info->ping_exclude_pHYs=MagickTrue;
11360           mng_info->ping_exclude_sRGB=MagickTrue;
11361           mng_info->ping_exclude_tEXt=MagickTrue;
11362           mng_info->ping_exclude_tRNS=MagickTrue;
11363           mng_info->ping_exclude_vpAg=MagickTrue;
11364           mng_info->ping_exclude_zCCP=MagickTrue;
11365           mng_info->ping_exclude_zTXt=MagickTrue;
11366         }
11367
11368       if (LocaleNCompare(value+i,"bkgd",4) == 0)
11369         mng_info->ping_exclude_bKGD=MagickFalse;
11370
11371       if (LocaleNCompare(value+i,"chrm",4) == 0)
11372         mng_info->ping_exclude_cHRM=MagickFalse;
11373
11374       if (LocaleNCompare(value+i,"date",4) == 0)
11375         mng_info->ping_exclude_date=MagickFalse;
11376
11377       if (LocaleNCompare(value+i,"exif",4) == 0)
11378         mng_info->ping_exclude_EXIF=MagickFalse;
11379
11380       if (LocaleNCompare(value+i,"gama",4) == 0)
11381         mng_info->ping_exclude_gAMA=MagickFalse;
11382
11383       if (LocaleNCompare(value+i,"iccp",4) == 0)
11384         mng_info->ping_exclude_iCCP=MagickFalse;
11385
11386     /*
11387       if (LocaleNCompare(value+i,"itxt",4) == 0)
11388         mng_info->ping_exclude_iTXt=MagickFalse;
11389      */
11390
11391       if (LocaleNCompare(value+i,"gama",4) == 0)
11392         mng_info->ping_exclude_gAMA=MagickFalse;
11393
11394       if (LocaleNCompare(value+i,"offs",4) == 0)
11395         mng_info->ping_exclude_oFFs=MagickFalse;
11396
11397       if (LocaleNCompare(value+i,"phys",4) == 0)
11398         mng_info->ping_exclude_pHYs=MagickFalse;
11399
11400       if (LocaleNCompare(value+i,"srgb",4) == 0)
11401         mng_info->ping_exclude_sRGB=MagickFalse;
11402
11403       if (LocaleNCompare(value+i,"text",4) == 0)
11404         mng_info->ping_exclude_tEXt=MagickFalse;
11405
11406       if (LocaleNCompare(value+i,"trns",4) == 0)
11407         mng_info->ping_exclude_tRNS=MagickFalse;
11408
11409       if (LocaleNCompare(value+i,"vpag",4) == 0)
11410         mng_info->ping_exclude_vpAg=MagickFalse;
11411
11412       if (LocaleNCompare(value+i,"zccp",4) == 0)
11413         mng_info->ping_exclude_zCCP=MagickFalse;
11414
11415       if (LocaleNCompare(value+i,"ztxt",4) == 0)
11416         mng_info->ping_exclude_zTXt=MagickFalse;
11417
11418       }
11419     }
11420   }
11421
11422   if (excluding != MagickFalse && logging != MagickFalse)
11423   {
11424     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11425       "  Chunks to be excluded from the output PNG:");
11426     if (mng_info->ping_exclude_bKGD != MagickFalse)
11427       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11428           "    bKGD");
11429     if (mng_info->ping_exclude_cHRM != MagickFalse)
11430       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11431           "    cHRM");
11432     if (mng_info->ping_exclude_date != MagickFalse)
11433       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11434           "    date");
11435     if (mng_info->ping_exclude_EXIF != MagickFalse)
11436       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11437           "    EXIF");
11438     if (mng_info->ping_exclude_gAMA != MagickFalse)
11439       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11440           "    gAMA");
11441     if (mng_info->ping_exclude_iCCP != MagickFalse)
11442       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11443           "    iCCP");
11444 /*
11445     if (mng_info->ping_exclude_iTXt != MagickFalse)
11446       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11447           "    iTXt");
11448 */
11449     if (mng_info->ping_exclude_oFFs != MagickFalse)
11450       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11451           "    oFFs");
11452     if (mng_info->ping_exclude_pHYs != MagickFalse)
11453       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11454           "    pHYs");
11455     if (mng_info->ping_exclude_sRGB != MagickFalse)
11456       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11457           "    sRGB");
11458     if (mng_info->ping_exclude_tEXt != MagickFalse)
11459       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11460           "    tEXt");
11461     if (mng_info->ping_exclude_tRNS != MagickFalse)
11462       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11463           "    tRNS");
11464     if (mng_info->ping_exclude_vpAg != MagickFalse)
11465       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11466           "    vpAg");
11467     if (mng_info->ping_exclude_zCCP != MagickFalse)
11468       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11469           "    zCCP");
11470     if (mng_info->ping_exclude_zTXt != MagickFalse)
11471       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11472           "    zTXt");
11473   }
11474
11475   mng_info->need_blob = MagickTrue;
11476
11477   status=WriteOnePNGImage(mng_info,image_info,image);
11478
11479   MngInfoFreeStruct(mng_info,&have_mng_structure);
11480
11481   if (logging != MagickFalse)
11482     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
11483
11484   return(status);
11485 }
11486
11487 #if defined(JNG_SUPPORTED)
11488
11489 /* Write one JNG image */
11490 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11491    const ImageInfo *image_info,Image *image)
11492 {
11493   Image
11494     *jpeg_image;
11495
11496   ImageInfo
11497     *jpeg_image_info;
11498
11499   MagickBooleanType
11500     logging,
11501     status;
11502
11503   size_t
11504     length;
11505
11506   unsigned char
11507     *blob,
11508     chunk[80],
11509     *p;
11510
11511   unsigned int
11512     jng_alpha_compression_method,
11513     jng_alpha_sample_depth,
11514     jng_color_type,
11515     transparent;
11516
11517   size_t
11518     jng_quality;
11519
11520   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
11521     "  Enter WriteOneJNGImage()");
11522
11523   blob=(unsigned char *) NULL;
11524   jpeg_image=(Image *) NULL;
11525   jpeg_image_info=(ImageInfo *) NULL;
11526
11527   status=MagickTrue;
11528   transparent=image_info->type==GrayscaleMatteType ||
11529      image_info->type==TrueColorMatteType;
11530   jng_color_type=10;
11531   jng_alpha_sample_depth=0;
11532   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11533   jng_alpha_compression_method=0;
11534
11535   if (image->matte != MagickFalse)
11536     {
11537       /* if any pixels are transparent */
11538       transparent=MagickTrue;
11539       if (image_info->compression==JPEGCompression)
11540         jng_alpha_compression_method=8;
11541     }
11542
11543   if (transparent)
11544     {
11545       jng_color_type=14;
11546
11547       /* Create JPEG blob, image, and image_info */
11548       if (logging != MagickFalse)
11549         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11550           "  Creating jpeg_image_info for alpha.");
11551
11552       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11553
11554       if (jpeg_image_info == (ImageInfo *) NULL)
11555         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11556
11557       if (logging != MagickFalse)
11558         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11559           "  Creating jpeg_image.");
11560
11561       jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11562
11563       if (jpeg_image == (Image *) NULL)
11564         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11565
11566       (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11567       PushPixelChannelMap(jpeg_image,AlphaChannel);
11568       status=SeparateImage(jpeg_image);
11569       PopPixelChannelMap(jpeg_image);
11570       jpeg_image->matte=MagickFalse;
11571
11572       if (jng_quality >= 1000)
11573         jpeg_image_info->quality=jng_quality/1000;
11574
11575       else
11576         jpeg_image_info->quality=jng_quality;
11577
11578       jpeg_image_info->type=GrayscaleType;
11579       (void) SetImageType(jpeg_image,GrayscaleType);
11580       (void) AcquireUniqueFilename(jpeg_image->filename);
11581       (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
11582         "%s",jpeg_image->filename);
11583     }
11584
11585   /* To do: check bit depth of PNG alpha channel */
11586
11587   /* Check if image is grayscale. */
11588   if (image_info->type != TrueColorMatteType && image_info->type !=
11589     TrueColorType && ImageIsGray(image))
11590     jng_color_type-=2;
11591
11592   if (transparent)
11593     {
11594       if (jng_alpha_compression_method==0)
11595         {
11596           const char
11597             *value;
11598
11599           /* Encode alpha as a grayscale PNG blob */
11600           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11601             &image->exception);
11602           if (logging != MagickFalse)
11603             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11604               "  Creating PNG blob.");
11605           length=0;
11606
11607           (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11608           (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11609           jpeg_image_info->interlace=NoInterlace;
11610
11611           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11612             &image->exception);
11613
11614           /* Retrieve sample depth used */
11615           value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11616           if (value != (char *) NULL)
11617             jng_alpha_sample_depth= (unsigned int) value[0];
11618         }
11619       else
11620         {
11621           /* Encode alpha as a grayscale JPEG blob */
11622
11623           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11624             &image->exception);
11625
11626           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11627           (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11628           jpeg_image_info->interlace=NoInterlace;
11629           if (logging != MagickFalse)
11630             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11631               "  Creating blob.");
11632           blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11633            &image->exception);
11634           jng_alpha_sample_depth=8;
11635
11636           if (logging != MagickFalse)
11637             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11638               "  Successfully read jpeg_image into a blob, length=%.20g.",
11639               (double) length);
11640
11641         }
11642       /* Destroy JPEG image and image_info */
11643       jpeg_image=DestroyImage(jpeg_image);
11644       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11645       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11646     }
11647
11648   /* Write JHDR chunk */
11649   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
11650   PNGType(chunk,mng_JHDR);
11651   LogPNGChunk(logging,mng_JHDR,16L);
11652   PNGLong(chunk+4,(png_uint_32) image->columns);
11653   PNGLong(chunk+8,(png_uint_32) image->rows);
11654   chunk[12]=jng_color_type;
11655   chunk[13]=8;  /* sample depth */
11656   chunk[14]=8; /*jng_image_compression_method */
11657   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11658   chunk[16]=jng_alpha_sample_depth;
11659   chunk[17]=jng_alpha_compression_method;
11660   chunk[18]=0; /*jng_alpha_filter_method */
11661   chunk[19]=0; /*jng_alpha_interlace_method */
11662   (void) WriteBlob(image,20,chunk);
11663   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11664   if (logging != MagickFalse)
11665     {
11666       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11667         "    JNG width:%15lu",(unsigned long) image->columns);
11668
11669       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11670         "    JNG height:%14lu",(unsigned long) image->rows);
11671
11672       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11673         "    JNG color type:%10d",jng_color_type);
11674
11675       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11676         "    JNG sample depth:%8d",8);
11677
11678       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11679         "    JNG compression:%9d",8);
11680
11681       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11682         "    JNG interlace:%11d",0);
11683
11684       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11685         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
11686
11687       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11688         "    JNG alpha compression:%3d",jng_alpha_compression_method);
11689
11690       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11691         "    JNG alpha filter:%8d",0);
11692
11693       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11694         "    JNG alpha interlace:%5d",0);
11695     }
11696
11697   /* Write any JNG-chunk-b profiles */
11698   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
11699
11700   /*
11701      Write leading ancillary chunks
11702   */
11703
11704   if (transparent)
11705   {
11706     /*
11707       Write JNG bKGD chunk
11708     */
11709
11710     unsigned char
11711       blue,
11712       green,
11713       red;
11714
11715     ssize_t
11716       num_bytes;
11717
11718     if (jng_color_type == 8 || jng_color_type == 12)
11719       num_bytes=6L;
11720     else
11721       num_bytes=10L;
11722     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
11723     PNGType(chunk,mng_bKGD);
11724     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
11725     red=ScaleQuantumToChar(image->background_color.red);
11726     green=ScaleQuantumToChar(image->background_color.green);
11727     blue=ScaleQuantumToChar(image->background_color.blue);
11728     *(chunk+4)=0;
11729     *(chunk+5)=red;
11730     *(chunk+6)=0;
11731     *(chunk+7)=green;
11732     *(chunk+8)=0;
11733     *(chunk+9)=blue;
11734     (void) WriteBlob(image,(size_t) num_bytes,chunk);
11735     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11736   }
11737
11738   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11739     {
11740       /*
11741         Write JNG sRGB chunk
11742       */
11743       (void) WriteBlobMSBULong(image,1L);
11744       PNGType(chunk,mng_sRGB);
11745       LogPNGChunk(logging,mng_sRGB,1L);
11746
11747       if (image->rendering_intent != UndefinedIntent)
11748         chunk[4]=(unsigned char)
11749           Magick_RenderingIntent_to_PNG_RenderingIntent(
11750           (image->rendering_intent));
11751
11752       else
11753         chunk[4]=(unsigned char)
11754           Magick_RenderingIntent_to_PNG_RenderingIntent(
11755           (PerceptualIntent));
11756
11757       (void) WriteBlob(image,5,chunk);
11758       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11759     }
11760   else
11761     {
11762       if (image->gamma != 0.0)
11763         {
11764           /*
11765              Write JNG gAMA chunk
11766           */
11767           (void) WriteBlobMSBULong(image,4L);
11768           PNGType(chunk,mng_gAMA);
11769           LogPNGChunk(logging,mng_gAMA,4L);
11770           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
11771           (void) WriteBlob(image,8,chunk);
11772           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11773         }
11774
11775       if ((mng_info->equal_chrms == MagickFalse) &&
11776           (image->chromaticity.red_primary.x != 0.0))
11777         {
11778           PrimaryInfo
11779             primary;
11780
11781           /*
11782              Write JNG cHRM chunk
11783           */
11784           (void) WriteBlobMSBULong(image,32L);
11785           PNGType(chunk,mng_cHRM);
11786           LogPNGChunk(logging,mng_cHRM,32L);
11787           primary=image->chromaticity.white_point;
11788           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11789           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
11790           primary=image->chromaticity.red_primary;
11791           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11792           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
11793           primary=image->chromaticity.green_primary;
11794           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11795           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
11796           primary=image->chromaticity.blue_primary;
11797           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11798           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
11799           (void) WriteBlob(image,36,chunk);
11800           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11801         }
11802     }
11803
11804   if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11805     {
11806       /*
11807          Write JNG pHYs chunk
11808       */
11809       (void) WriteBlobMSBULong(image,9L);
11810       PNGType(chunk,mng_pHYs);
11811       LogPNGChunk(logging,mng_pHYs,9L);
11812       if (image->units == PixelsPerInchResolution)
11813         {
11814           PNGLong(chunk+4,(png_uint_32)
11815             (image->x_resolution*100.0/2.54+0.5));
11816
11817           PNGLong(chunk+8,(png_uint_32)
11818             (image->y_resolution*100.0/2.54+0.5));
11819
11820           chunk[12]=1;
11821         }
11822
11823       else
11824         {
11825           if (image->units == PixelsPerCentimeterResolution)
11826             {
11827               PNGLong(chunk+4,(png_uint_32)
11828                 (image->x_resolution*100.0+0.5));
11829
11830               PNGLong(chunk+8,(png_uint_32)
11831                 (image->y_resolution*100.0+0.5));
11832
11833               chunk[12]=1;
11834             }
11835
11836           else
11837             {
11838               PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11839               PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
11840               chunk[12]=0;
11841             }
11842         }
11843       (void) WriteBlob(image,13,chunk);
11844       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11845     }
11846
11847   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11848     {
11849       /*
11850          Write JNG oFFs chunk
11851       */
11852       (void) WriteBlobMSBULong(image,9L);
11853       PNGType(chunk,mng_oFFs);
11854       LogPNGChunk(logging,mng_oFFs,9L);
11855       PNGsLong(chunk+4,(ssize_t) (image->page.x));
11856       PNGsLong(chunk+8,(ssize_t) (image->page.y));
11857       chunk[12]=0;
11858       (void) WriteBlob(image,13,chunk);
11859       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11860     }
11861   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11862     {
11863        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
11864        PNGType(chunk,mng_vpAg);
11865        LogPNGChunk(logging,mng_vpAg,9L);
11866        PNGLong(chunk+4,(png_uint_32) image->page.width);
11867        PNGLong(chunk+8,(png_uint_32) image->page.height);
11868        chunk[12]=0;   /* unit = pixels */
11869        (void) WriteBlob(image,13,chunk);
11870        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11871     }
11872
11873
11874   if (transparent)
11875     {
11876       if (jng_alpha_compression_method==0)
11877         {
11878           register ssize_t
11879             i;
11880
11881           ssize_t
11882             len;
11883
11884           /* Write IDAT chunk header */
11885           if (logging != MagickFalse)
11886             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11887               "  Write IDAT chunks from blob, length=%.20g.",(double)
11888               length);
11889
11890           /* Copy IDAT chunks */
11891           len=0;
11892           p=blob+8;
11893           for (i=8; i<(ssize_t) length; i+=len+12)
11894           {
11895             len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11896             p+=4;
11897
11898             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11899               {
11900                 /* Found an IDAT chunk. */
11901                 (void) WriteBlobMSBULong(image,(size_t) len);
11902                 LogPNGChunk(logging,mng_IDAT,(size_t) len);
11903                 (void) WriteBlob(image,(size_t) len+4,p);
11904                 (void) WriteBlobMSBULong(image,
11905                     crc32(0,p,(uInt) len+4));
11906               }
11907
11908             else
11909               {
11910                 if (logging != MagickFalse)
11911                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11912                     "    Skipping %c%c%c%c chunk, length=%.20g.",
11913                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
11914               }
11915             p+=(8+len);
11916           }
11917         }
11918       else
11919         {
11920           /* Write JDAA chunk header */
11921           if (logging != MagickFalse)
11922             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11923               "  Write JDAA chunk, length=%.20g.",(double) length);
11924           (void) WriteBlobMSBULong(image,(size_t) length);
11925           PNGType(chunk,mng_JDAA);
11926           LogPNGChunk(logging,mng_JDAA,length);
11927           /* Write JDAT chunk(s) data */
11928           (void) WriteBlob(image,4,chunk);
11929           (void) WriteBlob(image,length,blob);
11930           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11931              (uInt) length));
11932         }
11933       blob=(unsigned char *) RelinquishMagickMemory(blob);
11934     }
11935
11936   /* Encode image as a JPEG blob */
11937   if (logging != MagickFalse)
11938     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11939       "  Creating jpeg_image_info.");
11940   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11941   if (jpeg_image_info == (ImageInfo *) NULL)
11942     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11943
11944   if (logging != MagickFalse)
11945     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11946       "  Creating jpeg_image.");
11947
11948   jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11949   if (jpeg_image == (Image *) NULL)
11950     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11951   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11952
11953   (void) AcquireUniqueFilename(jpeg_image->filename);
11954   (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
11955     jpeg_image->filename);
11956
11957   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11958     &image->exception);
11959
11960   if (logging != MagickFalse)
11961     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11962       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11963       (double) jpeg_image->rows);
11964
11965   if (jng_color_type == 8 || jng_color_type == 12)
11966     jpeg_image_info->type=GrayscaleType;
11967
11968   jpeg_image_info->quality=jng_quality % 1000;
11969   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11970   (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11971
11972   if (logging != MagickFalse)
11973     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11974       "  Creating blob.");
11975
11976   blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
11977
11978   if (logging != MagickFalse)
11979     {
11980       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11981         "  Successfully read jpeg_image into a blob, length=%.20g.",
11982         (double) length);
11983
11984       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11985         "  Write JDAT chunk, length=%.20g.",(double) length);
11986     }
11987
11988   /* Write JDAT chunk(s) */
11989   (void) WriteBlobMSBULong(image,(size_t) length);
11990   PNGType(chunk,mng_JDAT);
11991   LogPNGChunk(logging,mng_JDAT,length);
11992   (void) WriteBlob(image,4,chunk);
11993   (void) WriteBlob(image,length,blob);
11994   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11995
11996   jpeg_image=DestroyImage(jpeg_image);
11997   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11998   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11999   blob=(unsigned char *) RelinquishMagickMemory(blob);
12000
12001   /* Write any JNG-chunk-e profiles */
12002   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12003
12004   /* Write IEND chunk */
12005   (void) WriteBlobMSBULong(image,0L);
12006   PNGType(chunk,mng_IEND);
12007   LogPNGChunk(logging,mng_IEND,0);
12008   (void) WriteBlob(image,4,chunk);
12009   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12010
12011   if (logging != MagickFalse)
12012     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12013       "  exit WriteOneJNGImage()");
12014
12015   return(status);
12016 }
12017
12018
12019 /*
12020 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12021 %                                                                             %
12022 %                                                                             %
12023 %                                                                             %
12024 %   W r i t e J N G I m a g e                                                 %
12025 %                                                                             %
12026 %                                                                             %
12027 %                                                                             %
12028 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12029 %
12030 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12031 %
12032 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
12033 %
12034 %  The format of the WriteJNGImage method is:
12035 %
12036 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
12037 %
12038 %  A description of each parameter follows:
12039 %
12040 %    o image_info: the image info.
12041 %
12042 %    o image:  The image.
12043 %
12044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12045 */
12046 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
12047 {
12048   MagickBooleanType
12049     have_mng_structure,
12050     logging,
12051     status;
12052
12053   MngInfo
12054     *mng_info;
12055
12056   /*
12057     Open image file.
12058   */
12059   assert(image_info != (const ImageInfo *) NULL);
12060   assert(image_info->signature == MagickSignature);
12061   assert(image != (Image *) NULL);
12062   assert(image->signature == MagickSignature);
12063   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12064   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12065   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12066   if (status == MagickFalse)
12067     return(status);
12068
12069   /*
12070     Allocate a MngInfo structure.
12071   */
12072   have_mng_structure=MagickFalse;
12073   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12074   if (mng_info == (MngInfo *) NULL)
12075     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12076   /*
12077     Initialize members of the MngInfo structure.
12078   */
12079   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12080   mng_info->image=image;
12081   have_mng_structure=MagickTrue;
12082
12083   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12084
12085   status=WriteOneJNGImage(mng_info,image_info,image);
12086   (void) CloseBlob(image);
12087
12088   (void) CatchImageException(image);
12089   MngInfoFreeStruct(mng_info,&have_mng_structure);
12090   if (logging != MagickFalse)
12091     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12092   return(status);
12093 }
12094 #endif
12095
12096
12097
12098 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12099 {
12100   const char
12101     *option;
12102
12103   Image
12104     *next_image;
12105
12106   MagickBooleanType
12107     have_mng_structure,
12108     status;
12109
12110   volatile MagickBooleanType
12111     logging;
12112
12113   MngInfo
12114     *mng_info;
12115
12116   int
12117     image_count,
12118     need_iterations,
12119     need_matte;
12120
12121   volatile int
12122 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12123     defined(PNG_MNG_FEATURES_SUPPORTED)
12124     need_local_plte,
12125 #endif
12126     all_images_are_gray,
12127     need_defi,
12128     use_global_plte;
12129
12130   register ssize_t
12131     i;
12132
12133   unsigned char
12134     chunk[800];
12135
12136   volatile unsigned int
12137     write_jng,
12138     write_mng;
12139
12140   volatile size_t
12141     scene;
12142
12143   size_t
12144     final_delay=0,
12145     initial_delay;
12146
12147 #if (PNG_LIBPNG_VER < 10200)
12148     if (image_info->verbose)
12149       printf("Your PNG library (libpng-%s) is rather old.\n",
12150          PNG_LIBPNG_VER_STRING);
12151 #endif
12152
12153   /*
12154     Open image file.
12155   */
12156   assert(image_info != (const ImageInfo *) NULL);
12157   assert(image_info->signature == MagickSignature);
12158   assert(image != (Image *) NULL);
12159   assert(image->signature == MagickSignature);
12160   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12161   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12162   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12163   if (status == MagickFalse)
12164     return(status);
12165
12166   /*
12167     Allocate a MngInfo structure.
12168   */
12169   have_mng_structure=MagickFalse;
12170   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12171   if (mng_info == (MngInfo *) NULL)
12172     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12173   /*
12174     Initialize members of the MngInfo structure.
12175   */
12176   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12177   mng_info->image=image;
12178   have_mng_structure=MagickTrue;
12179   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12180
12181   /*
12182    * See if user has requested a specific PNG subformat to be used
12183    * for all of the PNGs in the MNG being written, e.g.,
12184    *
12185    *    convert *.png png8:animation.mng
12186    *
12187    * To do: check -define png:bit_depth and png:color_type as well,
12188    * or perhaps use mng:bit_depth and mng:color_type instead for
12189    * global settings.
12190    */
12191
12192   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12193   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12194   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12195
12196   write_jng=MagickFalse;
12197   if (image_info->compression == JPEGCompression)
12198     write_jng=MagickTrue;
12199
12200   mng_info->adjoin=image_info->adjoin &&
12201     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12202
12203   if (logging != MagickFalse)
12204     {
12205       /* Log some info about the input */
12206       Image
12207         *p;
12208
12209       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12210         "  Checking input image(s)");
12211
12212       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12213         "    Image_info depth: %.20g",(double) image_info->depth);
12214
12215       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12216         "    Type: %d",image_info->type);
12217
12218       scene=0;
12219       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12220       {
12221         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12222           "    Scene: %.20g",(double) scene++);
12223
12224         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12225           "      Image depth: %.20g",(double) p->depth);
12226
12227         if (p->matte)
12228           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12229             "      Matte: True");
12230
12231         else
12232           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12233             "      Matte: False");
12234
12235         if (p->storage_class == PseudoClass)
12236           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12237             "      Storage class: PseudoClass");
12238
12239         else
12240           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12241             "      Storage class: DirectClass");
12242
12243         if (p->colors)
12244           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12245             "      Number of colors: %.20g",(double) p->colors);
12246
12247         else
12248           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12249             "      Number of colors: unspecified");
12250
12251         if (mng_info->adjoin == MagickFalse)
12252           break;
12253       }
12254     }
12255
12256   use_global_plte=MagickFalse;
12257   all_images_are_gray=MagickFalse;
12258 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12259   need_local_plte=MagickTrue;
12260 #endif
12261   need_defi=MagickFalse;
12262   need_matte=MagickFalse;
12263   mng_info->framing_mode=1;
12264   mng_info->old_framing_mode=1;
12265
12266   if (write_mng)
12267       if (image_info->page != (char *) NULL)
12268         {
12269           /*
12270             Determine image bounding box.
12271           */
12272           SetGeometry(image,&mng_info->page);
12273           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12274             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12275         }
12276   if (write_mng)
12277     {
12278       unsigned int
12279         need_geom;
12280
12281       unsigned short
12282         red,
12283         green,
12284         blue;
12285
12286       mng_info->page=image->page;
12287       need_geom=MagickTrue;
12288       if (mng_info->page.width || mng_info->page.height)
12289          need_geom=MagickFalse;
12290       /*
12291         Check all the scenes.
12292       */
12293       initial_delay=image->delay;
12294       need_iterations=MagickFalse;
12295       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12296       mng_info->equal_physs=MagickTrue,
12297       mng_info->equal_gammas=MagickTrue;
12298       mng_info->equal_srgbs=MagickTrue;
12299       mng_info->equal_backgrounds=MagickTrue;
12300       image_count=0;
12301 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12302     defined(PNG_MNG_FEATURES_SUPPORTED)
12303       all_images_are_gray=MagickTrue;
12304       mng_info->equal_palettes=MagickFalse;
12305       need_local_plte=MagickFalse;
12306 #endif
12307       for (next_image=image; next_image != (Image *) NULL; )
12308       {
12309         if (need_geom)
12310           {
12311             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12312               mng_info->page.width=next_image->columns+next_image->page.x;
12313
12314             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12315               mng_info->page.height=next_image->rows+next_image->page.y;
12316           }
12317
12318         if (next_image->page.x || next_image->page.y)
12319           need_defi=MagickTrue;
12320
12321         if (next_image->matte)
12322           need_matte=MagickTrue;
12323
12324         if ((int) next_image->dispose >= BackgroundDispose)
12325           if (next_image->matte || next_image->page.x || next_image->page.y ||
12326               ((next_image->columns < mng_info->page.width) &&
12327                (next_image->rows < mng_info->page.height)))
12328             mng_info->need_fram=MagickTrue;
12329
12330         if (next_image->iterations)
12331           need_iterations=MagickTrue;
12332
12333         final_delay=next_image->delay;
12334
12335         if (final_delay != initial_delay || final_delay > 1UL*
12336            next_image->ticks_per_second)
12337           mng_info->need_fram=1;
12338
12339 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12340     defined(PNG_MNG_FEATURES_SUPPORTED)
12341         /*
12342           check for global palette possibility.
12343         */
12344         if (image->matte != MagickFalse)
12345            need_local_plte=MagickTrue;
12346
12347         if (need_local_plte == 0)
12348           {
12349             if (ImageIsGray(image) == MagickFalse)
12350               all_images_are_gray=MagickFalse;
12351             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12352             if (use_global_plte == 0)
12353               use_global_plte=mng_info->equal_palettes;
12354             need_local_plte=!mng_info->equal_palettes;
12355           }
12356 #endif
12357         if (GetNextImageInList(next_image) != (Image *) NULL)
12358           {
12359             if (next_image->background_color.red !=
12360                 next_image->next->background_color.red ||
12361                 next_image->background_color.green !=
12362                 next_image->next->background_color.green ||
12363                 next_image->background_color.blue !=
12364                 next_image->next->background_color.blue)
12365               mng_info->equal_backgrounds=MagickFalse;
12366
12367             if (next_image->gamma != next_image->next->gamma)
12368               mng_info->equal_gammas=MagickFalse;
12369
12370             if (next_image->rendering_intent !=
12371                 next_image->next->rendering_intent)
12372               mng_info->equal_srgbs=MagickFalse;
12373
12374             if ((next_image->units != next_image->next->units) ||
12375                 (next_image->x_resolution != next_image->next->x_resolution) ||
12376                 (next_image->y_resolution != next_image->next->y_resolution))
12377               mng_info->equal_physs=MagickFalse;
12378
12379             if (mng_info->equal_chrms)
12380               {
12381                 if (next_image->chromaticity.red_primary.x !=
12382                     next_image->next->chromaticity.red_primary.x ||
12383                     next_image->chromaticity.red_primary.y !=
12384                     next_image->next->chromaticity.red_primary.y ||
12385                     next_image->chromaticity.green_primary.x !=
12386                     next_image->next->chromaticity.green_primary.x ||
12387                     next_image->chromaticity.green_primary.y !=
12388                     next_image->next->chromaticity.green_primary.y ||
12389                     next_image->chromaticity.blue_primary.x !=
12390                     next_image->next->chromaticity.blue_primary.x ||
12391                     next_image->chromaticity.blue_primary.y !=
12392                     next_image->next->chromaticity.blue_primary.y ||
12393                     next_image->chromaticity.white_point.x !=
12394                     next_image->next->chromaticity.white_point.x ||
12395                     next_image->chromaticity.white_point.y !=
12396                     next_image->next->chromaticity.white_point.y)
12397                   mng_info->equal_chrms=MagickFalse;
12398               }
12399           }
12400         image_count++;
12401         next_image=GetNextImageInList(next_image);
12402       }
12403       if (image_count < 2)
12404         {
12405           mng_info->equal_backgrounds=MagickFalse;
12406           mng_info->equal_chrms=MagickFalse;
12407           mng_info->equal_gammas=MagickFalse;
12408           mng_info->equal_srgbs=MagickFalse;
12409           mng_info->equal_physs=MagickFalse;
12410           use_global_plte=MagickFalse;
12411 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12412           need_local_plte=MagickTrue;
12413 #endif
12414           need_iterations=MagickFalse;
12415         }
12416
12417      if (mng_info->need_fram == MagickFalse)
12418        {
12419          /*
12420            Only certain framing rates 100/n are exactly representable without
12421            the FRAM chunk but we'll allow some slop in VLC files
12422          */
12423          if (final_delay == 0)
12424            {
12425              if (need_iterations != MagickFalse)
12426                {
12427                  /*
12428                    It's probably a GIF with loop; don't run it *too* fast.
12429                  */
12430                  if (mng_info->adjoin)
12431                    {
12432                      final_delay=10;
12433                      (void) ThrowMagickException(&image->exception,
12434                         GetMagickModule(),CoderWarning,
12435                        "input has zero delay between all frames; assuming",
12436                        " 10 cs `%s'","");
12437                    }
12438                }
12439              else
12440                mng_info->ticks_per_second=0;
12441            }
12442          if (final_delay != 0)
12443            mng_info->ticks_per_second=(png_uint_32)
12444               (image->ticks_per_second/final_delay);
12445          if (final_delay > 50)
12446            mng_info->ticks_per_second=2;
12447
12448          if (final_delay > 75)
12449            mng_info->ticks_per_second=1;
12450
12451          if (final_delay > 125)
12452            mng_info->need_fram=MagickTrue;
12453
12454          if (need_defi && final_delay > 2 && (final_delay != 4) &&
12455             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12456             (final_delay != 25) && (final_delay != 50) && (final_delay !=
12457                1UL*image->ticks_per_second))
12458            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
12459        }
12460
12461      if (mng_info->need_fram != MagickFalse)
12462         mng_info->ticks_per_second=1UL*image->ticks_per_second;
12463      /*
12464         If pseudocolor, we should also check to see if all the
12465         palettes are identical and write a global PLTE if they are.
12466         ../glennrp Feb 99.
12467      */
12468      /*
12469         Write the MNG version 1.0 signature and MHDR chunk.
12470      */
12471      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12472      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
12473      PNGType(chunk,mng_MHDR);
12474      LogPNGChunk(logging,mng_MHDR,28L);
12475      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12476      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
12477      PNGLong(chunk+12,mng_info->ticks_per_second);
12478      PNGLong(chunk+16,0L);  /* layer count=unknown */
12479      PNGLong(chunk+20,0L);  /* frame count=unknown */
12480      PNGLong(chunk+24,0L);  /* play time=unknown   */
12481      if (write_jng)
12482        {
12483          if (need_matte)
12484            {
12485              if (need_defi || mng_info->need_fram || use_global_plte)
12486                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
12487
12488              else
12489                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
12490            }
12491
12492          else
12493            {
12494              if (need_defi || mng_info->need_fram || use_global_plte)
12495                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
12496
12497              else
12498                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
12499            }
12500        }
12501
12502      else
12503        {
12504          if (need_matte)
12505            {
12506              if (need_defi || mng_info->need_fram || use_global_plte)
12507                PNGLong(chunk+28,11L);    /* simplicity=LC */
12508
12509              else
12510                PNGLong(chunk+28,9L);    /* simplicity=VLC */
12511            }
12512
12513          else
12514            {
12515              if (need_defi || mng_info->need_fram || use_global_plte)
12516                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
12517
12518              else
12519                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
12520            }
12521        }
12522      (void) WriteBlob(image,32,chunk);
12523      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12524      option=GetImageOption(image_info,"mng:need-cacheoff");
12525      if (option != (const char *) NULL)
12526        {
12527          size_t
12528            length;
12529
12530          /*
12531            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12532          */
12533          PNGType(chunk,mng_nEED);
12534          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
12535          (void) WriteBlobMSBULong(image,(size_t) length);
12536          LogPNGChunk(logging,mng_nEED,(size_t) length);
12537          length+=4;
12538          (void) WriteBlob(image,length,chunk);
12539          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12540        }
12541      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12542          (GetNextImageInList(image) != (Image *) NULL) &&
12543          (image->iterations != 1))
12544        {
12545          /*
12546            Write MNG TERM chunk
12547          */
12548          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
12549          PNGType(chunk,mng_TERM);
12550          LogPNGChunk(logging,mng_TERM,10L);
12551          chunk[4]=3;  /* repeat animation */
12552          chunk[5]=0;  /* show last frame when done */
12553          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12554             final_delay/MagickMax(image->ticks_per_second,1)));
12555
12556          if (image->iterations == 0)
12557            PNGLong(chunk+10,PNG_UINT_31_MAX);
12558
12559          else
12560            PNGLong(chunk+10,(png_uint_32) image->iterations);
12561
12562          if (logging != MagickFalse)
12563            {
12564              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12565                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12566               final_delay/MagickMax(image->ticks_per_second,1)));
12567
12568              if (image->iterations == 0)
12569                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12570                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
12571
12572              else
12573                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12574                  "     Image iterations: %.20g",(double) image->iterations);
12575            }
12576          (void) WriteBlob(image,14,chunk);
12577          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12578        }
12579      /*
12580        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12581      */
12582      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12583           mng_info->equal_srgbs)
12584        {
12585          /*
12586            Write MNG sRGB chunk
12587          */
12588          (void) WriteBlobMSBULong(image,1L);
12589          PNGType(chunk,mng_sRGB);
12590          LogPNGChunk(logging,mng_sRGB,1L);
12591
12592          if (image->rendering_intent != UndefinedIntent)
12593            chunk[4]=(unsigned char)
12594              Magick_RenderingIntent_to_PNG_RenderingIntent(
12595              (image->rendering_intent));
12596
12597          else
12598            chunk[4]=(unsigned char)
12599              Magick_RenderingIntent_to_PNG_RenderingIntent(
12600                (PerceptualIntent));
12601
12602          (void) WriteBlob(image,5,chunk);
12603          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12604          mng_info->have_write_global_srgb=MagickTrue;
12605        }
12606
12607      else
12608        {
12609          if (image->gamma && mng_info->equal_gammas)
12610            {
12611              /*
12612                 Write MNG gAMA chunk
12613              */
12614              (void) WriteBlobMSBULong(image,4L);
12615              PNGType(chunk,mng_gAMA);
12616              LogPNGChunk(logging,mng_gAMA,4L);
12617              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12618              (void) WriteBlob(image,8,chunk);
12619              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12620              mng_info->have_write_global_gama=MagickTrue;
12621            }
12622          if (mng_info->equal_chrms)
12623            {
12624              PrimaryInfo
12625                primary;
12626
12627              /*
12628                 Write MNG cHRM chunk
12629              */
12630              (void) WriteBlobMSBULong(image,32L);
12631              PNGType(chunk,mng_cHRM);
12632              LogPNGChunk(logging,mng_cHRM,32L);
12633              primary=image->chromaticity.white_point;
12634              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12635              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12636              primary=image->chromaticity.red_primary;
12637              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12638              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12639              primary=image->chromaticity.green_primary;
12640              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12641              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12642              primary=image->chromaticity.blue_primary;
12643              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12644              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12645              (void) WriteBlob(image,36,chunk);
12646              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12647              mng_info->have_write_global_chrm=MagickTrue;
12648            }
12649        }
12650      if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12651        {
12652          /*
12653             Write MNG pHYs chunk
12654          */
12655          (void) WriteBlobMSBULong(image,9L);
12656          PNGType(chunk,mng_pHYs);
12657          LogPNGChunk(logging,mng_pHYs,9L);
12658
12659          if (image->units == PixelsPerInchResolution)
12660            {
12661              PNGLong(chunk+4,(png_uint_32)
12662                (image->x_resolution*100.0/2.54+0.5));
12663
12664              PNGLong(chunk+8,(png_uint_32)
12665                (image->y_resolution*100.0/2.54+0.5));
12666
12667              chunk[12]=1;
12668            }
12669
12670          else
12671            {
12672              if (image->units == PixelsPerCentimeterResolution)
12673                {
12674                  PNGLong(chunk+4,(png_uint_32)
12675                    (image->x_resolution*100.0+0.5));
12676
12677                  PNGLong(chunk+8,(png_uint_32)
12678                    (image->y_resolution*100.0+0.5));
12679
12680                  chunk[12]=1;
12681                }
12682
12683              else
12684                {
12685                  PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12686                  PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
12687                  chunk[12]=0;
12688                }
12689            }
12690          (void) WriteBlob(image,13,chunk);
12691          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12692        }
12693      /*
12694        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12695        or does not cover the entire frame.
12696      */
12697      if (write_mng && (image->matte || image->page.x > 0 ||
12698          image->page.y > 0 || (image->page.width &&
12699          (image->page.width+image->page.x < mng_info->page.width))
12700          || (image->page.height && (image->page.height+image->page.y
12701          < mng_info->page.height))))
12702        {
12703          (void) WriteBlobMSBULong(image,6L);
12704          PNGType(chunk,mng_BACK);
12705          LogPNGChunk(logging,mng_BACK,6L);
12706          red=ScaleQuantumToShort(image->background_color.red);
12707          green=ScaleQuantumToShort(image->background_color.green);
12708          blue=ScaleQuantumToShort(image->background_color.blue);
12709          PNGShort(chunk+4,red);
12710          PNGShort(chunk+6,green);
12711          PNGShort(chunk+8,blue);
12712          (void) WriteBlob(image,10,chunk);
12713          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12714          if (mng_info->equal_backgrounds)
12715            {
12716              (void) WriteBlobMSBULong(image,6L);
12717              PNGType(chunk,mng_bKGD);
12718              LogPNGChunk(logging,mng_bKGD,6L);
12719              (void) WriteBlob(image,10,chunk);
12720              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12721            }
12722        }
12723
12724 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12725      if ((need_local_plte == MagickFalse) &&
12726          (image->storage_class == PseudoClass) &&
12727          (all_images_are_gray == MagickFalse))
12728        {
12729          size_t
12730            data_length;
12731
12732          /*
12733            Write MNG PLTE chunk
12734          */
12735          data_length=3*image->colors;
12736          (void) WriteBlobMSBULong(image,data_length);
12737          PNGType(chunk,mng_PLTE);
12738          LogPNGChunk(logging,mng_PLTE,data_length);
12739
12740          for (i=0; i < (ssize_t) image->colors; i++)
12741          {
12742            chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
12743            chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
12744            chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
12745          }
12746
12747          (void) WriteBlob(image,data_length+4,chunk);
12748          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12749          mng_info->have_write_global_plte=MagickTrue;
12750        }
12751 #endif
12752     }
12753   scene=0;
12754   mng_info->delay=0;
12755 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12756     defined(PNG_MNG_FEATURES_SUPPORTED)
12757   mng_info->equal_palettes=MagickFalse;
12758 #endif
12759   do
12760   {
12761     if (mng_info->adjoin)
12762     {
12763 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12764     defined(PNG_MNG_FEATURES_SUPPORTED)
12765     /*
12766       If we aren't using a global palette for the entire MNG, check to
12767       see if we can use one for two or more consecutive images.
12768     */
12769     if (need_local_plte && use_global_plte && !all_images_are_gray)
12770       {
12771         if (mng_info->IsPalette)
12772           {
12773             /*
12774               When equal_palettes is true, this image has the same palette
12775               as the previous PseudoClass image
12776             */
12777             mng_info->have_write_global_plte=mng_info->equal_palettes;
12778             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12779             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12780               {
12781                 /*
12782                   Write MNG PLTE chunk
12783                 */
12784                 size_t
12785                   data_length;
12786
12787                 data_length=3*image->colors;
12788                 (void) WriteBlobMSBULong(image,data_length);
12789                 PNGType(chunk,mng_PLTE);
12790                 LogPNGChunk(logging,mng_PLTE,data_length);
12791
12792                 for (i=0; i < (ssize_t) image->colors; i++)
12793                 {
12794                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12795                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12796                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12797                 }
12798
12799                 (void) WriteBlob(image,data_length+4,chunk);
12800                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12801                    (uInt) (data_length+4)));
12802                 mng_info->have_write_global_plte=MagickTrue;
12803               }
12804           }
12805         else
12806           mng_info->have_write_global_plte=MagickFalse;
12807       }
12808 #endif
12809     if (need_defi)
12810       {
12811         ssize_t
12812           previous_x,
12813           previous_y;
12814
12815         if (scene)
12816           {
12817             previous_x=mng_info->page.x;
12818             previous_y=mng_info->page.y;
12819           }
12820         else
12821           {
12822             previous_x=0;
12823             previous_y=0;
12824           }
12825         mng_info->page=image->page;
12826         if ((mng_info->page.x !=  previous_x) ||
12827             (mng_info->page.y != previous_y))
12828           {
12829              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
12830              PNGType(chunk,mng_DEFI);
12831              LogPNGChunk(logging,mng_DEFI,12L);
12832              chunk[4]=0; /* object 0 MSB */
12833              chunk[5]=0; /* object 0 LSB */
12834              chunk[6]=0; /* visible  */
12835              chunk[7]=0; /* abstract */
12836              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12837              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12838              (void) WriteBlob(image,16,chunk);
12839              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12840           }
12841       }
12842     }
12843
12844    mng_info->write_mng=write_mng;
12845
12846    if ((int) image->dispose >= 3)
12847      mng_info->framing_mode=3;
12848
12849    if (mng_info->need_fram && mng_info->adjoin &&
12850        ((image->delay != mng_info->delay) ||
12851         (mng_info->framing_mode != mng_info->old_framing_mode)))
12852      {
12853        if (image->delay == mng_info->delay)
12854          {
12855            /*
12856              Write a MNG FRAM chunk with the new framing mode.
12857            */
12858            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
12859            PNGType(chunk,mng_FRAM);
12860            LogPNGChunk(logging,mng_FRAM,1L);
12861            chunk[4]=(unsigned char) mng_info->framing_mode;
12862            (void) WriteBlob(image,5,chunk);
12863            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12864          }
12865        else
12866          {
12867            /*
12868              Write a MNG FRAM chunk with the delay.
12869            */
12870            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
12871            PNGType(chunk,mng_FRAM);
12872            LogPNGChunk(logging,mng_FRAM,10L);
12873            chunk[4]=(unsigned char) mng_info->framing_mode;
12874            chunk[5]=0;  /* frame name separator (no name) */
12875            chunk[6]=2;  /* flag for changing default delay */
12876            chunk[7]=0;  /* flag for changing frame timeout */
12877            chunk[8]=0;  /* flag for changing frame clipping */
12878            chunk[9]=0;  /* flag for changing frame sync_id */
12879            PNGLong(chunk+10,(png_uint_32)
12880              ((mng_info->ticks_per_second*
12881              image->delay)/MagickMax(image->ticks_per_second,1)));
12882            (void) WriteBlob(image,14,chunk);
12883            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12884            mng_info->delay=(png_uint_32) image->delay;
12885          }
12886        mng_info->old_framing_mode=mng_info->framing_mode;
12887      }
12888
12889 #if defined(JNG_SUPPORTED)
12890    if (image_info->compression == JPEGCompression)
12891      {
12892        ImageInfo
12893          *write_info;
12894
12895        if (logging != MagickFalse)
12896          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12897            "  Writing JNG object.");
12898        /* To do: specify the desired alpha compression method. */
12899        write_info=CloneImageInfo(image_info);
12900        write_info->compression=UndefinedCompression;
12901        status=WriteOneJNGImage(mng_info,write_info,image);
12902        write_info=DestroyImageInfo(write_info);
12903      }
12904    else
12905 #endif
12906      {
12907        if (logging != MagickFalse)
12908          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12909            "  Writing PNG object.");
12910
12911        mng_info->need_blob = MagickFalse;
12912        mng_info->ping_preserve_colormap = MagickFalse;
12913
12914        /* We don't want any ancillary chunks written */
12915        mng_info->ping_exclude_bKGD=MagickTrue;
12916        mng_info->ping_exclude_cHRM=MagickTrue;
12917        mng_info->ping_exclude_date=MagickTrue;
12918        mng_info->ping_exclude_EXIF=MagickTrue;
12919        mng_info->ping_exclude_gAMA=MagickTrue;
12920        mng_info->ping_exclude_iCCP=MagickTrue;
12921        /* mng_info->ping_exclude_iTXt=MagickTrue; */
12922        mng_info->ping_exclude_oFFs=MagickTrue;
12923        mng_info->ping_exclude_pHYs=MagickTrue;
12924        mng_info->ping_exclude_sRGB=MagickTrue;
12925        mng_info->ping_exclude_tEXt=MagickTrue;
12926        mng_info->ping_exclude_tRNS=MagickTrue;
12927        mng_info->ping_exclude_vpAg=MagickTrue;
12928        mng_info->ping_exclude_zCCP=MagickTrue;
12929        mng_info->ping_exclude_zTXt=MagickTrue;
12930
12931        status=WriteOnePNGImage(mng_info,image_info,image);
12932      }
12933
12934     if (status == MagickFalse)
12935       {
12936         MngInfoFreeStruct(mng_info,&have_mng_structure);
12937         (void) CloseBlob(image);
12938         return(MagickFalse);
12939       }
12940     (void) CatchImageException(image);
12941     if (GetNextImageInList(image) == (Image *) NULL)
12942       break;
12943     image=SyncNextImageInList(image);
12944     status=SetImageProgress(image,SaveImagesTag,scene++,
12945       GetImageListLength(image));
12946
12947     if (status == MagickFalse)
12948       break;
12949
12950   } while (mng_info->adjoin);
12951
12952   if (write_mng)
12953     {
12954       while (GetPreviousImageInList(image) != (Image *) NULL)
12955         image=GetPreviousImageInList(image);
12956       /*
12957         Write the MEND chunk.
12958       */
12959       (void) WriteBlobMSBULong(image,0x00000000L);
12960       PNGType(chunk,mng_MEND);
12961       LogPNGChunk(logging,mng_MEND,0L);
12962       (void) WriteBlob(image,4,chunk);
12963       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12964     }
12965   /*
12966     Relinquish resources.
12967   */
12968   (void) CloseBlob(image);
12969   MngInfoFreeStruct(mng_info,&have_mng_structure);
12970
12971   if (logging != MagickFalse)
12972     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
12973
12974   return(MagickTrue);
12975 }
12976 #else /* PNG_LIBPNG_VER > 10011 */
12977
12978 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12979 {
12980   image=image;
12981   printf("Your PNG library is too old: You have libpng-%s\n",
12982      PNG_LIBPNG_VER_STRING);
12983
12984   ThrowBinaryException(CoderError,"PNG library is too old",
12985      image_info->filename);
12986 }
12987
12988 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12989 {
12990   return(WritePNGImage(image_info,image));
12991 }
12992 #endif /* PNG_LIBPNG_VER > 10011 */
12993 #endif