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