blob: 458d23f9b4b77f47e8a7ef543bba4cda1112acb7 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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% %
cristy7e41fe82010-12-04 23:12:08 +000021% Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% 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
41/*
42 Include declarations.
43*/
cristy4c08aed2011-07-01 19:47:50 +000044#include "MagickCore/studio.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colormap.h"
53#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000054#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000055#include "MagickCore/constitute.h"
56#include "MagickCore/enhance.h"
57#include "MagickCore/exception.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/histogram.h"
61#include "MagickCore/image.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/layer.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/MagickCore.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/module.h"
69#include "MagickCore/monitor.h"
70#include "MagickCore/monitor-private.h"
71#include "MagickCore/option.h"
72#include "MagickCore/pixel.h"
73#include "MagickCore/pixel-accessor.h"
74#include "MagickCore/profile.h"
75#include "MagickCore/property.h"
76#include "MagickCore/quantum-private.h"
77#include "MagickCore/resource_.h"
78#include "MagickCore/semaphore.h"
79#include "MagickCore/quantum-private.h"
80#include "MagickCore/static.h"
81#include "MagickCore/statistic.h"
82#include "MagickCore/string_.h"
83#include "MagickCore/string-private.h"
84#include "MagickCore/transform.h"
85#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000086#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000087
glennrp7ef138c2009-11-10 13:50:20 +000088/* Suppress libpng pedantic warnings that were added in
89 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000090 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000091 * fix any code that generates warnings.
92 */
glennrp991e92a2010-01-28 03:09:00 +000093/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000094/* #define PNG_USE_RESULT The result of this function must be checked */
95/* #define PNG_NORETURN This function does not return */
96/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000097/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000098
99/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +0000100#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +0000101
cristy3ed852e2009-09-05 21:47:34 +0000102#include "png.h"
103#include "zlib.h"
104
105/* ImageMagick differences */
106#define first_scene scene
107
glennrpd5045b42010-03-24 12:40:35 +0000108#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000109/*
110 Optional declarations. Define or undefine them as you like.
111*/
112/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
113
114/*
115 Features under construction. Define these to work on them.
116*/
117#undef MNG_OBJECT_BUFFERS
118#undef MNG_BASI_SUPPORTED
119#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
120#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000121#if defined(MAGICKCORE_JPEG_DELEGATE)
122# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
123#endif
124#if !defined(RGBColorMatchExact)
125#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000126 (((color).red == (target).red) && \
127 ((color).green == (target).green) && \
128 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000129#endif
130
glennrp8e58efd2011-05-20 12:16:29 +0000131/* Macros for left-bit-replication to ensure that pixels
glennrp1a2061f2011-10-18 12:30:45 +0000132 * and PixelInfos all have the same image->depth, and for use
glennrp8e58efd2011-05-20 12:16:29 +0000133 * in PNG8 quantization.
134 */
135
136
137/* LBR01: Replicate top bit */
138
glennrp05001c32011-08-06 13:04:16 +0000139#define LBR01PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000140 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
141 0 : QuantumRange);
142
glennrp91d99252011-06-25 14:30:13 +0000143#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000144 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
145 0 : QuantumRange);
146
glennrp91d99252011-06-25 14:30:13 +0000147#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000148 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
149 0 : QuantumRange);
150
cristy4c08aed2011-07-01 19:47:50 +0000151#define LBR01PacketAlpha(pixelpacket) \
152 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000153 0 : QuantumRange);
154
glennrp91d99252011-06-25 14:30:13 +0000155#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000156 { \
glennrp05001c32011-08-06 13:04:16 +0000157 LBR01PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000158 LBR01PacketGreen((pixelpacket)); \
159 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000160 }
glennrp8e58efd2011-05-20 12:16:29 +0000161
glennrp91d99252011-06-25 14:30:13 +0000162#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000163 { \
glennrp91d99252011-06-25 14:30:13 +0000164 LBR01PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000165 LBR01PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000166 }
glennrp8e58efd2011-05-20 12:16:29 +0000167
cristyef618312011-06-25 12:26:44 +0000168#define LBR01PixelRed(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000169 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000170 0 : QuantumRange);
171
glennrp54cf7972011-08-06 14:28:09 +0000172#define LBR01PixelGreen(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000173 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000174 0 : QuantumRange);
175
glennrp54cf7972011-08-06 14:28:09 +0000176#define LBR01PixelBlue(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000177 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000178 0 : QuantumRange);
179
glennrp54cf7972011-08-06 14:28:09 +0000180#define LBR01PixelAlpha(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000181 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000182 0 : QuantumRange);
183
glennrp54cf7972011-08-06 14:28:09 +0000184#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000185 { \
cristyef618312011-06-25 12:26:44 +0000186 LBR01PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000187 LBR01PixelGreen((pixel)); \
188 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000189 }
glennrp8e58efd2011-05-20 12:16:29 +0000190
glennrp54cf7972011-08-06 14:28:09 +0000191#define LBR01PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000192 { \
glennrp54cf7972011-08-06 14:28:09 +0000193 LBR01PixelRGB((pixel)); \
194 LBR01PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000195 }
glennrp8e58efd2011-05-20 12:16:29 +0000196
197/* LBR02: Replicate top 2 bits */
198
glennrp05001c32011-08-06 13:04:16 +0000199#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000200 { \
201 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
202 (pixelpacket).red=ScaleCharToQuantum( \
203 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
204 }
glennrp91d99252011-06-25 14:30:13 +0000205#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000206 { \
207 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
208 (pixelpacket).green=ScaleCharToQuantum( \
209 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
210 }
glennrp91d99252011-06-25 14:30:13 +0000211#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000212 { \
213 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
214 (pixelpacket).blue=ScaleCharToQuantum( \
215 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
216 }
cristy4c08aed2011-07-01 19:47:50 +0000217#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000218 { \
cristy4c08aed2011-07-01 19:47:50 +0000219 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
220 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000221 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
222 }
223
glennrp91d99252011-06-25 14:30:13 +0000224#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000225 { \
glennrp05001c32011-08-06 13:04:16 +0000226 LBR02PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000227 LBR02PacketGreen((pixelpacket)); \
228 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000229 }
glennrp8e58efd2011-05-20 12:16:29 +0000230
glennrp91d99252011-06-25 14:30:13 +0000231#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000232 { \
glennrp91d99252011-06-25 14:30:13 +0000233 LBR02PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000234 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000235 }
glennrp8e58efd2011-05-20 12:16:29 +0000236
cristyef618312011-06-25 12:26:44 +0000237#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000238 { \
cristy4c08aed2011-07-01 19:47:50 +0000239 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000240 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000241 SetPixelRed(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000242 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
243 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000244 }
glennrp54cf7972011-08-06 14:28:09 +0000245#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000246 { \
cristy4c08aed2011-07-01 19:47:50 +0000247 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000248 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000249 SetPixelGreen(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000250 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
251 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000252 }
glennrp54cf7972011-08-06 14:28:09 +0000253#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000254 { \
255 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000256 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
257 SetPixelBlue(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000258 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
259 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000260 }
glennrp54cf7972011-08-06 14:28:09 +0000261#define LBR02PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000262 { \
263 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000264 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
265 SetPixelAlpha(image, ScaleCharToQuantum( \
glennrp847370c2011-07-05 17:37:15 +0000266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
267 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000268 }
269
glennrp54cf7972011-08-06 14:28:09 +0000270#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000271 { \
cristyef618312011-06-25 12:26:44 +0000272 LBR02PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000273 LBR02PixelGreen((pixel)); \
274 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000275 }
glennrp8e58efd2011-05-20 12:16:29 +0000276
glennrp54cf7972011-08-06 14:28:09 +0000277#define LBR02PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000278 { \
glennrp54cf7972011-08-06 14:28:09 +0000279 LBR02PixelRGB((pixel)); \
280 LBR02PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000281 }
glennrp8e58efd2011-05-20 12:16:29 +0000282
283/* LBR03: Replicate top 3 bits (only used with opaque pixels during
284 PNG8 quantization) */
285
glennrp05001c32011-08-06 13:04:16 +0000286#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000287 { \
288 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
289 (pixelpacket).red=ScaleCharToQuantum( \
290 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
291 }
glennrp91d99252011-06-25 14:30:13 +0000292#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000293 { \
294 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
295 (pixelpacket).green=ScaleCharToQuantum( \
296 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
297 }
glennrp91d99252011-06-25 14:30:13 +0000298#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000299 { \
300 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
301 (pixelpacket).blue=ScaleCharToQuantum( \
302 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
303 }
304
glennrp91d99252011-06-25 14:30:13 +0000305#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000306 { \
glennrp05001c32011-08-06 13:04:16 +0000307 LBR03PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000308 LBR03PacketGreen((pixelpacket)); \
309 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000310 }
glennrp8e58efd2011-05-20 12:16:29 +0000311
cristyef618312011-06-25 12:26:44 +0000312#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000313 { \
cristy4c08aed2011-07-01 19:47:50 +0000314 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000315 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000316 SetPixelRed(image, ScaleCharToQuantum( \
317 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000318 }
cristy4c08aed2011-07-01 19:47:50 +0000319#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000320 { \
cristy4c08aed2011-07-01 19:47:50 +0000321 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000322 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000323 SetPixelGreen(image, ScaleCharToQuantum( \
324 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000325 }
cristy4c08aed2011-07-01 19:47:50 +0000326#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000327 { \
cristy4c08aed2011-07-01 19:47:50 +0000328 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000329 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000330 SetPixelBlue(image, ScaleCharToQuantum( \
331 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000332 }
333
cristy4c08aed2011-07-01 19:47:50 +0000334#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000335 { \
cristyef618312011-06-25 12:26:44 +0000336 LBR03PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000337 LBR03Green((pixel)); \
338 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000339 }
glennrp8e58efd2011-05-20 12:16:29 +0000340
341/* LBR04: Replicate top 4 bits */
342
glennrp05001c32011-08-06 13:04:16 +0000343#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
346 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
347 }
glennrp91d99252011-06-25 14:30:13 +0000348#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000349 { \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
351 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
352 }
glennrp91d99252011-06-25 14:30:13 +0000353#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000354 { \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
356 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
357 }
cristy4c08aed2011-07-01 19:47:50 +0000358#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000359 { \
cristy4c08aed2011-07-01 19:47:50 +0000360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
361 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000362 }
363
glennrp91d99252011-06-25 14:30:13 +0000364#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000365 { \
glennrp05001c32011-08-06 13:04:16 +0000366 LBR04PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000367 LBR04PacketGreen((pixelpacket)); \
368 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000369 }
glennrp8e58efd2011-05-20 12:16:29 +0000370
glennrp91d99252011-06-25 14:30:13 +0000371#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000372 { \
glennrp91d99252011-06-25 14:30:13 +0000373 LBR04PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000374 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000375 }
glennrp8e58efd2011-05-20 12:16:29 +0000376
cristyef618312011-06-25 12:26:44 +0000377#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000378 { \
cristy4c08aed2011-07-01 19:47:50 +0000379 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000380 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000381 SetPixelRed(image,\
382 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000383 }
glennrp54cf7972011-08-06 14:28:09 +0000384#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000385 { \
cristy4c08aed2011-07-01 19:47:50 +0000386 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000387 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000388 SetPixelGreen(image,\
389 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000390 }
glennrp54cf7972011-08-06 14:28:09 +0000391#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000392 { \
393 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000394 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
395 SetPixelBlue(image,\
396 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000397 }
glennrp54cf7972011-08-06 14:28:09 +0000398#define LBR04PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000399 { \
400 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000401 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
402 SetPixelAlpha(image,\
403 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000404 }
405
glennrp54cf7972011-08-06 14:28:09 +0000406#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000407 { \
cristyef618312011-06-25 12:26:44 +0000408 LBR04PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000409 LBR04PixelGreen((pixel)); \
410 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000411 }
glennrp8e58efd2011-05-20 12:16:29 +0000412
glennrp54cf7972011-08-06 14:28:09 +0000413#define LBR04PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000414 { \
glennrp54cf7972011-08-06 14:28:09 +0000415 LBR04PixelRGB((pixel)); \
416 LBR04PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000417 }
glennrp8e58efd2011-05-20 12:16:29 +0000418
419
420/* LBR08: Replicate top 8 bits */
421
glennrp05001c32011-08-06 13:04:16 +0000422#define LBR08PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000423 { \
424 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
425 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
426 }
glennrp91d99252011-06-25 14:30:13 +0000427#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000428 { \
429 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
430 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
431 }
glennrp91d99252011-06-25 14:30:13 +0000432#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000433 { \
434 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
435 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
436 }
cristy4c08aed2011-07-01 19:47:50 +0000437#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000438 { \
cristy4c08aed2011-07-01 19:47:50 +0000439 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
440 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000441 }
442
glennrp91d99252011-06-25 14:30:13 +0000443#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000444 { \
glennrp05001c32011-08-06 13:04:16 +0000445 LBR08PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000446 LBR08PacketGreen((pixelpacket)); \
447 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000448 }
glennrp8e58efd2011-05-20 12:16:29 +0000449
glennrp91d99252011-06-25 14:30:13 +0000450#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000451 { \
glennrp91d99252011-06-25 14:30:13 +0000452 LBR08PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000453 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000454 }
glennrp8e58efd2011-05-20 12:16:29 +0000455
cristyef618312011-06-25 12:26:44 +0000456#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000457 { \
458 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000459 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
460 SetPixelRed(image,\
461 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000462 }
glennrp54cf7972011-08-06 14:28:09 +0000463#define LBR08PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000464 { \
465 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000466 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
467 SetPixelGreen(image,\
468 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000469 }
glennrp54cf7972011-08-06 14:28:09 +0000470#define LBR08PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000471 { \
472 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000473 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
474 SetPixelBlue(image,\
475 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000476 }
glennrp54cf7972011-08-06 14:28:09 +0000477#define LBR08PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000478 { \
479 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000480 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
481 SetPixelAlpha(image,\
482 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000483 }
484
glennrp54cf7972011-08-06 14:28:09 +0000485#define LBR08PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000486 { \
cristyef618312011-06-25 12:26:44 +0000487 LBR08PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000488 LBR08PixelGreen((pixel)); \
489 LBR08PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000490 }
glennrp8e58efd2011-05-20 12:16:29 +0000491
glennrp54cf7972011-08-06 14:28:09 +0000492#define LBR08PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000493 { \
glennrp54cf7972011-08-06 14:28:09 +0000494 LBR08PixelRGB((pixel)); \
495 LBR08PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000496 }
glennrp8e58efd2011-05-20 12:16:29 +0000497
498
499/* LBR16: Replicate top 16 bits */
500
glennrp05001c32011-08-06 13:04:16 +0000501#define LBR16PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000502 { \
503 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
504 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
505 }
glennrp91d99252011-06-25 14:30:13 +0000506#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000507 { \
508 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
509 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
510 }
glennrp91d99252011-06-25 14:30:13 +0000511#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000512 { \
513 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
514 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
515 }
cristy4c08aed2011-07-01 19:47:50 +0000516#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000517 { \
cristy4c08aed2011-07-01 19:47:50 +0000518 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
519 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000520 }
521
glennrp91d99252011-06-25 14:30:13 +0000522#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000523 { \
glennrp05001c32011-08-06 13:04:16 +0000524 LBR16PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000525 LBR16PacketGreen((pixelpacket)); \
526 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000527 }
glennrp8e58efd2011-05-20 12:16:29 +0000528
glennrp91d99252011-06-25 14:30:13 +0000529#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000530 { \
glennrp91d99252011-06-25 14:30:13 +0000531 LBR16PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000532 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000533 }
glennrp8e58efd2011-05-20 12:16:29 +0000534
cristyef618312011-06-25 12:26:44 +0000535#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000536 { \
537 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000538 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
539 SetPixelRed(image,\
540 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000541 }
glennrp54cf7972011-08-06 14:28:09 +0000542#define LBR16PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000543 { \
544 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000545 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
546 SetPixelGreen(image,\
547 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000548 }
glennrp54cf7972011-08-06 14:28:09 +0000549#define LBR16PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000550 { \
551 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000552 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
553 SetPixelBlue(image,\
554 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000555 }
glennrp54cf7972011-08-06 14:28:09 +0000556#define LBR16PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000557 { \
558 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000559 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
560 SetPixelAlpha(image,\
561 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000562 }
563
glennrp54cf7972011-08-06 14:28:09 +0000564#define LBR16PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000565 { \
cristyef618312011-06-25 12:26:44 +0000566 LBR16PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000567 LBR16PixelGreen((pixel)); \
568 LBR16PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000569 }
glennrp8e58efd2011-05-20 12:16:29 +0000570
glennrp54cf7972011-08-06 14:28:09 +0000571#define LBR16PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000572 { \
glennrp54cf7972011-08-06 14:28:09 +0000573 LBR16PixelRGB((pixel)); \
574 LBR16PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000575 }
glennrp8e58efd2011-05-20 12:16:29 +0000576
cristy3ed852e2009-09-05 21:47:34 +0000577/*
578 Establish thread safety.
579 setjmp/longjmp is claimed to be safe on these platforms:
580 setjmp/longjmp is alleged to be unsafe on these platforms:
581*/
582#ifndef SETJMP_IS_THREAD_SAFE
583#define PNG_SETJMP_NOT_THREAD_SAFE
584#endif
585
586#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
587static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000588 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000589#endif
590
591/*
592 This temporary until I set up malloc'ed object attributes array.
593 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
594 waste more memory.
595*/
596#define MNG_MAX_OBJECTS 256
597
598/*
599 If this not defined, spec is interpreted strictly. If it is
600 defined, an attempt will be made to recover from some errors,
601 including
602 o global PLTE too short
603*/
604#undef MNG_LOOSE
605
606/*
607 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
608 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
609 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
610 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
611 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
612 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
613 will be enabled by default in libpng-1.2.0.
614*/
cristy3ed852e2009-09-05 21:47:34 +0000615#ifdef PNG_MNG_FEATURES_SUPPORTED
616# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
617# define PNG_READ_EMPTY_PLTE_SUPPORTED
618# endif
619# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
620# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
621# endif
622#endif
623
624/*
cristybb503372010-05-27 20:51:26 +0000625 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000626 This macro is only defined in libpng-1.0.3 and later.
627 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
628*/
629#ifndef PNG_UINT_31_MAX
630#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
631#endif
632
633/*
634 Constant strings for known chunk types. If you need to add a chunk,
635 add a string holding the name here. To make the code more
636 portable, we use ASCII numbers like this, not characters.
637*/
638
639static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
640static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
641static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
642static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
643static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
644static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
645static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
646static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
647static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
648static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
649static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
650static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
651static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
652static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
653static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
654static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
655static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
656static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
657static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
658static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
659static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
660static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
661static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
662static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
663static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
664static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
665static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
666static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
667static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
668static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
669static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
670static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
671static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
672static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
673
674#if defined(JNG_SUPPORTED)
675static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
676static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
677static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
678static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
679static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
680static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
681#endif
682
683/*
684Other known chunks that are not yet supported by ImageMagick:
685static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
686static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
687static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
688static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
689static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
690static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
691static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
692static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
693*/
694
695typedef struct _MngBox
696{
cristy8182b072010-05-30 20:10:53 +0000697 long
cristy3ed852e2009-09-05 21:47:34 +0000698 left,
699 right,
700 top,
701 bottom;
702} MngBox;
703
704typedef struct _MngPair
705{
cristy8182b072010-05-30 20:10:53 +0000706 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000707 a,
708 b;
709} MngPair;
710
711#ifdef MNG_OBJECT_BUFFERS
712typedef struct _MngBuffer
713{
714
cristybb503372010-05-27 20:51:26 +0000715 size_t
cristy3ed852e2009-09-05 21:47:34 +0000716 height,
717 width;
718
719 Image
720 *image;
721
722 png_color
723 plte[256];
724
725 int
726 reference_count;
727
728 unsigned char
729 alpha_sample_depth,
730 compression_method,
731 color_type,
732 concrete,
733 filter_method,
734 frozen,
735 image_type,
736 interlace_method,
737 pixel_sample_depth,
738 plte_length,
739 sample_depth,
740 viewable;
741} MngBuffer;
742#endif
743
744typedef struct _MngInfo
745{
746
747#ifdef MNG_OBJECT_BUFFERS
748 MngBuffer
749 *ob[MNG_MAX_OBJECTS];
750#endif
751
752 Image *
753 image;
754
755 RectangleInfo
756 page;
757
758 int
759 adjoin,
760#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
761 bytes_in_read_buffer,
762 found_empty_plte,
763#endif
764 equal_backgrounds,
765 equal_chrms,
766 equal_gammas,
767#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
768 defined(PNG_MNG_FEATURES_SUPPORTED)
769 equal_palettes,
770#endif
771 equal_physs,
772 equal_srgbs,
773 framing_mode,
774 have_global_bkgd,
775 have_global_chrm,
776 have_global_gama,
777 have_global_phys,
778 have_global_sbit,
779 have_global_srgb,
780 have_saved_bkgd_index,
781 have_write_global_chrm,
782 have_write_global_gama,
783 have_write_global_plte,
784 have_write_global_srgb,
785 need_fram,
786 object_id,
787 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000788 saved_bkgd_index;
789
790 int
791 new_number_colors;
792
cristybb503372010-05-27 20:51:26 +0000793 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000794 image_found,
795 loop_count[256],
796 loop_iteration[256],
797 scenes_found,
798 x_off[MNG_MAX_OBJECTS],
799 y_off[MNG_MAX_OBJECTS];
800
801 MngBox
802 clip,
803 frame,
804 image_box,
805 object_clip[MNG_MAX_OBJECTS];
806
807 unsigned char
808 /* These flags could be combined into one byte */
809 exists[MNG_MAX_OBJECTS],
810 frozen[MNG_MAX_OBJECTS],
811 loop_active[256],
812 invisible[MNG_MAX_OBJECTS],
813 viewable[MNG_MAX_OBJECTS];
814
815 MagickOffsetType
816 loop_jump[256];
817
818 png_colorp
819 global_plte;
820
821 png_color_8
822 global_sbit;
823
824 png_byte
825#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
826 read_buffer[8],
827#endif
828 global_trns[256];
829
830 float
831 global_gamma;
832
833 ChromaticityInfo
834 global_chrm;
835
836 RenderingIntent
837 global_srgb_intent;
838
cristy35ef8242010-06-03 16:24:13 +0000839 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000840 delay,
841 global_plte_length,
842 global_trns_length,
843 global_x_pixels_per_unit,
844 global_y_pixels_per_unit,
845 mng_width,
846 mng_height,
847 ticks_per_second;
848
glennrpb9cfe272010-12-21 15:08:06 +0000849 MagickBooleanType
850 need_blob;
851
cristy3ed852e2009-09-05 21:47:34 +0000852 unsigned int
853 IsPalette,
854 global_phys_unit_type,
855 basi_warning,
856 clon_warning,
857 dhdr_warning,
858 jhdr_warning,
859 magn_warning,
860 past_warning,
861 phyg_warning,
862 phys_warning,
863 sbit_warning,
864 show_warning,
865 mng_type,
866 write_mng,
867 write_png_colortype,
868 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000869 write_png_compression_level,
870 write_png_compression_strategy,
871 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000872 write_png8,
873 write_png24,
874 write_png32;
875
876#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000877 size_t
cristy3ed852e2009-09-05 21:47:34 +0000878 basi_width,
879 basi_height;
880
881 unsigned int
882 basi_depth,
883 basi_color_type,
884 basi_compression_method,
885 basi_filter_type,
886 basi_interlace_method,
887 basi_red,
888 basi_green,
889 basi_blue,
890 basi_alpha,
891 basi_viewable;
892#endif
893
894 png_uint_16
895 magn_first,
896 magn_last,
897 magn_mb,
898 magn_ml,
899 magn_mr,
900 magn_mt,
901 magn_mx,
902 magn_my,
903 magn_methx,
904 magn_methy;
905
cristy101ab702011-10-13 13:06:32 +0000906 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000907 mng_global_bkgd;
908
glennrp26f37912010-12-23 16:22:42 +0000909 /* Added at version 6.6.6-7 */
910 MagickBooleanType
911 ping_exclude_bKGD,
912 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000913 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000914 ping_exclude_EXIF,
915 ping_exclude_gAMA,
916 ping_exclude_iCCP,
917 /* ping_exclude_iTXt, */
918 ping_exclude_oFFs,
919 ping_exclude_pHYs,
920 ping_exclude_sRGB,
921 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000922 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000923 ping_exclude_vpAg,
924 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000925 ping_exclude_zTXt,
926 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000927
cristy3ed852e2009-09-05 21:47:34 +0000928} MngInfo;
929#endif /* VER */
930
931/*
932 Forward declarations.
933*/
934static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000935 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000936
cristy3ed852e2009-09-05 21:47:34 +0000937static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000938 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000939
cristy3ed852e2009-09-05 21:47:34 +0000940#if defined(JNG_SUPPORTED)
941static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000942 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000943#endif
944
glennrp0c3e06b2010-11-19 13:45:02 +0000945#if PNG_LIBPNG_VER > 10011
946
glennrpfd05d622011-02-25 04:10:33 +0000947
glennrp0c3e06b2010-11-19 13:45:02 +0000948#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
949static MagickBooleanType
glennrpfd05d622011-02-25 04:10:33 +0000950LosslessReduceDepthOK(Image *image)
glennrp0c3e06b2010-11-19 13:45:02 +0000951{
glennrp67b9c1a2011-04-22 18:47:36 +0000952 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
953 *
954 * This is true if the high byte and the next highest byte of
955 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000956 * are equal to each other. We check this by seeing if the samples
957 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000958 *
959 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000960 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000961 */
962
glennrp3faa9a32011-04-23 14:00:25 +0000963#define QuantumToCharToQuantumEqQuantum(quantum) \
964 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
965
glennrp0c3e06b2010-11-19 13:45:02 +0000966 MagickBooleanType
967 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000968
glennrp03e11f62011-04-22 13:30:16 +0000969 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000970 {
971
cristy4c08aed2011-07-01 19:47:50 +0000972 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000973 *p;
974
975 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000976 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
977 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
979 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000980
981 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
982 {
983 int indx;
984
985 for (indx=0; indx < (ssize_t) image->colors; indx++)
986 {
glennrp3faa9a32011-04-23 14:00:25 +0000987 ok_to_reduce=(
988 QuantumToCharToQuantumEqQuantum(
989 image->colormap[indx].red) &&
990 QuantumToCharToQuantumEqQuantum(
991 image->colormap[indx].green) &&
992 QuantumToCharToQuantumEqQuantum(
993 image->colormap[indx].blue)) ?
994 MagickTrue : MagickFalse;
995
glennrp0c3e06b2010-11-19 13:45:02 +0000996 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000997 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000998 }
999 }
1000
1001 if ((ok_to_reduce != MagickFalse) &&
1002 (image->storage_class != PseudoClass))
1003 {
1004 ssize_t
1005 y;
1006
1007 register ssize_t
1008 x;
1009
1010 for (y=0; y < (ssize_t) image->rows; y++)
1011 {
1012 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1013
cristy4c08aed2011-07-01 19:47:50 +00001014 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001015 {
1016 ok_to_reduce = MagickFalse;
1017 break;
1018 }
1019
1020 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1021 {
glennrp3faa9a32011-04-23 14:00:25 +00001022 ok_to_reduce=
cristy4c08aed2011-07-01 19:47:50 +00001023 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1024 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001026 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001027
1028 if (ok_to_reduce == MagickFalse)
1029 break;
1030
cristyed231572011-07-14 02:18:59 +00001031 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001032 }
glennrp8640fb52010-11-23 15:48:26 +00001033 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001034 break;
1035 }
1036 }
1037
1038 if (ok_to_reduce != MagickFalse)
1039 {
glennrp0c3e06b2010-11-19 13:45:02 +00001040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001041 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001042 }
glennrpa6a06632011-01-19 15:15:34 +00001043 else
1044 {
1045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001046 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001047 }
glennrp0c3e06b2010-11-19 13:45:02 +00001048 }
1049
1050 return ok_to_reduce;
1051}
1052#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1053
glennrpe610a072010-08-05 17:08:46 +00001054static int
glennrpcf002022011-01-30 02:38:15 +00001055Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001056{
glennrpe610a072010-08-05 17:08:46 +00001057 switch (intent)
1058 {
1059 case PerceptualIntent:
1060 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001061
glennrpe610a072010-08-05 17:08:46 +00001062 case RelativeIntent:
1063 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001064
glennrpe610a072010-08-05 17:08:46 +00001065 case SaturationIntent:
1066 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001067
glennrpe610a072010-08-05 17:08:46 +00001068 case AbsoluteIntent:
1069 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001070
glennrpe610a072010-08-05 17:08:46 +00001071 default:
1072 return -1;
1073 }
1074}
1075
1076static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001077Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001078{
glennrpcf002022011-01-30 02:38:15 +00001079 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001080 {
1081 case 0:
1082 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001083
glennrpe610a072010-08-05 17:08:46 +00001084 case 1:
1085 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001086
glennrpe610a072010-08-05 17:08:46 +00001087 case 2:
1088 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001089
glennrpe610a072010-08-05 17:08:46 +00001090 case 3:
1091 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001092
glennrpe610a072010-08-05 17:08:46 +00001093 default:
1094 return UndefinedIntent;
1095 }
1096}
1097
cristybb503372010-05-27 20:51:26 +00001098static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001099{
1100 if (x > y)
1101 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001102
cristy3ed852e2009-09-05 21:47:34 +00001103 return(y);
1104}
glennrp0c3e06b2010-11-19 13:45:02 +00001105
cristybb503372010-05-27 20:51:26 +00001106static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001107{
1108 if (x < y)
1109 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001110
cristy3ed852e2009-09-05 21:47:34 +00001111 return(y);
1112}
glennrp0c3e06b2010-11-19 13:45:02 +00001113
cristy3ed852e2009-09-05 21:47:34 +00001114
1115/*
1116%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117% %
1118% %
1119% %
1120% I m a g e I s G r a y %
1121% %
1122% %
1123% %
1124%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125% %
cristy4c08aed2011-07-01 19:47:50 +00001126% Like IsImageGray except does not change DirectClass to PseudoClass %
cristy3ed852e2009-09-05 21:47:34 +00001127% %
1128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1129*/
1130static MagickBooleanType ImageIsGray(Image *image)
1131{
cristy4c08aed2011-07-01 19:47:50 +00001132 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001133 *p;
1134
cristybb503372010-05-27 20:51:26 +00001135 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001136 i,
1137 x,
1138 y;
1139
1140 assert(image != (Image *) NULL);
1141 assert(image->signature == MagickSignature);
1142 if (image->debug != MagickFalse)
1143 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1144
1145 if (image->storage_class == PseudoClass)
1146 {
cristybb503372010-05-27 20:51:26 +00001147 for (i=0; i < (ssize_t) image->colors; i++)
cristy101ab702011-10-13 13:06:32 +00001148 if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001149 return(MagickFalse);
1150 return(MagickTrue);
1151 }
cristybb503372010-05-27 20:51:26 +00001152 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001153 {
1154 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00001155 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001156 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001157 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001158 {
cristy4c08aed2011-07-01 19:47:50 +00001159 if (IsPixelGray(image,p) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001160 return(MagickFalse);
cristyed231572011-07-14 02:18:59 +00001161 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001162 }
1163 }
1164 return(MagickTrue);
1165}
glennrpd5045b42010-03-24 12:40:35 +00001166#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001167#endif /* MAGICKCORE_PNG_DELEGATE */
1168
1169/*
1170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171% %
1172% %
1173% %
1174% I s M N G %
1175% %
1176% %
1177% %
1178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179%
1180% IsMNG() returns MagickTrue if the image format type, identified by the
1181% magick string, is MNG.
1182%
1183% The format of the IsMNG method is:
1184%
1185% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1186%
1187% A description of each parameter follows:
1188%
1189% o magick: compare image format pattern against these bytes.
1190%
1191% o length: Specifies the length of the magick string.
1192%
1193%
1194*/
1195static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1196{
1197 if (length < 8)
1198 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001199
cristy3ed852e2009-09-05 21:47:34 +00001200 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1201 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001202
cristy3ed852e2009-09-05 21:47:34 +00001203 return(MagickFalse);
1204}
1205
1206/*
1207%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1208% %
1209% %
1210% %
1211% I s J N G %
1212% %
1213% %
1214% %
1215%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216%
1217% IsJNG() returns MagickTrue if the image format type, identified by the
1218% magick string, is JNG.
1219%
1220% The format of the IsJNG method is:
1221%
1222% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1223%
1224% A description of each parameter follows:
1225%
1226% o magick: compare image format pattern against these bytes.
1227%
1228% o length: Specifies the length of the magick string.
1229%
1230%
1231*/
1232static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1233{
1234 if (length < 8)
1235 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001236
cristy3ed852e2009-09-05 21:47:34 +00001237 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1238 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001239
cristy3ed852e2009-09-05 21:47:34 +00001240 return(MagickFalse);
1241}
1242
1243/*
1244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245% %
1246% %
1247% %
1248% I s P N G %
1249% %
1250% %
1251% %
1252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253%
1254% IsPNG() returns MagickTrue if the image format type, identified by the
1255% magick string, is PNG.
1256%
1257% The format of the IsPNG method is:
1258%
1259% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1260%
1261% A description of each parameter follows:
1262%
1263% o magick: compare image format pattern against these bytes.
1264%
1265% o length: Specifies the length of the magick string.
1266%
1267*/
1268static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1269{
1270 if (length < 8)
1271 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001272
cristy3ed852e2009-09-05 21:47:34 +00001273 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1274 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001275
cristy3ed852e2009-09-05 21:47:34 +00001276 return(MagickFalse);
1277}
1278
1279#if defined(MAGICKCORE_PNG_DELEGATE)
1280#if defined(__cplusplus) || defined(c_plusplus)
1281extern "C" {
1282#endif
1283
glennrpd5045b42010-03-24 12:40:35 +00001284#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001285static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001286{
1287 unsigned char
1288 buffer[4];
1289
1290 assert(image != (Image *) NULL);
1291 assert(image->signature == MagickSignature);
1292 buffer[0]=(unsigned char) (value >> 24);
1293 buffer[1]=(unsigned char) (value >> 16);
1294 buffer[2]=(unsigned char) (value >> 8);
1295 buffer[3]=(unsigned char) value;
1296 return((size_t) WriteBlob(image,4,buffer));
1297}
1298
1299static void PNGLong(png_bytep p,png_uint_32 value)
1300{
1301 *p++=(png_byte) ((value >> 24) & 0xff);
1302 *p++=(png_byte) ((value >> 16) & 0xff);
1303 *p++=(png_byte) ((value >> 8) & 0xff);
1304 *p++=(png_byte) (value & 0xff);
1305}
1306
glennrpa521b2f2010-10-29 04:11:03 +00001307#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001308static void PNGsLong(png_bytep p,png_int_32 value)
1309{
1310 *p++=(png_byte) ((value >> 24) & 0xff);
1311 *p++=(png_byte) ((value >> 16) & 0xff);
1312 *p++=(png_byte) ((value >> 8) & 0xff);
1313 *p++=(png_byte) (value & 0xff);
1314}
glennrpa521b2f2010-10-29 04:11:03 +00001315#endif
cristy3ed852e2009-09-05 21:47:34 +00001316
1317static void PNGShort(png_bytep p,png_uint_16 value)
1318{
1319 *p++=(png_byte) ((value >> 8) & 0xff);
1320 *p++=(png_byte) (value & 0xff);
1321}
1322
1323static void PNGType(png_bytep p,png_bytep type)
1324{
1325 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1326}
1327
glennrp03812ae2010-12-24 01:31:34 +00001328static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1329 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001330{
1331 if (logging != MagickFalse)
1332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001333 " Writing %c%c%c%c chunk, length: %.20g",
1334 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001335}
glennrpd5045b42010-03-24 12:40:35 +00001336#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001337
1338#if defined(__cplusplus) || defined(c_plusplus)
1339}
1340#endif
1341
glennrpd5045b42010-03-24 12:40:35 +00001342#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001343/*
1344%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1345% %
1346% %
1347% %
1348% R e a d P N G I m a g e %
1349% %
1350% %
1351% %
1352%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1353%
1354% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1355% Multiple-image Network Graphics (MNG) image file and returns it. It
1356% allocates the memory necessary for the new Image structure and returns a
1357% pointer to the new image or set of images.
1358%
1359% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1360%
1361% The format of the ReadPNGImage method is:
1362%
1363% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1364%
1365% A description of each parameter follows:
1366%
1367% o image_info: the image info.
1368%
1369% o exception: return any errors or warnings in this structure.
1370%
1371% To do, more or less in chronological order (as of version 5.5.2,
1372% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1373%
1374% Get 16-bit cheap transparency working.
1375%
1376% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1377%
1378% Preserve all unknown and not-yet-handled known chunks found in input
1379% PNG file and copy them into output PNG files according to the PNG
1380% copying rules.
1381%
1382% (At this point, PNG encoding should be in full MNG compliance)
1383%
1384% Provide options for choice of background to use when the MNG BACK
1385% chunk is not present or is not mandatory (i.e., leave transparent,
1386% user specified, MNG BACK, PNG bKGD)
1387%
1388% Implement LOOP/ENDL [done, but could do discretionary loops more
1389% efficiently by linking in the duplicate frames.].
1390%
1391% Decode and act on the MHDR simplicity profile (offer option to reject
1392% files or attempt to process them anyway when the profile isn't LC or VLC).
1393%
1394% Upgrade to full MNG without Delta-PNG.
1395%
1396% o BACK [done a while ago except for background image ID]
1397% o MOVE [done 15 May 1999]
1398% o CLIP [done 15 May 1999]
1399% o DISC [done 19 May 1999]
1400% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1401% o SEEK [partially done 19 May 1999 (discard function only)]
1402% o SHOW
1403% o PAST
1404% o BASI
1405% o MNG-level tEXt/iTXt/zTXt
1406% o pHYg
1407% o pHYs
1408% o sBIT
1409% o bKGD
1410% o iTXt (wait for libpng implementation).
1411%
1412% Use the scene signature to discover when an identical scene is
1413% being reused, and just point to the original image->exception instead
1414% of storing another set of pixels. This not specific to MNG
1415% but could be applied generally.
1416%
1417% Upgrade to full MNG with Delta-PNG.
1418%
1419% JNG tEXt/iTXt/zTXt
1420%
1421% We will not attempt to read files containing the CgBI chunk.
1422% They are really Xcode files meant for display on the iPhone.
1423% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001424% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001425% since irretrievable loss of color data has occurred due to the
1426% use of premultiplied alpha.
1427*/
1428
1429#if defined(__cplusplus) || defined(c_plusplus)
1430extern "C" {
1431#endif
1432
1433/*
1434 This the function that does the actual reading of data. It is
1435 the same as the one supplied in libpng, except that it receives the
1436 datastream from the ReadBlob() function instead of standard input.
1437*/
1438static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1439{
1440 Image
1441 *image;
1442
1443 image=(Image *) png_get_io_ptr(png_ptr);
1444 if (length)
1445 {
1446 png_size_t
1447 check;
1448
1449 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1450 if (check != length)
1451 {
1452 char
1453 msg[MaxTextExtent];
1454
cristy3b6fd2e2011-05-20 12:53:50 +00001455 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001456 "Expected %.20g bytes; found %.20g bytes",(double) length,
1457 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001458 png_warning(png_ptr,msg);
1459 png_error(png_ptr,"Read Exception");
1460 }
1461 }
1462}
1463
1464#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1465 !defined(PNG_MNG_FEATURES_SUPPORTED)
1466/* We use mng_get_data() instead of png_get_data() if we have a libpng
1467 * older than libpng-1.0.3a, which was the first to allow the empty
1468 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1469 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1470 * encountered after an empty PLTE, so we have to look ahead for bKGD
1471 * chunks and remove them from the datastream that is passed to libpng,
1472 * and store their contents for later use.
1473 */
1474static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1475{
1476 MngInfo
1477 *mng_info;
1478
1479 Image
1480 *image;
1481
1482 png_size_t
1483 check;
1484
cristybb503372010-05-27 20:51:26 +00001485 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001486 i;
1487
1488 i=0;
1489 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1490 image=(Image *) mng_info->image;
1491 while (mng_info->bytes_in_read_buffer && length)
1492 {
1493 data[i]=mng_info->read_buffer[i];
1494 mng_info->bytes_in_read_buffer--;
1495 length--;
1496 i++;
1497 }
1498 if (length)
1499 {
1500 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001501
cristy3ed852e2009-09-05 21:47:34 +00001502 if (check != length)
1503 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001504
cristy3ed852e2009-09-05 21:47:34 +00001505 if (length == 4)
1506 {
1507 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1508 (data[3] == 0))
1509 {
1510 check=(png_size_t) ReadBlob(image,(size_t) length,
1511 (char *) mng_info->read_buffer);
1512 mng_info->read_buffer[4]=0;
1513 mng_info->bytes_in_read_buffer=4;
1514 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1515 mng_info->found_empty_plte=MagickTrue;
1516 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1517 {
1518 mng_info->found_empty_plte=MagickFalse;
1519 mng_info->have_saved_bkgd_index=MagickFalse;
1520 }
1521 }
glennrp0fe50b42010-11-16 03:52:51 +00001522
cristy3ed852e2009-09-05 21:47:34 +00001523 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1524 (data[3] == 1))
1525 {
1526 check=(png_size_t) ReadBlob(image,(size_t) length,
1527 (char *) mng_info->read_buffer);
1528 mng_info->read_buffer[4]=0;
1529 mng_info->bytes_in_read_buffer=4;
1530 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1531 if (mng_info->found_empty_plte)
1532 {
1533 /*
1534 Skip the bKGD data byte and CRC.
1535 */
1536 check=(png_size_t)
1537 ReadBlob(image,5,(char *) mng_info->read_buffer);
1538 check=(png_size_t) ReadBlob(image,(size_t) length,
1539 (char *) mng_info->read_buffer);
1540 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1541 mng_info->have_saved_bkgd_index=MagickTrue;
1542 mng_info->bytes_in_read_buffer=0;
1543 }
1544 }
1545 }
1546 }
1547}
1548#endif
1549
1550static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1551{
1552 Image
1553 *image;
1554
1555 image=(Image *) png_get_io_ptr(png_ptr);
1556 if (length)
1557 {
1558 png_size_t
1559 check;
1560
cristybb503372010-05-27 20:51:26 +00001561 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001562
cristy3ed852e2009-09-05 21:47:34 +00001563 if (check != length)
1564 png_error(png_ptr,"WriteBlob Failed");
1565 }
1566}
1567
1568static void png_flush_data(png_structp png_ptr)
1569{
1570 (void) png_ptr;
1571}
1572
1573#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1574static int PalettesAreEqual(Image *a,Image *b)
1575{
cristybb503372010-05-27 20:51:26 +00001576 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001577 i;
1578
1579 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1580 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001581
cristy3ed852e2009-09-05 21:47:34 +00001582 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1583 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001584
cristy3ed852e2009-09-05 21:47:34 +00001585 if (a->colors != b->colors)
1586 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001587
cristybb503372010-05-27 20:51:26 +00001588 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001589 {
1590 if ((a->colormap[i].red != b->colormap[i].red) ||
1591 (a->colormap[i].green != b->colormap[i].green) ||
1592 (a->colormap[i].blue != b->colormap[i].blue))
1593 return((int) MagickFalse);
1594 }
glennrp0fe50b42010-11-16 03:52:51 +00001595
cristy3ed852e2009-09-05 21:47:34 +00001596 return((int) MagickTrue);
1597}
1598#endif
1599
1600static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1601{
1602 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1603 mng_info->exists[i] && !mng_info->frozen[i])
1604 {
1605#ifdef MNG_OBJECT_BUFFERS
1606 if (mng_info->ob[i] != (MngBuffer *) NULL)
1607 {
1608 if (mng_info->ob[i]->reference_count > 0)
1609 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001610
cristy3ed852e2009-09-05 21:47:34 +00001611 if (mng_info->ob[i]->reference_count == 0)
1612 {
1613 if (mng_info->ob[i]->image != (Image *) NULL)
1614 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001615
cristy3ed852e2009-09-05 21:47:34 +00001616 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1617 }
1618 }
1619 mng_info->ob[i]=(MngBuffer *) NULL;
1620#endif
1621 mng_info->exists[i]=MagickFalse;
1622 mng_info->invisible[i]=MagickFalse;
1623 mng_info->viewable[i]=MagickFalse;
1624 mng_info->frozen[i]=MagickFalse;
1625 mng_info->x_off[i]=0;
1626 mng_info->y_off[i]=0;
1627 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001628 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001629 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001630 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001631 }
1632}
1633
glennrp21f0e622011-01-07 16:20:57 +00001634static void MngInfoFreeStruct(MngInfo *mng_info,
1635 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001636{
glennrp21f0e622011-01-07 16:20:57 +00001637 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001638 {
cristybb503372010-05-27 20:51:26 +00001639 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001640 i;
1641
1642 for (i=1; i < MNG_MAX_OBJECTS; i++)
1643 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001644
cristy3ed852e2009-09-05 21:47:34 +00001645 if (mng_info->global_plte != (png_colorp) NULL)
1646 mng_info->global_plte=(png_colorp)
1647 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001648
cristy3ed852e2009-09-05 21:47:34 +00001649 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1650 *have_mng_structure=MagickFalse;
1651 }
1652}
1653
1654static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1655{
1656 MngBox
1657 box;
1658
1659 box=box1;
1660 if (box.left < box2.left)
1661 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001662
cristy3ed852e2009-09-05 21:47:34 +00001663 if (box.top < box2.top)
1664 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001665
cristy3ed852e2009-09-05 21:47:34 +00001666 if (box.right > box2.right)
1667 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001668
cristy3ed852e2009-09-05 21:47:34 +00001669 if (box.bottom > box2.bottom)
1670 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001671
cristy3ed852e2009-09-05 21:47:34 +00001672 return box;
1673}
1674
1675static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1676{
1677 MngBox
1678 box;
1679
1680 /*
1681 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1682 */
cristybb503372010-05-27 20:51:26 +00001683 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1684 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1685 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1686 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001687 if (delta_type != 0)
1688 {
1689 box.left+=previous_box.left;
1690 box.right+=previous_box.right;
1691 box.top+=previous_box.top;
1692 box.bottom+=previous_box.bottom;
1693 }
glennrp0fe50b42010-11-16 03:52:51 +00001694
cristy3ed852e2009-09-05 21:47:34 +00001695 return(box);
1696}
1697
1698static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1699 unsigned char *p)
1700{
1701 MngPair
1702 pair;
1703 /*
cristybb503372010-05-27 20:51:26 +00001704 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001705 */
cristy8182b072010-05-30 20:10:53 +00001706 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1707 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001708
cristy3ed852e2009-09-05 21:47:34 +00001709 if (delta_type != 0)
1710 {
1711 pair.a+=previous_pair.a;
1712 pair.b+=previous_pair.b;
1713 }
glennrp0fe50b42010-11-16 03:52:51 +00001714
cristy3ed852e2009-09-05 21:47:34 +00001715 return(pair);
1716}
1717
cristy8182b072010-05-30 20:10:53 +00001718static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001719{
cristy8182b072010-05-30 20:10:53 +00001720 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001721}
1722
glennrpcf002022011-01-30 02:38:15 +00001723static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001724{
1725 Image
1726 *image;
1727
1728 image=(Image *) png_get_error_ptr(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001729
cristy3ed852e2009-09-05 21:47:34 +00001730 if (image->debug != MagickFalse)
1731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1732 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001733
cristy3ed852e2009-09-05 21:47:34 +00001734 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1735 message,"`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001736
glennrpe4017e32011-01-08 17:16:09 +00001737#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001738 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1739 * are building with libpng-1.4.x and can be ignored.
1740 */
cristy3ed852e2009-09-05 21:47:34 +00001741 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001742#else
1743 png_longjmp(ping,1);
1744#endif
cristy3ed852e2009-09-05 21:47:34 +00001745}
1746
glennrpcf002022011-01-30 02:38:15 +00001747static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001748{
1749 Image
1750 *image;
1751
1752 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1753 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001754
cristy3ed852e2009-09-05 21:47:34 +00001755 image=(Image *) png_get_error_ptr(ping);
1756 if (image->debug != MagickFalse)
1757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001758 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001759
cristy3ed852e2009-09-05 21:47:34 +00001760 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1761 message,"`%s'",image->filename);
1762}
1763
1764#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001765static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001766{
1767#if (PNG_LIBPNG_VER < 10011)
1768 png_voidp
1769 ret;
1770
1771 png_ptr=png_ptr;
1772 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001773
cristy3ed852e2009-09-05 21:47:34 +00001774 if (ret == NULL)
1775 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001776
cristy3ed852e2009-09-05 21:47:34 +00001777 return(ret);
1778#else
1779 png_ptr=png_ptr;
1780 return((png_voidp) AcquireMagickMemory((size_t) size));
1781#endif
1782}
1783
1784/*
1785 Free a pointer. It is removed from the list at the same time.
1786*/
glennrpcf002022011-01-30 02:38:15 +00001787static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001788{
1789 png_ptr=png_ptr;
1790 ptr=RelinquishMagickMemory(ptr);
1791 return((png_free_ptr) NULL);
1792}
1793#endif
1794
1795#if defined(__cplusplus) || defined(c_plusplus)
1796}
1797#endif
1798
1799static int
glennrpcf002022011-01-30 02:38:15 +00001800Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristyd15e6592011-10-15 00:13:06 +00001801 png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001802{
cristybb503372010-05-27 20:51:26 +00001803 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001804 i;
1805
1806 register unsigned char
1807 *dp;
1808
1809 register png_charp
1810 sp;
1811
1812 png_uint_32
1813 length,
1814 nibbles;
1815
1816 StringInfo
1817 *profile;
1818
glennrp0c3e06b2010-11-19 13:45:02 +00001819 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001820 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1821 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1822 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1823 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1824 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1825 13,14,15};
1826
1827 sp=text[ii].text+1;
1828 /* look for newline */
1829 while (*sp != '\n')
1830 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001831
cristy3ed852e2009-09-05 21:47:34 +00001832 /* look for length */
1833 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1834 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001835
cristyf2f27272009-12-17 14:48:46 +00001836 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001837
glennrp97f90e22011-02-22 05:47:58 +00001838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1839 " length: %lu",(unsigned long) length);
1840
cristy3ed852e2009-09-05 21:47:34 +00001841 while (*sp != ' ' && *sp != '\n')
1842 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001843
cristy3ed852e2009-09-05 21:47:34 +00001844 /* allocate space */
1845 if (length == 0)
1846 {
1847 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1848 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1849 return(MagickFalse);
1850 }
glennrp0fe50b42010-11-16 03:52:51 +00001851
cristy8723e4b2011-09-01 13:11:19 +00001852 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001853
cristy3ed852e2009-09-05 21:47:34 +00001854 if (profile == (StringInfo *) NULL)
1855 {
1856 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1857 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1858 "unable to copy profile");
1859 return(MagickFalse);
1860 }
glennrp0fe50b42010-11-16 03:52:51 +00001861
cristy3ed852e2009-09-05 21:47:34 +00001862 /* copy profile, skipping white space and column 1 "=" signs */
1863 dp=GetStringInfoDatum(profile);
1864 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001865
cristybb503372010-05-27 20:51:26 +00001866 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001867 {
1868 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1869 {
1870 if (*sp == '\0')
1871 {
1872 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1873 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1874 profile=DestroyStringInfo(profile);
1875 return(MagickFalse);
1876 }
1877 sp++;
1878 }
glennrp0fe50b42010-11-16 03:52:51 +00001879
cristy3ed852e2009-09-05 21:47:34 +00001880 if (i%2 == 0)
1881 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001882
cristy3ed852e2009-09-05 21:47:34 +00001883 else
1884 (*dp++)+=unhex[(int) *sp++];
1885 }
1886 /*
1887 We have already read "Raw profile type.
1888 */
cristyd15e6592011-10-15 00:13:06 +00001889 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001890 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001891
cristy3ed852e2009-09-05 21:47:34 +00001892 if (image_info->verbose)
1893 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001894
cristy3ed852e2009-09-05 21:47:34 +00001895 return MagickTrue;
1896}
1897
1898#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1899static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1900{
1901 Image
1902 *image;
1903
1904
1905 /* The unknown chunk structure contains the chunk data:
1906 png_byte name[5];
1907 png_byte *data;
1908 png_size_t size;
1909
1910 Note that libpng has already taken care of the CRC handling.
1911 */
1912
1913
1914 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1915 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1916 return(0); /* Did not recognize */
1917
1918 /* recognized vpAg */
1919
1920 if (chunk->size != 9)
1921 return(-1); /* Error return */
1922
1923 if (chunk->data[8] != 0)
1924 return(0); /* ImageMagick requires pixel units */
1925
1926 image=(Image *) png_get_user_chunk_ptr(ping);
1927
cristybb503372010-05-27 20:51:26 +00001928 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001929 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001930
cristybb503372010-05-27 20:51:26 +00001931 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001932 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1933
1934 /* Return one of the following: */
1935 /* return(-n); chunk had an error */
1936 /* return(0); did not recognize */
1937 /* return(n); success */
1938
1939 return(1);
1940
1941}
1942#endif
1943
1944/*
1945%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1946% %
1947% %
1948% %
1949% R e a d O n e P N G I m a g e %
1950% %
1951% %
1952% %
1953%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1954%
1955% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1956% (minus the 8-byte signature) and returns it. It allocates the memory
1957% necessary for the new Image structure and returns a pointer to the new
1958% image.
1959%
1960% The format of the ReadOnePNGImage method is:
1961%
1962% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1963% ExceptionInfo *exception)
1964%
1965% A description of each parameter follows:
1966%
1967% o mng_info: Specifies a pointer to a MngInfo structure.
1968%
1969% o image_info: the image info.
1970%
1971% o exception: return any errors or warnings in this structure.
1972%
1973*/
1974static Image *ReadOnePNGImage(MngInfo *mng_info,
1975 const ImageInfo *image_info, ExceptionInfo *exception)
1976{
1977 /* Read one PNG image */
1978
glennrpcc95c3f2011-04-18 16:46:48 +00001979 /* To do: Read the tIME chunk into the date:modify property */
1980 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1981
cristy3ed852e2009-09-05 21:47:34 +00001982 Image
1983 *image;
1984
1985 int
glennrp4eb39312011-03-30 21:34:55 +00001986 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001987 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001988 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001989 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001990 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001991 pass,
1992 ping_bit_depth,
1993 ping_color_type,
1994 ping_interlace_method,
1995 ping_compression_method,
1996 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001997 ping_num_trans,
1998 unit_type;
1999
2000 double
2001 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002002
cristy101ab702011-10-13 13:06:32 +00002003 PixelInfo
glennrpa6a06632011-01-19 15:15:34 +00002004 transparent_color;
2005
cristy3ed852e2009-09-05 21:47:34 +00002006 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002007 logging,
cristy3ed852e2009-09-05 21:47:34 +00002008 status;
2009
glennrpfaa852b2010-03-30 12:17:00 +00002010 png_bytep
2011 ping_trans_alpha;
2012
2013 png_color_16p
2014 ping_background,
2015 ping_trans_color;
2016
cristy3ed852e2009-09-05 21:47:34 +00002017 png_info
2018 *end_info,
2019 *ping_info;
2020
2021 png_struct
2022 *ping;
2023
2024 png_textp
2025 text;
2026
glennrpfaa852b2010-03-30 12:17:00 +00002027 png_uint_32
2028 ping_height,
2029 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002030 ping_rowbytes,
2031 x_resolution,
2032 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002033
cristy3ed852e2009-09-05 21:47:34 +00002034 QuantumInfo
2035 *quantum_info;
2036
2037 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002038 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002039
cristybb503372010-05-27 20:51:26 +00002040 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002041 y;
2042
2043 register unsigned char
2044 *p;
2045
cristybb503372010-05-27 20:51:26 +00002046 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002047 i,
2048 x;
2049
cristy4c08aed2011-07-01 19:47:50 +00002050 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002051 *q;
2052
2053 size_t
glennrp39992b42010-11-14 00:03:43 +00002054 length,
cristy3ed852e2009-09-05 21:47:34 +00002055 row_offset;
2056
cristyeb3b22a2011-03-31 20:16:11 +00002057 ssize_t
2058 j;
2059
cristy3ed852e2009-09-05 21:47:34 +00002060#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2061 png_byte unused_chunks[]=
2062 {
2063 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2064 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2065 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2066 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2067 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2068 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2069 };
2070#endif
2071
2072 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002073 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002074
2075#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002076 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002077#endif
2078
glennrp25c1e2b2010-03-25 01:39:56 +00002079#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002080 if (image_info->verbose)
2081 printf("Your PNG library (libpng-%s) is rather old.\n",
2082 PNG_LIBPNG_VER_STRING);
2083#endif
2084
glennrp61b4c952009-11-10 20:40:41 +00002085#if (PNG_LIBPNG_VER >= 10400)
2086# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2087 if (image_info->verbose)
2088 {
2089 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2090 PNG_LIBPNG_VER_STRING);
2091 printf("Please update it.\n");
2092 }
2093# endif
2094#endif
2095
2096
cristyed552522009-10-16 14:04:35 +00002097 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002098 image=mng_info->image;
2099
glennrpa6a06632011-01-19 15:15:34 +00002100 if (logging != MagickFalse)
2101 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2102 " image->matte=%d",(int) image->matte);
2103
glennrp0e319732011-01-25 21:53:13 +00002104 /* Set to an out-of-range color unless tRNS chunk is present */
2105 transparent_color.red=65537;
2106 transparent_color.green=65537;
2107 transparent_color.blue=65537;
cristy4c08aed2011-07-01 19:47:50 +00002108 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002109
glennrpcb395ac2011-03-30 19:50:23 +00002110 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002111 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002112 num_raw_profiles = 0;
2113
cristy3ed852e2009-09-05 21:47:34 +00002114 /*
2115 Allocate the PNG structures
2116 */
2117#ifdef PNG_USER_MEM_SUPPORTED
2118 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
glennrpcf002022011-01-30 02:38:15 +00002119 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2120 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002121#else
2122 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00002123 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002124#endif
2125 if (ping == (png_struct *) NULL)
2126 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002127
cristy3ed852e2009-09-05 21:47:34 +00002128 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002129
cristy3ed852e2009-09-05 21:47:34 +00002130 if (ping_info == (png_info *) NULL)
2131 {
2132 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2133 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2134 }
glennrp0fe50b42010-11-16 03:52:51 +00002135
cristy3ed852e2009-09-05 21:47:34 +00002136 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002137
cristy3ed852e2009-09-05 21:47:34 +00002138 if (end_info == (png_info *) NULL)
2139 {
2140 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2141 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2142 }
glennrp0fe50b42010-11-16 03:52:51 +00002143
glennrpcf002022011-01-30 02:38:15 +00002144 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002145
glennrpfaa852b2010-03-30 12:17:00 +00002146 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002147 {
2148 /*
2149 PNG image is corrupt.
2150 */
2151 png_destroy_read_struct(&ping,&ping_info,&end_info);
2152#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002153 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002154#endif
2155 if (logging != MagickFalse)
2156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2157 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002158
cristy3ed852e2009-09-05 21:47:34 +00002159 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002160 {
2161 InheritException(exception,&image->exception);
2162 image->columns=0;
2163 }
glennrp0fe50b42010-11-16 03:52:51 +00002164
cristy3ed852e2009-09-05 21:47:34 +00002165 return(GetFirstImageInList(image));
2166 }
2167 /*
2168 Prepare PNG for reading.
2169 */
glennrpfaa852b2010-03-30 12:17:00 +00002170
cristy3ed852e2009-09-05 21:47:34 +00002171 mng_info->image_found++;
2172 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002173
cristy3ed852e2009-09-05 21:47:34 +00002174 if (LocaleCompare(image_info->magick,"MNG") == 0)
2175 {
2176#if defined(PNG_MNG_FEATURES_SUPPORTED)
2177 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2178 png_set_read_fn(ping,image,png_get_data);
2179#else
2180#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2181 png_permit_empty_plte(ping,MagickTrue);
2182 png_set_read_fn(ping,image,png_get_data);
2183#else
2184 mng_info->image=image;
2185 mng_info->bytes_in_read_buffer=0;
2186 mng_info->found_empty_plte=MagickFalse;
2187 mng_info->have_saved_bkgd_index=MagickFalse;
2188 png_set_read_fn(ping,mng_info,mng_get_data);
2189#endif
2190#endif
2191 }
glennrp0fe50b42010-11-16 03:52:51 +00002192
cristy3ed852e2009-09-05 21:47:34 +00002193 else
2194 png_set_read_fn(ping,image,png_get_data);
2195
2196#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2197 /* Ignore unused chunks and all unknown chunks except for vpAg */
2198 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2199 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2200 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2201 (int)sizeof(unused_chunks)/5);
2202 /* Callback for other unknown chunks */
2203 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2204#endif
2205
glennrp991e92a2010-01-28 03:09:00 +00002206#if (PNG_LIBPNG_VER < 10400)
2207# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2208 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002209 /* Disable thread-unsafe features of pnggccrd */
2210 if (png_access_version_number() >= 10200)
2211 {
2212 png_uint_32 mmx_disable_mask=0;
2213 png_uint_32 asm_flags;
2214
2215 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2216 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2217 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2218 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2219 asm_flags=png_get_asm_flags(ping);
2220 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2221 }
glennrp991e92a2010-01-28 03:09:00 +00002222# endif
cristy3ed852e2009-09-05 21:47:34 +00002223#endif
2224
2225 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002226
2227 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2228 &ping_bit_depth,&ping_color_type,
2229 &ping_interlace_method,&ping_compression_method,
2230 &ping_filter_method);
2231
2232 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2233 &ping_trans_color);
2234
2235 (void) png_get_bKGD(ping, ping_info, &ping_background);
2236
2237 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002238 {
glennrpfaa852b2010-03-30 12:17:00 +00002239 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2240 {
2241 png_set_packing(ping);
2242 ping_bit_depth = 8;
2243 }
cristy3ed852e2009-09-05 21:47:34 +00002244 }
glennrpfaa852b2010-03-30 12:17:00 +00002245
2246 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002247 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002248 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002249 if (logging != MagickFalse)
2250 {
2251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002252 " PNG width: %.20g, height: %.20g",
2253 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002254
cristy3ed852e2009-09-05 21:47:34 +00002255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2256 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002257 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002258
cristy3ed852e2009-09-05 21:47:34 +00002259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2260 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002261 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002262
cristy3ed852e2009-09-05 21:47:34 +00002263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2264 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002265 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002266 }
2267
glennrpfaa852b2010-03-30 12:17:00 +00002268#ifdef PNG_READ_iCCP_SUPPORTED
2269 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002270 {
2271 int
2272 compression;
2273
glennrpe4017e32011-01-08 17:16:09 +00002274#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002275 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002276 info;
2277#else
2278 png_bytep
2279 info;
2280#endif
2281
2282 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002283 name;
2284
2285 png_uint_32
2286 profile_length;
2287
2288 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2289 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristy3ed852e2009-09-05 21:47:34 +00002291 if (profile_length != 0)
2292 {
2293 StringInfo
2294 *profile;
2295
2296 if (logging != MagickFalse)
2297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2298 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002299 profile=BlobToStringInfo(info,profile_length);
2300 if (profile == (StringInfo *) NULL)
2301 {
2302 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2303 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2304 "unable to copy profile");
cristyad5b0852011-09-08 02:01:21 +00002305 return((Image *) NULL);
cristye8f8f382011-09-01 13:32:37 +00002306 }
cristy3ed852e2009-09-05 21:47:34 +00002307 SetStringInfoDatum(profile,(const unsigned char *) info);
cristyd15e6592011-10-15 00:13:06 +00002308 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00002309 profile=DestroyStringInfo(profile);
2310 }
2311 }
2312#endif
2313#if defined(PNG_READ_sRGB_SUPPORTED)
2314 {
cristy3ed852e2009-09-05 21:47:34 +00002315 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002316 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2317 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002318
cristy3ed852e2009-09-05 21:47:34 +00002319 if (png_get_sRGB(ping,ping_info,&intent))
2320 {
glennrpcf002022011-01-30 02:38:15 +00002321 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2322 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002323
cristy3ed852e2009-09-05 21:47:34 +00002324 if (logging != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002326 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002327 }
2328 }
2329#endif
2330 {
glennrpfaa852b2010-03-30 12:17:00 +00002331 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2332 if (mng_info->have_global_gama)
2333 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002334
cristy3ed852e2009-09-05 21:47:34 +00002335 if (png_get_gAMA(ping,ping_info,&file_gamma))
2336 {
2337 image->gamma=(float) file_gamma;
2338 if (logging != MagickFalse)
2339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2340 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2341 }
2342 }
glennrpfaa852b2010-03-30 12:17:00 +00002343 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2344 {
2345 if (mng_info->have_global_chrm != MagickFalse)
2346 {
2347 (void) png_set_cHRM(ping,ping_info,
2348 mng_info->global_chrm.white_point.x,
2349 mng_info->global_chrm.white_point.y,
2350 mng_info->global_chrm.red_primary.x,
2351 mng_info->global_chrm.red_primary.y,
2352 mng_info->global_chrm.green_primary.x,
2353 mng_info->global_chrm.green_primary.y,
2354 mng_info->global_chrm.blue_primary.x,
2355 mng_info->global_chrm.blue_primary.y);
2356 }
2357 }
glennrp0fe50b42010-11-16 03:52:51 +00002358
glennrpfaa852b2010-03-30 12:17:00 +00002359 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002360 {
2361 (void) png_get_cHRM(ping,ping_info,
2362 &image->chromaticity.white_point.x,
2363 &image->chromaticity.white_point.y,
2364 &image->chromaticity.red_primary.x,
2365 &image->chromaticity.red_primary.y,
2366 &image->chromaticity.green_primary.x,
2367 &image->chromaticity.green_primary.y,
2368 &image->chromaticity.blue_primary.x,
2369 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002370
cristy3ed852e2009-09-05 21:47:34 +00002371 if (logging != MagickFalse)
2372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2373 " Reading PNG cHRM chunk.");
2374 }
glennrp0fe50b42010-11-16 03:52:51 +00002375
glennrpe610a072010-08-05 17:08:46 +00002376 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002377 {
glennrpe610a072010-08-05 17:08:46 +00002378 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002379 Magick_RenderingIntent_to_PNG_RenderingIntent
2380 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002381 png_set_gAMA(ping,ping_info,0.45455f);
2382 png_set_cHRM(ping,ping_info,
2383 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2384 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002385 }
cristy3ed852e2009-09-05 21:47:34 +00002386#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002387 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002388 {
cristy905ef802011-02-23 00:29:18 +00002389 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2390 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002391
cristy3ed852e2009-09-05 21:47:34 +00002392 if (logging != MagickFalse)
2393 if (image->page.x || image->page.y)
2394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002395 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2396 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002397 }
2398#endif
2399#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002400 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2401 {
2402 if (mng_info->have_global_phys)
2403 {
2404 png_set_pHYs(ping,ping_info,
2405 mng_info->global_x_pixels_per_unit,
2406 mng_info->global_y_pixels_per_unit,
2407 mng_info->global_phys_unit_type);
2408 }
2409 }
2410
2411 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002412 {
cristy3ed852e2009-09-05 21:47:34 +00002413 /*
2414 Set image resolution.
2415 */
2416 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002417 &unit_type);
2418 image->x_resolution=(double) x_resolution;
2419 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002420
cristy3ed852e2009-09-05 21:47:34 +00002421 if (unit_type == PNG_RESOLUTION_METER)
2422 {
2423 image->units=PixelsPerCentimeterResolution;
2424 image->x_resolution=(double) x_resolution/100.0;
2425 image->y_resolution=(double) y_resolution/100.0;
2426 }
glennrp0fe50b42010-11-16 03:52:51 +00002427
cristy3ed852e2009-09-05 21:47:34 +00002428 if (logging != MagickFalse)
2429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002430 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2431 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002432 }
cristy3ed852e2009-09-05 21:47:34 +00002433#endif
glennrp823b55c2011-03-14 18:46:46 +00002434
glennrpfaa852b2010-03-30 12:17:00 +00002435 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002436 {
2437 int
2438 number_colors;
2439
2440 png_colorp
2441 palette;
2442
2443 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002444
cristy3ed852e2009-09-05 21:47:34 +00002445 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002446 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002447 {
2448 if (mng_info->global_plte_length)
2449 {
2450 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2451 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002452
glennrpfaa852b2010-03-30 12:17:00 +00002453 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002454 if (mng_info->global_trns_length)
2455 {
2456 if (mng_info->global_trns_length >
2457 mng_info->global_plte_length)
2458 (void) ThrowMagickException(&image->exception,
2459 GetMagickModule(),CoderError,
2460 "global tRNS has more entries than global PLTE",
2461 "`%s'",image_info->filename);
2462 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2463 (int) mng_info->global_trns_length,NULL);
2464 }
glennrpbfd9e612011-04-22 14:02:20 +00002465#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002466 if (
2467#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2468 mng_info->have_saved_bkgd_index ||
2469#endif
glennrpfaa852b2010-03-30 12:17:00 +00002470 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002471 {
2472 png_color_16
2473 background;
2474
2475#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2476 if (mng_info->have_saved_bkgd_index)
2477 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002478#endif
glennrpfaa852b2010-03-30 12:17:00 +00002479 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2480 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002481
cristy3ed852e2009-09-05 21:47:34 +00002482 background.red=(png_uint_16)
2483 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002484
cristy3ed852e2009-09-05 21:47:34 +00002485 background.green=(png_uint_16)
2486 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002487
cristy3ed852e2009-09-05 21:47:34 +00002488 background.blue=(png_uint_16)
2489 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002490
glennrpc6c391a2011-04-27 02:23:56 +00002491 background.gray=(png_uint_16)
2492 mng_info->global_plte[background.index].green;
2493
cristy3ed852e2009-09-05 21:47:34 +00002494 png_set_bKGD(ping,ping_info,&background);
2495 }
2496#endif
2497 }
2498 else
2499 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2500 CoderError,"No global PLTE in file","`%s'",
2501 image_info->filename);
2502 }
2503 }
2504
glennrpbfd9e612011-04-22 14:02:20 +00002505#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002506 if (mng_info->have_global_bkgd &&
2507 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002508 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002509
glennrpfaa852b2010-03-30 12:17:00 +00002510 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002511 {
glennrpbfd9e612011-04-22 14:02:20 +00002512 unsigned int
2513 bkgd_scale;
2514
cristy3ed852e2009-09-05 21:47:34 +00002515 /*
2516 Set image background color.
2517 */
2518 if (logging != MagickFalse)
2519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2520 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002521
glennrpbfd9e612011-04-22 14:02:20 +00002522 /* Scale background components to 16-bit, then scale
2523 * to quantum depth
2524 */
2525 if (logging != MagickFalse)
2526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2527 " raw ping_background=(%d,%d,%d).",ping_background->red,
2528 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002529
glennrpbfd9e612011-04-22 14:02:20 +00002530 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002531
glennrpbfd9e612011-04-22 14:02:20 +00002532 if (ping_bit_depth == 1)
2533 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002534
glennrpbfd9e612011-04-22 14:02:20 +00002535 else if (ping_bit_depth == 2)
2536 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002537
glennrpbfd9e612011-04-22 14:02:20 +00002538 else if (ping_bit_depth == 4)
2539 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002540
glennrpbfd9e612011-04-22 14:02:20 +00002541 if (ping_bit_depth <= 8)
2542 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002543
glennrpbfd9e612011-04-22 14:02:20 +00002544 ping_background->red *= bkgd_scale;
2545 ping_background->green *= bkgd_scale;
2546 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002547
glennrpbfd9e612011-04-22 14:02:20 +00002548 if (logging != MagickFalse)
2549 {
glennrp2cbb4482010-06-02 04:37:24 +00002550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2551 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002552
glennrp2cbb4482010-06-02 04:37:24 +00002553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2554 " ping_background=(%d,%d,%d).",ping_background->red,
2555 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002556 }
glennrp2cbb4482010-06-02 04:37:24 +00002557
glennrpbfd9e612011-04-22 14:02:20 +00002558 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002559 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002560
glennrpbfd9e612011-04-22 14:02:20 +00002561 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002562 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002563
glennrpbfd9e612011-04-22 14:02:20 +00002564 image->background_color.blue=
2565 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002566
cristy4c08aed2011-07-01 19:47:50 +00002567 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002568
glennrpbfd9e612011-04-22 14:02:20 +00002569 if (logging != MagickFalse)
2570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2571 " image->background_color=(%.20g,%.20g,%.20g).",
2572 (double) image->background_color.red,
2573 (double) image->background_color.green,
2574 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002575 }
glennrpbfd9e612011-04-22 14:02:20 +00002576#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002577
glennrpfaa852b2010-03-30 12:17:00 +00002578 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002579 {
2580 /*
glennrpa6a06632011-01-19 15:15:34 +00002581 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002582 */
2583 int
2584 max_sample;
2585
cristy35ef8242010-06-03 16:24:13 +00002586 size_t
2587 one=1;
2588
cristy3ed852e2009-09-05 21:47:34 +00002589 if (logging != MagickFalse)
2590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2591 " Reading PNG tRNS chunk.");
2592
cristyf9cca6a2010-06-04 23:49:28 +00002593 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002594
glennrpfaa852b2010-03-30 12:17:00 +00002595 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2596 (int)ping_trans_color->gray > max_sample) ||
2597 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2598 ((int)ping_trans_color->red > max_sample ||
2599 (int)ping_trans_color->green > max_sample ||
2600 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002601 {
2602 if (logging != MagickFalse)
2603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2604 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002605 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002606 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002607 image->matte=MagickFalse;
2608 }
2609 else
2610 {
glennrpa6a06632011-01-19 15:15:34 +00002611 int
2612 scale_to_short;
2613
2614 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2615
2616 /* Scale transparent_color to short */
2617 transparent_color.red= scale_to_short*ping_trans_color->red;
2618 transparent_color.green= scale_to_short*ping_trans_color->green;
2619 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy4c08aed2011-07-01 19:47:50 +00002620 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002621
glennrpfaa852b2010-03-30 12:17:00 +00002622 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002623 {
glennrp0f111982010-07-07 20:18:33 +00002624 if (logging != MagickFalse)
2625 {
2626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2627 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002628
glennrp0f111982010-07-07 20:18:33 +00002629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy101ab702011-10-13 13:06:32 +00002630 " scaled graylevel is %.20g.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002631 }
cristy4c08aed2011-07-01 19:47:50 +00002632 transparent_color.red=transparent_color.alpha;
2633 transparent_color.green=transparent_color.alpha;
2634 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002635 }
2636 }
2637 }
2638#if defined(PNG_READ_sBIT_SUPPORTED)
2639 if (mng_info->have_global_sbit)
2640 {
glennrpfaa852b2010-03-30 12:17:00 +00002641 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002642 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2643 }
2644#endif
2645 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002646
cristy3ed852e2009-09-05 21:47:34 +00002647 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002648
2649 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2650
cristy3ed852e2009-09-05 21:47:34 +00002651 /*
2652 Initialize image structure.
2653 */
2654 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002655 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002656 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002657 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002658 if (mng_info->mng_type == 0)
2659 {
glennrpfaa852b2010-03-30 12:17:00 +00002660 mng_info->mng_width=ping_width;
2661 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002662 mng_info->frame=mng_info->image_box;
2663 mng_info->clip=mng_info->image_box;
2664 }
glennrp0fe50b42010-11-16 03:52:51 +00002665
cristy3ed852e2009-09-05 21:47:34 +00002666 else
2667 {
2668 image->page.y=mng_info->y_off[mng_info->object_id];
2669 }
glennrp0fe50b42010-11-16 03:52:51 +00002670
cristy3ed852e2009-09-05 21:47:34 +00002671 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002672 image->columns=ping_width;
2673 image->rows=ping_height;
2674 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002675 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002676 {
cristybefe4d22010-06-07 01:18:58 +00002677 size_t
2678 one;
2679
cristy3ed852e2009-09-05 21:47:34 +00002680 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002681 one=1;
2682 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002683#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2684 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002685 image->colors=256;
2686#else
2687 if (image->colors > 65536L)
2688 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002689#endif
glennrpfaa852b2010-03-30 12:17:00 +00002690 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002691 {
2692 int
2693 number_colors;
2694
2695 png_colorp
2696 palette;
2697
2698 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002699 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002700
cristy3ed852e2009-09-05 21:47:34 +00002701 if (logging != MagickFalse)
2702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2703 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2704 }
2705 }
2706
2707 if (image->storage_class == PseudoClass)
2708 {
2709 /*
2710 Initialize image colormap.
2711 */
cristy018f07f2011-09-04 21:15:19 +00002712 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00002713 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002714
glennrpfaa852b2010-03-30 12:17:00 +00002715 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002716 {
2717 int
2718 number_colors;
2719
2720 png_colorp
2721 palette;
2722
2723 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002724
glennrp6af6cf12011-04-22 13:05:16 +00002725 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002726 {
2727 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2728 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2729 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2730 }
glennrp6af6cf12011-04-22 13:05:16 +00002731
glennrp67b9c1a2011-04-22 18:47:36 +00002732 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002733 {
2734 image->colormap[i].red=0;
2735 image->colormap[i].green=0;
2736 image->colormap[i].blue=0;
2737 }
cristy3ed852e2009-09-05 21:47:34 +00002738 }
glennrp0fe50b42010-11-16 03:52:51 +00002739
cristy3ed852e2009-09-05 21:47:34 +00002740 else
2741 {
cristybb503372010-05-27 20:51:26 +00002742 size_t
cristy3ed852e2009-09-05 21:47:34 +00002743 scale;
2744
glennrpfaa852b2010-03-30 12:17:00 +00002745 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002746
cristy3ed852e2009-09-05 21:47:34 +00002747 if (scale < 1)
2748 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002749
cristybb503372010-05-27 20:51:26 +00002750 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002751 {
2752 image->colormap[i].red=(Quantum) (i*scale);
2753 image->colormap[i].green=(Quantum) (i*scale);
2754 image->colormap[i].blue=(Quantum) (i*scale);
2755 }
2756 }
2757 }
glennrp147bc912011-03-30 18:47:21 +00002758
glennrpcb395ac2011-03-30 19:50:23 +00002759 /* Set some properties for reporting by "identify" */
2760 {
glennrp147bc912011-03-30 18:47:21 +00002761 char
2762 msg[MaxTextExtent];
2763
2764 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2765 ping_interlace_method in value */
2766
cristy3b6fd2e2011-05-20 12:53:50 +00002767 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002768 "%d, %d",(int) ping_width, (int) ping_height);
cristyd15e6592011-10-15 00:13:06 +00002769 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002770
cristy3b6fd2e2011-05-20 12:53:50 +00002771 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
cristyd15e6592011-10-15 00:13:06 +00002772 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002773
cristy3b6fd2e2011-05-20 12:53:50 +00002774 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
cristyd15e6592011-10-15 00:13:06 +00002775 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002776
cristy3b6fd2e2011-05-20 12:53:50 +00002777 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002778 (int) ping_interlace_method);
cristyd15e6592011-10-15 00:13:06 +00002779 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg,exception);
glennrpcb395ac2011-03-30 19:50:23 +00002780 }
glennrp147bc912011-03-30 18:47:21 +00002781
cristy3ed852e2009-09-05 21:47:34 +00002782 /*
2783 Read image scanlines.
2784 */
2785 if (image->delay != 0)
2786 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002787
glennrp0ca69b12010-07-26 01:57:52 +00002788 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002789 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2790 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002791 {
2792 if (logging != MagickFalse)
2793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002794 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002795 mng_info->scenes_found-1);
2796 png_destroy_read_struct(&ping,&ping_info,&end_info);
2797#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002798 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002799#endif
2800 if (logging != MagickFalse)
2801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2802 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002803
cristy3ed852e2009-09-05 21:47:34 +00002804 return(image);
2805 }
glennrp0fe50b42010-11-16 03:52:51 +00002806
cristy3ed852e2009-09-05 21:47:34 +00002807 if (logging != MagickFalse)
2808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2809 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002810
cristy3ed852e2009-09-05 21:47:34 +00002811 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002812 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2813 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002814
cristy3ed852e2009-09-05 21:47:34 +00002815 else
glennrpcf002022011-01-30 02:38:15 +00002816 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2817 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002818
glennrpcf002022011-01-30 02:38:15 +00002819 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002820 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002821
cristy3ed852e2009-09-05 21:47:34 +00002822 if (logging != MagickFalse)
2823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2824 " Converting PNG pixels to pixel packets");
2825 /*
2826 Convert PNG pixels to pixel packets.
2827 */
glennrpfaa852b2010-03-30 12:17:00 +00002828 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002829 {
2830 /*
2831 PNG image is corrupt.
2832 */
2833 png_destroy_read_struct(&ping,&ping_info,&end_info);
2834#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002835 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002836#endif
2837 if (quantum_info != (QuantumInfo *) NULL)
2838 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002839
glennrpcf002022011-01-30 02:38:15 +00002840 if (ping_pixels != (unsigned char *) NULL)
2841 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002842
cristy3ed852e2009-09-05 21:47:34 +00002843 if (logging != MagickFalse)
2844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2845 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002846
cristy3ed852e2009-09-05 21:47:34 +00002847 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002848 {
2849 InheritException(exception,&image->exception);
2850 image->columns=0;
2851 }
glennrp0fe50b42010-11-16 03:52:51 +00002852
cristy3ed852e2009-09-05 21:47:34 +00002853 return(GetFirstImageInList(image));
2854 }
glennrp0fe50b42010-11-16 03:52:51 +00002855
cristyed552522009-10-16 14:04:35 +00002856 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002857
cristyed552522009-10-16 14:04:35 +00002858 if (quantum_info == (QuantumInfo *) NULL)
2859 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002860
glennrpc8cbc5d2011-01-01 00:12:34 +00002861 {
2862
2863 MagickBooleanType
2864 found_transparent_pixel;
2865
2866 found_transparent_pixel=MagickFalse;
2867
cristy3ed852e2009-09-05 21:47:34 +00002868 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002869 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002870 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002871 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002872 /*
2873 Convert image to DirectClass pixel packets.
2874 */
glennrp67b9c1a2011-04-22 18:47:36 +00002875#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2876 int
2877 depth;
2878
2879 depth=(ssize_t) ping_bit_depth;
2880#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002881 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2882 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2883 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2884 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002885
glennrpc8cbc5d2011-01-01 00:12:34 +00002886 for (y=0; y < (ssize_t) image->rows; y++)
2887 {
2888 if (num_passes > 1)
2889 row_offset=ping_rowbytes*y;
2890
2891 else
2892 row_offset=0;
2893
glennrpcf002022011-01-30 02:38:15 +00002894 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002895 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2896
cristyacd2ed22011-08-30 01:44:23 +00002897 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002898 break;
2899
glennrpc8cbc5d2011-01-01 00:12:34 +00002900 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2901 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002902 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002903
2904 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2905 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002906 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002907
2908 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2909 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002910 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002911
2912 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2913 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002914 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002915
2916 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2917 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002918 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002919
glennrpc8cbc5d2011-01-01 00:12:34 +00002920 if (found_transparent_pixel == MagickFalse)
2921 {
2922 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002923 if (y== 0 && logging != MagickFalse)
2924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2925 " Looking for cheap transparent pixel");
2926
glennrpc8cbc5d2011-01-01 00:12:34 +00002927 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2928 {
glennrp5aa37f62011-01-02 03:07:57 +00002929 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2930 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002931 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002932 {
glennrpa6a06632011-01-19 15:15:34 +00002933 if (logging != MagickFalse)
2934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2935 " ...got one.");
2936
glennrpc8cbc5d2011-01-01 00:12:34 +00002937 found_transparent_pixel = MagickTrue;
2938 break;
2939 }
glennrp4f25bd02011-01-01 18:51:28 +00002940 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2941 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp847370c2011-07-05 17:37:15 +00002942 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2943 transparent_color.red &&
2944 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2945 transparent_color.green &&
2946 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2947 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002948 {
glennrpa6a06632011-01-19 15:15:34 +00002949 if (logging != MagickFalse)
2950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2951 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002952 found_transparent_pixel = MagickTrue;
2953 break;
2954 }
cristyed231572011-07-14 02:18:59 +00002955 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002956 }
2957 }
2958
2959 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2960 {
2961 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2962 image->rows);
2963
2964 if (status == MagickFalse)
2965 break;
2966 }
2967 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2968 break;
2969 }
2970
2971 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2972 {
2973 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002974 if (status == MagickFalse)
2975 break;
2976 }
cristy3ed852e2009-09-05 21:47:34 +00002977 }
cristy3ed852e2009-09-05 21:47:34 +00002978 }
glennrp0fe50b42010-11-16 03:52:51 +00002979
cristy3ed852e2009-09-05 21:47:34 +00002980 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002981
cristy3ed852e2009-09-05 21:47:34 +00002982 for (pass=0; pass < num_passes; pass++)
2983 {
2984 Quantum
2985 *quantum_scanline;
2986
2987 register Quantum
2988 *r;
2989
2990 /*
2991 Convert grayscale image to PseudoClass pixel packets.
2992 */
glennrpc17d96f2011-06-27 01:20:11 +00002993 if (logging != MagickFalse)
2994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2995 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00002996
glennrpfaa852b2010-03-30 12:17:00 +00002997 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002998 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002999
cristy3ed852e2009-09-05 21:47:34 +00003000 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3001 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003002
cristy3ed852e2009-09-05 21:47:34 +00003003 if (quantum_scanline == (Quantum *) NULL)
3004 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003005
cristybb503372010-05-27 20:51:26 +00003006 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003007 {
3008 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003009 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003010
cristy3ed852e2009-09-05 21:47:34 +00003011 else
3012 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003013
glennrpcf002022011-01-30 02:38:15 +00003014 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003015 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003016
cristyacd2ed22011-08-30 01:44:23 +00003017 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003018 break;
glennrp0fe50b42010-11-16 03:52:51 +00003019
glennrpcf002022011-01-30 02:38:15 +00003020 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003021 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003022
glennrpfaa852b2010-03-30 12:17:00 +00003023 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003024 {
3025 case 1:
3026 {
cristybb503372010-05-27 20:51:26 +00003027 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003028 bit;
3029
cristybb503372010-05-27 20:51:26 +00003030 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003031 {
3032 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003033 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003034 p++;
3035 }
glennrp0fe50b42010-11-16 03:52:51 +00003036
cristy3ed852e2009-09-05 21:47:34 +00003037 if ((image->columns % 8) != 0)
3038 {
cristybb503372010-05-27 20:51:26 +00003039 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003040 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003041 }
glennrp0fe50b42010-11-16 03:52:51 +00003042
cristy3ed852e2009-09-05 21:47:34 +00003043 break;
3044 }
glennrp47b9dd52010-11-24 18:12:06 +00003045
cristy3ed852e2009-09-05 21:47:34 +00003046 case 2:
3047 {
cristybb503372010-05-27 20:51:26 +00003048 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003049 {
glennrpa18d5bc2011-04-23 14:51:34 +00003050 *r++=(*p >> 6) & 0x03;
3051 *r++=(*p >> 4) & 0x03;
3052 *r++=(*p >> 2) & 0x03;
3053 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003054 }
glennrp0fe50b42010-11-16 03:52:51 +00003055
cristy3ed852e2009-09-05 21:47:34 +00003056 if ((image->columns % 4) != 0)
3057 {
cristybb503372010-05-27 20:51:26 +00003058 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003059 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003060 }
glennrp0fe50b42010-11-16 03:52:51 +00003061
cristy3ed852e2009-09-05 21:47:34 +00003062 break;
3063 }
glennrp47b9dd52010-11-24 18:12:06 +00003064
cristy3ed852e2009-09-05 21:47:34 +00003065 case 4:
3066 {
cristybb503372010-05-27 20:51:26 +00003067 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003068 {
glennrpa18d5bc2011-04-23 14:51:34 +00003069 *r++=(*p >> 4) & 0x0f;
3070 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003071 }
glennrp0fe50b42010-11-16 03:52:51 +00003072
cristy3ed852e2009-09-05 21:47:34 +00003073 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003074 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003075
cristy3ed852e2009-09-05 21:47:34 +00003076 break;
3077 }
glennrp47b9dd52010-11-24 18:12:06 +00003078
cristy3ed852e2009-09-05 21:47:34 +00003079 case 8:
3080 {
glennrpfaa852b2010-03-30 12:17:00 +00003081 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003082 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003083 {
glennrpa18d5bc2011-04-23 14:51:34 +00003084 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003085 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3086 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003087 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003088 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003089 }
glennrp0fe50b42010-11-16 03:52:51 +00003090
cristy3ed852e2009-09-05 21:47:34 +00003091 else
cristybb503372010-05-27 20:51:26 +00003092 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003093 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003094
cristy3ed852e2009-09-05 21:47:34 +00003095 break;
3096 }
glennrp47b9dd52010-11-24 18:12:06 +00003097
cristy3ed852e2009-09-05 21:47:34 +00003098 case 16:
3099 {
cristybb503372010-05-27 20:51:26 +00003100 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003101 {
glennrpc17d96f2011-06-27 01:20:11 +00003102#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003103 size_t
3104 quantum;
3105
3106 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003107 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003108
3109 else
glennrpc17d96f2011-06-27 01:20:11 +00003110 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003111
glennrp58f77c72011-04-23 14:09:09 +00003112 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003113 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003114 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003115
3116 if (ping_color_type == 4)
3117 {
glennrpc17d96f2011-06-27 01:20:11 +00003118 if (image->colors > 256)
3119 quantum=((*p++) << 8);
3120 else
3121 quantum=0;
3122
glennrp58f77c72011-04-23 14:09:09 +00003123 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003124 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3125 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003126 found_transparent_pixel = MagickTrue;
cristyed231572011-07-14 02:18:59 +00003127 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003128 }
glennrp58f77c72011-04-23 14:09:09 +00003129
3130#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3131 *r++=(*p++);
3132 p++; /* strip low byte */
3133
3134 if (ping_color_type == 4)
3135 {
cristy4c08aed2011-07-01 19:47:50 +00003136 SetPixelAlpha(image,*p++,q);
3137 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003138 found_transparent_pixel = MagickTrue;
3139 p++;
cristyed231572011-07-14 02:18:59 +00003140 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003141 }
cristy3ed852e2009-09-05 21:47:34 +00003142#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003143 }
glennrp47b9dd52010-11-24 18:12:06 +00003144
cristy3ed852e2009-09-05 21:47:34 +00003145 break;
3146 }
glennrp47b9dd52010-11-24 18:12:06 +00003147
cristy3ed852e2009-09-05 21:47:34 +00003148 default:
3149 break;
3150 }
glennrp3faa9a32011-04-23 14:00:25 +00003151
cristy3ed852e2009-09-05 21:47:34 +00003152 /*
3153 Transfer image scanline.
3154 */
3155 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003156
cristy4c08aed2011-07-01 19:47:50 +00003157 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3158
cristyacd2ed22011-08-30 01:44:23 +00003159 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00003160 break;
cristybb503372010-05-27 20:51:26 +00003161 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003162 {
3163 SetPixelIndex(image,*r++,q);
cristyed231572011-07-14 02:18:59 +00003164 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00003165 }
glennrp0fe50b42010-11-16 03:52:51 +00003166
cristy3ed852e2009-09-05 21:47:34 +00003167 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3168 break;
glennrp0fe50b42010-11-16 03:52:51 +00003169
cristy7a287bf2010-02-14 02:18:09 +00003170 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3171 {
cristycee97112010-05-28 00:44:52 +00003172 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003173 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003174
cristy7a287bf2010-02-14 02:18:09 +00003175 if (status == MagickFalse)
3176 break;
3177 }
cristy3ed852e2009-09-05 21:47:34 +00003178 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003179
cristy7a287bf2010-02-14 02:18:09 +00003180 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003181 {
3182 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003183
cristy3ed852e2009-09-05 21:47:34 +00003184 if (status == MagickFalse)
3185 break;
3186 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003187
cristy3ed852e2009-09-05 21:47:34 +00003188 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3189 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003190
3191 image->matte=found_transparent_pixel;
3192
3193 if (logging != MagickFalse)
3194 {
3195 if (found_transparent_pixel != MagickFalse)
3196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3197 " Found transparent pixel");
3198 else
glennrp5aa37f62011-01-02 03:07:57 +00003199 {
3200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3201 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003202
glennrp5aa37f62011-01-02 03:07:57 +00003203 ping_color_type&=0x03;
3204 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003205 }
3206 }
3207
cristyb32b90a2009-09-07 21:45:48 +00003208 if (quantum_info != (QuantumInfo *) NULL)
3209 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003210
cristy5c6f7892010-05-05 22:53:29 +00003211 if (image->storage_class == PseudoClass)
3212 {
cristyaeb2cbc2010-05-07 13:28:58 +00003213 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003214 matte;
3215
3216 matte=image->matte;
3217 image->matte=MagickFalse;
3218 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00003219 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003220 }
glennrp47b9dd52010-11-24 18:12:06 +00003221
glennrp4eb39312011-03-30 21:34:55 +00003222 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003223
3224 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003225 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003226 {
3227 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003228 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003229 image->colors=2;
3230 (void) SetImageBackgroundColor(image);
3231#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003232 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003233#endif
3234 if (logging != MagickFalse)
3235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3236 " exit ReadOnePNGImage() early.");
3237 return(image);
3238 }
glennrp47b9dd52010-11-24 18:12:06 +00003239
glennrpfaa852b2010-03-30 12:17:00 +00003240 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003241 {
3242 ClassType
3243 storage_class;
3244
3245 /*
3246 Image has a transparent background.
3247 */
3248 storage_class=image->storage_class;
3249 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003250
glennrp3c218112010-11-27 15:31:26 +00003251/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003252
glennrp0fe50b42010-11-16 03:52:51 +00003253 if (storage_class == PseudoClass)
3254 {
3255 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003256 {
glennrp0fe50b42010-11-16 03:52:51 +00003257 for (x=0; x < ping_num_trans; x++)
3258 {
cristy4c08aed2011-07-01 19:47:50 +00003259 image->colormap[x].alpha =
3260 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003261 }
glennrpc11cf6a2010-03-20 16:46:19 +00003262 }
glennrp47b9dd52010-11-24 18:12:06 +00003263
glennrp0fe50b42010-11-16 03:52:51 +00003264 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3265 {
3266 for (x=0; x < (int) image->colors; x++)
3267 {
3268 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003269 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003270 {
cristy4c08aed2011-07-01 19:47:50 +00003271 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003272 }
3273 }
3274 }
3275 (void) SyncImage(image);
3276 }
glennrp47b9dd52010-11-24 18:12:06 +00003277
glennrpa6a06632011-01-19 15:15:34 +00003278#if 1 /* Should have already been done above, but glennrp problem P10
3279 * needs this.
3280 */
glennrp0fe50b42010-11-16 03:52:51 +00003281 else
3282 {
3283 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003284 {
glennrp0fe50b42010-11-16 03:52:51 +00003285 image->storage_class=storage_class;
3286 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3287
cristyacd2ed22011-08-30 01:44:23 +00003288 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003289 break;
3290
glennrp0fe50b42010-11-16 03:52:51 +00003291
glennrpa6a06632011-01-19 15:15:34 +00003292 /* Caution: on a Q8 build, this does not distinguish between
3293 * 16-bit colors that differ only in the low byte
3294 */
glennrp0fe50b42010-11-16 03:52:51 +00003295 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3296 {
glennrp847370c2011-07-05 17:37:15 +00003297 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3298 transparent_color.red &&
3299 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3300 transparent_color.green &&
3301 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3302 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003303 {
cristy4c08aed2011-07-01 19:47:50 +00003304 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003305 }
glennrp0fe50b42010-11-16 03:52:51 +00003306
glennrp67b9c1a2011-04-22 18:47:36 +00003307#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003308 else
glennrp4f25bd02011-01-01 18:51:28 +00003309 {
cristy4c08aed2011-07-01 19:47:50 +00003310 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003311 }
glennrpa6a06632011-01-19 15:15:34 +00003312#endif
glennrp0fe50b42010-11-16 03:52:51 +00003313
cristyed231572011-07-14 02:18:59 +00003314 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003315 }
3316
3317 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3318 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003319 }
glennrp0fe50b42010-11-16 03:52:51 +00003320 }
glennrpa6a06632011-01-19 15:15:34 +00003321#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003322
cristy3ed852e2009-09-05 21:47:34 +00003323 image->storage_class=DirectClass;
3324 }
glennrp3c218112010-11-27 15:31:26 +00003325
cristyb40fc462010-08-08 00:49:49 +00003326 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3327 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3328 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003329
cristyeb3b22a2011-03-31 20:16:11 +00003330 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003331 {
3332 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003333 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3334 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003335 else
glennrpa0ed0092011-04-18 16:36:29 +00003336 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3337 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003338
glennrp4eb39312011-03-30 21:34:55 +00003339 if (status != MagickFalse)
3340 for (i=0; i < (ssize_t) num_text; i++)
3341 {
3342 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003343
glennrp4eb39312011-03-30 21:34:55 +00003344 if (logging != MagickFalse)
3345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3346 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003347
glennrp4eb39312011-03-30 21:34:55 +00003348 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003349 {
cristyd15e6592011-10-15 00:13:06 +00003350 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i,
3351 exception);
glennrp4eb39312011-03-30 21:34:55 +00003352 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003353 }
glennrp0fe50b42010-11-16 03:52:51 +00003354
glennrp4eb39312011-03-30 21:34:55 +00003355 else
3356 {
3357 char
3358 *value;
3359
3360 length=text[i].text_length;
3361 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3362 sizeof(*value));
3363 if (value == (char *) NULL)
3364 {
3365 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3366 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3367 image->filename);
3368 break;
3369 }
3370 *value='\0';
3371 (void) ConcatenateMagickString(value,text[i].text,length+2);
3372
3373 /* Don't save "density" or "units" property if we have a pHYs
3374 * chunk
3375 */
3376 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3377 (LocaleCompare(text[i].key,"density") != 0 &&
3378 LocaleCompare(text[i].key,"units") != 0))
cristyd15e6592011-10-15 00:13:06 +00003379 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003380
3381 if (logging != MagickFalse)
3382 {
3383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3384 " length: %lu",(unsigned long) length);
3385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3386 " Keyword: %s",text[i].key);
3387 }
3388
3389 value=DestroyString(value);
3390 }
3391 }
3392 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003393 }
glennrp3c218112010-11-27 15:31:26 +00003394
cristy3ed852e2009-09-05 21:47:34 +00003395#ifdef MNG_OBJECT_BUFFERS
3396 /*
3397 Store the object if necessary.
3398 */
3399 if (object_id && !mng_info->frozen[object_id])
3400 {
3401 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3402 {
3403 /*
3404 create a new object buffer.
3405 */
3406 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003407 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003408
cristy3ed852e2009-09-05 21:47:34 +00003409 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3410 {
3411 mng_info->ob[object_id]->image=(Image *) NULL;
3412 mng_info->ob[object_id]->reference_count=1;
3413 }
3414 }
glennrp47b9dd52010-11-24 18:12:06 +00003415
cristy3ed852e2009-09-05 21:47:34 +00003416 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3417 mng_info->ob[object_id]->frozen)
3418 {
3419 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3420 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3421 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3422 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003423
cristy3ed852e2009-09-05 21:47:34 +00003424 if (mng_info->ob[object_id]->frozen)
3425 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3426 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3427 "`%s'",image->filename);
3428 }
glennrp0fe50b42010-11-16 03:52:51 +00003429
cristy3ed852e2009-09-05 21:47:34 +00003430 else
3431 {
cristy3ed852e2009-09-05 21:47:34 +00003432
3433 if (mng_info->ob[object_id]->image != (Image *) NULL)
3434 mng_info->ob[object_id]->image=DestroyImage
3435 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003436
cristy3ed852e2009-09-05 21:47:34 +00003437 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3438 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003439
cristy3ed852e2009-09-05 21:47:34 +00003440 if (mng_info->ob[object_id]->image != (Image *) NULL)
3441 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003442
cristy3ed852e2009-09-05 21:47:34 +00003443 else
3444 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3445 ResourceLimitError,"Cloning image for object buffer failed",
3446 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003447
glennrpfaa852b2010-03-30 12:17:00 +00003448 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003449 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003450
glennrpfaa852b2010-03-30 12:17:00 +00003451 mng_info->ob[object_id]->width=ping_width;
3452 mng_info->ob[object_id]->height=ping_height;
3453 mng_info->ob[object_id]->color_type=ping_color_type;
3454 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3455 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3456 mng_info->ob[object_id]->compression_method=
3457 ping_compression_method;
3458 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003459
glennrpfaa852b2010-03-30 12:17:00 +00003460 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003461 {
3462 int
3463 number_colors;
3464
3465 png_colorp
3466 plte;
3467
3468 /*
3469 Copy the PLTE to the object buffer.
3470 */
3471 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3472 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003473
cristy3ed852e2009-09-05 21:47:34 +00003474 for (i=0; i < number_colors; i++)
3475 {
3476 mng_info->ob[object_id]->plte[i]=plte[i];
3477 }
3478 }
glennrp47b9dd52010-11-24 18:12:06 +00003479
cristy3ed852e2009-09-05 21:47:34 +00003480 else
3481 mng_info->ob[object_id]->plte_length=0;
3482 }
3483 }
3484#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003485
3486 /* Set image->matte to MagickTrue if the input colortype supports
3487 * alpha or if a valid tRNS chunk is present, no matter whether there
3488 * is actual transparency present.
3489 */
3490 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3491 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3492 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3493 MagickTrue : MagickFalse;
3494
glennrpcb395ac2011-03-30 19:50:23 +00003495 /* Set more properties for identify to retrieve */
3496 {
3497 char
3498 msg[MaxTextExtent];
3499
glennrp4eb39312011-03-30 21:34:55 +00003500 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003501 {
3502 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003503 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003504 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrp1a2061f2011-10-18 12:30:45 +00003505 (void) SetImageProperty(image,"PNG:text ",msg,
3506 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003507 }
3508
3509 if (num_raw_profiles != 0)
3510 {
cristy3b6fd2e2011-05-20 12:53:50 +00003511 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003512 "%d were found", num_raw_profiles);
glennrp1a2061f2011-10-18 12:30:45 +00003513 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg,
3514 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003515 }
3516
glennrpcb395ac2011-03-30 19:50:23 +00003517 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003518 {
cristy3b6fd2e2011-05-20 12:53:50 +00003519 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003520 "chunk was found (see Chromaticity, above)");
glennrp1a2061f2011-10-18 12:30:45 +00003521 (void) SetImageProperty(image,"PNG:cHRM ",msg,
3522 exception);
glennrp59612252011-03-30 21:45:21 +00003523 }
glennrpcb395ac2011-03-30 19:50:23 +00003524
3525 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003526 {
cristy3b6fd2e2011-05-20 12:53:50 +00003527 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003528 "chunk was found (see Background color, above)");
glennrp1a2061f2011-10-18 12:30:45 +00003529 (void) SetImageProperty(image,"PNG:bKGD ",msg,
3530 exception);
glennrp59612252011-03-30 21:45:21 +00003531 }
3532
cristy3b6fd2e2011-05-20 12:53:50 +00003533 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003534 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003535
3536 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
glennrp1a2061f2011-10-18 12:30:45 +00003537 (void) SetImageProperty(image,"PNG:iCCP ",msg,
3538 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003539
glennrpcb395ac2011-03-30 19:50:23 +00003540 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
glennrp1a2061f2011-10-18 12:30:45 +00003541 (void) SetImageProperty(image,"PNG:tRNS ",msg,
3542 exception);
glennrp4eb39312011-03-30 21:34:55 +00003543
3544#if defined(PNG_sRGB_SUPPORTED)
3545 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3546 {
cristy3b6fd2e2011-05-20 12:53:50 +00003547 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003548 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003549 (int) intent);
glennrp1a2061f2011-10-18 12:30:45 +00003550 (void) SetImageProperty(image,"PNG:sRGB ",msg,
3551 exception);
glennrp4eb39312011-03-30 21:34:55 +00003552 }
3553#endif
3554
3555 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3556 {
cristy3b6fd2e2011-05-20 12:53:50 +00003557 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003558 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003559 file_gamma);
glennrp1a2061f2011-10-18 12:30:45 +00003560 (void) SetImageProperty(image,"PNG:gAMA ",msg,
3561 exception);
glennrp4eb39312011-03-30 21:34:55 +00003562 }
3563
3564#if defined(PNG_pHYs_SUPPORTED)
3565 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3566 {
cristy3b6fd2e2011-05-20 12:53:50 +00003567 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003568 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003569 (double) x_resolution,(double) y_resolution, unit_type);
glennrp1a2061f2011-10-18 12:30:45 +00003570 (void) SetImageProperty(image,"PNG:pHYs ",msg,
3571 exception);
glennrp4eb39312011-03-30 21:34:55 +00003572 }
3573#endif
3574
3575#if defined(PNG_oFFs_SUPPORTED)
3576 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3577 {
cristy3b6fd2e2011-05-20 12:53:50 +00003578 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003579 (double) image->page.x,(double) image->page.y);
glennrp1a2061f2011-10-18 12:30:45 +00003580 (void) SetImageProperty(image,"PNG:oFFs ",msg,
3581 exception);
glennrp4eb39312011-03-30 21:34:55 +00003582 }
3583#endif
3584
glennrp07523c72011-03-31 18:12:10 +00003585 if ((image->page.width != 0 && image->page.width != image->columns) ||
3586 (image->page.height != 0 && image->page.height != image->rows))
3587 {
cristy3b6fd2e2011-05-20 12:53:50 +00003588 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003589 "width=%.20g, height=%.20g",
3590 (double) image->page.width,(double) image->page.height);
glennrp1a2061f2011-10-18 12:30:45 +00003591 (void) SetImageProperty(image,"PNG:vpAg ",msg,
3592 exception);
glennrp07523c72011-03-31 18:12:10 +00003593 }
glennrpcb395ac2011-03-30 19:50:23 +00003594 }
3595
cristy3ed852e2009-09-05 21:47:34 +00003596 /*
3597 Relinquish resources.
3598 */
3599 png_destroy_read_struct(&ping,&ping_info,&end_info);
3600
glennrpcf002022011-01-30 02:38:15 +00003601 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003602#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003603 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003604#endif
3605
3606 if (logging != MagickFalse)
3607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3608 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003609
cristy3ed852e2009-09-05 21:47:34 +00003610 return(image);
3611
3612/* end of reading one PNG image */
3613}
3614
3615static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3616{
3617 Image
3618 *image,
3619 *previous;
3620
3621 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003622 have_mng_structure,
3623 logging,
cristy3ed852e2009-09-05 21:47:34 +00003624 status;
3625
3626 MngInfo
3627 *mng_info;
3628
3629 char
3630 magic_number[MaxTextExtent];
3631
cristy3ed852e2009-09-05 21:47:34 +00003632 ssize_t
3633 count;
3634
3635 /*
3636 Open image file.
3637 */
3638 assert(image_info != (const ImageInfo *) NULL);
3639 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003640
cristy3ed852e2009-09-05 21:47:34 +00003641 if (image_info->debug != MagickFalse)
3642 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3643 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003644
cristy3ed852e2009-09-05 21:47:34 +00003645 assert(exception != (ExceptionInfo *) NULL);
3646 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003647 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy9950d572011-10-01 18:22:35 +00003648 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003649 mng_info=(MngInfo *) NULL;
3650 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003651
cristy3ed852e2009-09-05 21:47:34 +00003652 if (status == MagickFalse)
3653 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003654
cristy3ed852e2009-09-05 21:47:34 +00003655 /*
3656 Verify PNG signature.
3657 */
3658 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003659
glennrpdde35db2011-02-21 12:06:32 +00003660 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003661 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003662
cristy3ed852e2009-09-05 21:47:34 +00003663 /*
3664 Allocate a MngInfo structure.
3665 */
3666 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003667 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003668
cristy3ed852e2009-09-05 21:47:34 +00003669 if (mng_info == (MngInfo *) NULL)
3670 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003671
cristy3ed852e2009-09-05 21:47:34 +00003672 /*
3673 Initialize members of the MngInfo structure.
3674 */
3675 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3676 mng_info->image=image;
3677 have_mng_structure=MagickTrue;
3678
3679 previous=image;
3680 image=ReadOnePNGImage(mng_info,image_info,exception);
3681 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003682
cristy3ed852e2009-09-05 21:47:34 +00003683 if (image == (Image *) NULL)
3684 {
3685 if (previous != (Image *) NULL)
3686 {
3687 if (previous->signature != MagickSignature)
3688 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003689
cristy3ed852e2009-09-05 21:47:34 +00003690 (void) CloseBlob(previous);
3691 (void) DestroyImageList(previous);
3692 }
glennrp0fe50b42010-11-16 03:52:51 +00003693
cristy3ed852e2009-09-05 21:47:34 +00003694 if (logging != MagickFalse)
3695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3696 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003697
cristy3ed852e2009-09-05 21:47:34 +00003698 return((Image *) NULL);
3699 }
glennrp47b9dd52010-11-24 18:12:06 +00003700
cristy3ed852e2009-09-05 21:47:34 +00003701 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003702
cristy3ed852e2009-09-05 21:47:34 +00003703 if ((image->columns == 0) || (image->rows == 0))
3704 {
3705 if (logging != MagickFalse)
3706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3707 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003708
cristy3ed852e2009-09-05 21:47:34 +00003709 ThrowReaderException(CorruptImageError,"CorruptImage");
3710 }
glennrp47b9dd52010-11-24 18:12:06 +00003711
cristy3ed852e2009-09-05 21:47:34 +00003712 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3713 {
cristy018f07f2011-09-04 21:15:19 +00003714 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003715 image->matte=MagickFalse;
3716 }
glennrp0fe50b42010-11-16 03:52:51 +00003717
cristy3ed852e2009-09-05 21:47:34 +00003718 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy018f07f2011-09-04 21:15:19 +00003719 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003720
cristy3ed852e2009-09-05 21:47:34 +00003721 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3723 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3724 (double) image->page.width,(double) image->page.height,
3725 (double) image->page.x,(double) image->page.y);
3726
3727 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003729
cristy3ed852e2009-09-05 21:47:34 +00003730 return(image);
3731}
3732
3733
3734
3735#if defined(JNG_SUPPORTED)
3736/*
3737%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3738% %
3739% %
3740% %
3741% R e a d O n e J N G I m a g e %
3742% %
3743% %
3744% %
3745%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3746%
3747% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3748% (minus the 8-byte signature) and returns it. It allocates the memory
3749% necessary for the new Image structure and returns a pointer to the new
3750% image.
3751%
3752% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3753%
3754% The format of the ReadOneJNGImage method is:
3755%
3756% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3757% ExceptionInfo *exception)
3758%
3759% A description of each parameter follows:
3760%
3761% o mng_info: Specifies a pointer to a MngInfo structure.
3762%
3763% o image_info: the image info.
3764%
3765% o exception: return any errors or warnings in this structure.
3766%
3767*/
3768static Image *ReadOneJNGImage(MngInfo *mng_info,
3769 const ImageInfo *image_info, ExceptionInfo *exception)
3770{
3771 Image
3772 *alpha_image,
3773 *color_image,
3774 *image,
3775 *jng_image;
3776
3777 ImageInfo
3778 *alpha_image_info,
3779 *color_image_info;
3780
cristy4383ec82011-01-05 15:42:32 +00003781 MagickBooleanType
3782 logging;
3783
cristybb503372010-05-27 20:51:26 +00003784 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003785 y;
3786
3787 MagickBooleanType
3788 status;
3789
3790 png_uint_32
3791 jng_height,
3792 jng_width;
3793
3794 png_byte
3795 jng_color_type,
3796 jng_image_sample_depth,
3797 jng_image_compression_method,
3798 jng_image_interlace_method,
3799 jng_alpha_sample_depth,
3800 jng_alpha_compression_method,
3801 jng_alpha_filter_method,
3802 jng_alpha_interlace_method;
3803
cristy4c08aed2011-07-01 19:47:50 +00003804 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003805 *s;
3806
cristybb503372010-05-27 20:51:26 +00003807 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003808 i,
3809 x;
3810
cristy4c08aed2011-07-01 19:47:50 +00003811 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003812 *q;
3813
3814 register unsigned char
3815 *p;
3816
3817 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003818 read_JSEP,
3819 reading_idat,
3820 skip_to_iend;
3821
cristybb503372010-05-27 20:51:26 +00003822 size_t
cristy3ed852e2009-09-05 21:47:34 +00003823 length;
3824
3825 jng_alpha_compression_method=0;
3826 jng_alpha_sample_depth=8;
3827 jng_color_type=0;
3828 jng_height=0;
3829 jng_width=0;
3830 alpha_image=(Image *) NULL;
3831 color_image=(Image *) NULL;
3832 alpha_image_info=(ImageInfo *) NULL;
3833 color_image_info=(ImageInfo *) NULL;
3834
3835 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003836 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003837
3838 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003839
cristy4c08aed2011-07-01 19:47:50 +00003840 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003841 {
3842 /*
3843 Allocate next image structure.
3844 */
3845 if (logging != MagickFalse)
3846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3847 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003848
cristy9950d572011-10-01 18:22:35 +00003849 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003850
cristy3ed852e2009-09-05 21:47:34 +00003851 if (GetNextImageInList(image) == (Image *) NULL)
3852 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003853
cristy3ed852e2009-09-05 21:47:34 +00003854 image=SyncNextImageInList(image);
3855 }
3856 mng_info->image=image;
3857
3858 /*
3859 Signature bytes have already been read.
3860 */
3861
3862 read_JSEP=MagickFalse;
3863 reading_idat=MagickFalse;
3864 skip_to_iend=MagickFalse;
3865 for (;;)
3866 {
3867 char
3868 type[MaxTextExtent];
3869
3870 unsigned char
3871 *chunk;
3872
3873 unsigned int
3874 count;
3875
3876 /*
3877 Read a new JNG chunk.
3878 */
3879 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3880 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003881
cristy3ed852e2009-09-05 21:47:34 +00003882 if (status == MagickFalse)
3883 break;
glennrp0fe50b42010-11-16 03:52:51 +00003884
cristy3ed852e2009-09-05 21:47:34 +00003885 type[0]='\0';
3886 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3887 length=ReadBlobMSBLong(image);
3888 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3889
3890 if (logging != MagickFalse)
3891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003892 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3893 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003894
3895 if (length > PNG_UINT_31_MAX || count == 0)
3896 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003897
cristy3ed852e2009-09-05 21:47:34 +00003898 p=NULL;
3899 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003900
cristy3ed852e2009-09-05 21:47:34 +00003901 if (length)
3902 {
3903 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003904
cristy3ed852e2009-09-05 21:47:34 +00003905 if (chunk == (unsigned char *) NULL)
3906 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003907
cristybb503372010-05-27 20:51:26 +00003908 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003909 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003910
cristy3ed852e2009-09-05 21:47:34 +00003911 p=chunk;
3912 }
glennrp47b9dd52010-11-24 18:12:06 +00003913
cristy3ed852e2009-09-05 21:47:34 +00003914 (void) ReadBlobMSBLong(image); /* read crc word */
3915
3916 if (skip_to_iend)
3917 {
3918 if (length)
3919 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003920
cristy3ed852e2009-09-05 21:47:34 +00003921 continue;
3922 }
3923
3924 if (memcmp(type,mng_JHDR,4) == 0)
3925 {
3926 if (length == 16)
3927 {
cristybb503372010-05-27 20:51:26 +00003928 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003929 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003930 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003931 (p[6] << 8) | p[7]);
3932 jng_color_type=p[8];
3933 jng_image_sample_depth=p[9];
3934 jng_image_compression_method=p[10];
3935 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003936
cristy3ed852e2009-09-05 21:47:34 +00003937 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3938 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003939
cristy3ed852e2009-09-05 21:47:34 +00003940 jng_alpha_sample_depth=p[12];
3941 jng_alpha_compression_method=p[13];
3942 jng_alpha_filter_method=p[14];
3943 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003944
cristy3ed852e2009-09-05 21:47:34 +00003945 if (logging != MagickFalse)
3946 {
3947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003948 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003949
cristy3ed852e2009-09-05 21:47:34 +00003950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003951 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003952
cristy3ed852e2009-09-05 21:47:34 +00003953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3954 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003955
cristy3ed852e2009-09-05 21:47:34 +00003956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3957 " jng_image_sample_depth: %3d",
3958 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003959
cristy3ed852e2009-09-05 21:47:34 +00003960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3961 " jng_image_compression_method:%3d",
3962 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003963
cristy3ed852e2009-09-05 21:47:34 +00003964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3965 " jng_image_interlace_method: %3d",
3966 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003967
cristy3ed852e2009-09-05 21:47:34 +00003968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3969 " jng_alpha_sample_depth: %3d",
3970 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003971
cristy3ed852e2009-09-05 21:47:34 +00003972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3973 " jng_alpha_compression_method:%3d",
3974 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003975
cristy3ed852e2009-09-05 21:47:34 +00003976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3977 " jng_alpha_filter_method: %3d",
3978 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003979
cristy3ed852e2009-09-05 21:47:34 +00003980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3981 " jng_alpha_interlace_method: %3d",
3982 jng_alpha_interlace_method);
3983 }
3984 }
glennrp47b9dd52010-11-24 18:12:06 +00003985
cristy3ed852e2009-09-05 21:47:34 +00003986 if (length)
3987 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003988
cristy3ed852e2009-09-05 21:47:34 +00003989 continue;
3990 }
3991
3992
3993 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3994 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3995 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3996 {
3997 /*
3998 o create color_image
3999 o open color_blob, attached to color_image
4000 o if (color type has alpha)
4001 open alpha_blob, attached to alpha_image
4002 */
4003
cristy73bd4a52010-10-05 11:24:23 +00004004 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004005
cristy3ed852e2009-09-05 21:47:34 +00004006 if (color_image_info == (ImageInfo *) NULL)
4007 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004008
cristy3ed852e2009-09-05 21:47:34 +00004009 GetImageInfo(color_image_info);
cristy9950d572011-10-01 18:22:35 +00004010 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004011
cristy3ed852e2009-09-05 21:47:34 +00004012 if (color_image == (Image *) NULL)
4013 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4014
4015 if (logging != MagickFalse)
4016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4017 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004018
cristy3ed852e2009-09-05 21:47:34 +00004019 (void) AcquireUniqueFilename(color_image->filename);
4020 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4021 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004022
cristy3ed852e2009-09-05 21:47:34 +00004023 if (status == MagickFalse)
4024 return((Image *) NULL);
4025
4026 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4027 {
4028 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004029 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004030
cristy3ed852e2009-09-05 21:47:34 +00004031 if (alpha_image_info == (ImageInfo *) NULL)
4032 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004033
cristy3ed852e2009-09-05 21:47:34 +00004034 GetImageInfo(alpha_image_info);
cristy9950d572011-10-01 18:22:35 +00004035 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004036
cristy3ed852e2009-09-05 21:47:34 +00004037 if (alpha_image == (Image *) NULL)
4038 {
4039 alpha_image=DestroyImage(alpha_image);
4040 ThrowReaderException(ResourceLimitError,
4041 "MemoryAllocationFailed");
4042 }
glennrp0fe50b42010-11-16 03:52:51 +00004043
cristy3ed852e2009-09-05 21:47:34 +00004044 if (logging != MagickFalse)
4045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4046 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004047
cristy3ed852e2009-09-05 21:47:34 +00004048 (void) AcquireUniqueFilename(alpha_image->filename);
4049 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4050 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004051
cristy3ed852e2009-09-05 21:47:34 +00004052 if (status == MagickFalse)
4053 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004054
cristy3ed852e2009-09-05 21:47:34 +00004055 if (jng_alpha_compression_method == 0)
4056 {
4057 unsigned char
4058 data[18];
4059
4060 if (logging != MagickFalse)
4061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4062 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004063
cristy3ed852e2009-09-05 21:47:34 +00004064 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4065 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004066
cristy3ed852e2009-09-05 21:47:34 +00004067 (void) WriteBlobMSBULong(alpha_image,13L);
4068 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004069 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004070 PNGLong(data+4,jng_width);
4071 PNGLong(data+8,jng_height);
4072 data[12]=jng_alpha_sample_depth;
4073 data[13]=0; /* color_type gray */
4074 data[14]=0; /* compression method 0 */
4075 data[15]=0; /* filter_method 0 */
4076 data[16]=0; /* interlace_method 0 */
4077 (void) WriteBlob(alpha_image,17,data);
4078 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4079 }
4080 }
4081 reading_idat=MagickTrue;
4082 }
4083
4084 if (memcmp(type,mng_JDAT,4) == 0)
4085 {
glennrp47b9dd52010-11-24 18:12:06 +00004086 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004087
4088 if (logging != MagickFalse)
4089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4090 " Copying JDAT chunk data to color_blob.");
4091
4092 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004093
cristy3ed852e2009-09-05 21:47:34 +00004094 if (length)
4095 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004096
cristy3ed852e2009-09-05 21:47:34 +00004097 continue;
4098 }
4099
4100 if (memcmp(type,mng_IDAT,4) == 0)
4101 {
4102 png_byte
4103 data[5];
4104
glennrp47b9dd52010-11-24 18:12:06 +00004105 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004106
4107 if (image_info->ping == MagickFalse)
4108 {
4109 if (logging != MagickFalse)
4110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4111 " Copying IDAT chunk data to alpha_blob.");
4112
cristybb503372010-05-27 20:51:26 +00004113 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004114 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004115 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004116 (void) WriteBlob(alpha_image,4,data);
4117 (void) WriteBlob(alpha_image,length,chunk);
4118 (void) WriteBlobMSBULong(alpha_image,
4119 crc32(crc32(0,data,4),chunk,(uInt) length));
4120 }
glennrp0fe50b42010-11-16 03:52:51 +00004121
cristy3ed852e2009-09-05 21:47:34 +00004122 if (length)
4123 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004124
cristy3ed852e2009-09-05 21:47:34 +00004125 continue;
4126 }
4127
4128 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4129 {
glennrp47b9dd52010-11-24 18:12:06 +00004130 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004131
4132 if (image_info->ping == MagickFalse)
4133 {
4134 if (logging != MagickFalse)
4135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4136 " Copying JDAA chunk data to alpha_blob.");
4137
4138 (void) WriteBlob(alpha_image,length,chunk);
4139 }
glennrp0fe50b42010-11-16 03:52:51 +00004140
cristy3ed852e2009-09-05 21:47:34 +00004141 if (length)
4142 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004143
cristy3ed852e2009-09-05 21:47:34 +00004144 continue;
4145 }
4146
4147 if (memcmp(type,mng_JSEP,4) == 0)
4148 {
4149 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004150
cristy3ed852e2009-09-05 21:47:34 +00004151 if (length)
4152 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004153
cristy3ed852e2009-09-05 21:47:34 +00004154 continue;
4155 }
4156
4157 if (memcmp(type,mng_bKGD,4) == 0)
4158 {
4159 if (length == 2)
4160 {
4161 image->background_color.red=ScaleCharToQuantum(p[1]);
4162 image->background_color.green=image->background_color.red;
4163 image->background_color.blue=image->background_color.red;
4164 }
glennrp0fe50b42010-11-16 03:52:51 +00004165
cristy3ed852e2009-09-05 21:47:34 +00004166 if (length == 6)
4167 {
4168 image->background_color.red=ScaleCharToQuantum(p[1]);
4169 image->background_color.green=ScaleCharToQuantum(p[3]);
4170 image->background_color.blue=ScaleCharToQuantum(p[5]);
4171 }
glennrp0fe50b42010-11-16 03:52:51 +00004172
cristy3ed852e2009-09-05 21:47:34 +00004173 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4174 continue;
4175 }
4176
4177 if (memcmp(type,mng_gAMA,4) == 0)
4178 {
4179 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004180 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004181
cristy3ed852e2009-09-05 21:47:34 +00004182 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4183 continue;
4184 }
4185
4186 if (memcmp(type,mng_cHRM,4) == 0)
4187 {
4188 if (length == 32)
4189 {
cristy8182b072010-05-30 20:10:53 +00004190 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4191 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4192 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4193 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4194 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4195 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4196 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4197 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004198 }
glennrp47b9dd52010-11-24 18:12:06 +00004199
cristy3ed852e2009-09-05 21:47:34 +00004200 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4201 continue;
4202 }
4203
4204 if (memcmp(type,mng_sRGB,4) == 0)
4205 {
4206 if (length == 1)
4207 {
glennrpe610a072010-08-05 17:08:46 +00004208 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004209 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004210 image->gamma=0.45455f;
4211 image->chromaticity.red_primary.x=0.6400f;
4212 image->chromaticity.red_primary.y=0.3300f;
4213 image->chromaticity.green_primary.x=0.3000f;
4214 image->chromaticity.green_primary.y=0.6000f;
4215 image->chromaticity.blue_primary.x=0.1500f;
4216 image->chromaticity.blue_primary.y=0.0600f;
4217 image->chromaticity.white_point.x=0.3127f;
4218 image->chromaticity.white_point.y=0.3290f;
4219 }
glennrp47b9dd52010-11-24 18:12:06 +00004220
cristy3ed852e2009-09-05 21:47:34 +00004221 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4222 continue;
4223 }
4224
4225 if (memcmp(type,mng_oFFs,4) == 0)
4226 {
4227 if (length > 8)
4228 {
glennrp5eae7602011-02-22 15:21:32 +00004229 image->page.x=(ssize_t) mng_get_long(p);
4230 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004231
cristy3ed852e2009-09-05 21:47:34 +00004232 if ((int) p[8] != 0)
4233 {
4234 image->page.x/=10000;
4235 image->page.y/=10000;
4236 }
4237 }
glennrp47b9dd52010-11-24 18:12:06 +00004238
cristy3ed852e2009-09-05 21:47:34 +00004239 if (length)
4240 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004241
cristy3ed852e2009-09-05 21:47:34 +00004242 continue;
4243 }
4244
4245 if (memcmp(type,mng_pHYs,4) == 0)
4246 {
4247 if (length > 8)
4248 {
cristy8182b072010-05-30 20:10:53 +00004249 image->x_resolution=(double) mng_get_long(p);
4250 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004251 if ((int) p[8] == PNG_RESOLUTION_METER)
4252 {
4253 image->units=PixelsPerCentimeterResolution;
4254 image->x_resolution=image->x_resolution/100.0f;
4255 image->y_resolution=image->y_resolution/100.0f;
4256 }
4257 }
glennrp0fe50b42010-11-16 03:52:51 +00004258
cristy3ed852e2009-09-05 21:47:34 +00004259 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4260 continue;
4261 }
4262
4263#if 0
4264 if (memcmp(type,mng_iCCP,4) == 0)
4265 {
glennrpfd05d622011-02-25 04:10:33 +00004266 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004267 if (length)
4268 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004269
cristy3ed852e2009-09-05 21:47:34 +00004270 continue;
4271 }
4272#endif
4273
4274 if (length)
4275 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4276
4277 if (memcmp(type,mng_IEND,4))
4278 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004279
cristy3ed852e2009-09-05 21:47:34 +00004280 break;
4281 }
4282
4283
4284 /* IEND found */
4285
4286 /*
4287 Finish up reading image data:
4288
4289 o read main image from color_blob.
4290
4291 o close color_blob.
4292
4293 o if (color_type has alpha)
4294 if alpha_encoding is PNG
4295 read secondary image from alpha_blob via ReadPNG
4296 if alpha_encoding is JPEG
4297 read secondary image from alpha_blob via ReadJPEG
4298
4299 o close alpha_blob.
4300
4301 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004302 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004303
4304 o destroy the secondary image.
4305 */
4306
4307 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004308
cristy3ed852e2009-09-05 21:47:34 +00004309 if (logging != MagickFalse)
4310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4311 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004312
cristy3b6fd2e2011-05-20 12:53:50 +00004313 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004314 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004315
cristy3ed852e2009-09-05 21:47:34 +00004316 color_image_info->ping=MagickFalse; /* To do: avoid this */
4317 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004318
cristy3ed852e2009-09-05 21:47:34 +00004319 if (jng_image == (Image *) NULL)
4320 return((Image *) NULL);
4321
4322 (void) RelinquishUniqueFileResource(color_image->filename);
4323 color_image=DestroyImage(color_image);
4324 color_image_info=DestroyImageInfo(color_image_info);
4325
4326 if (jng_image == (Image *) NULL)
4327 return((Image *) NULL);
4328
4329 if (logging != MagickFalse)
4330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4331 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004332
cristy3ed852e2009-09-05 21:47:34 +00004333 image->rows=jng_height;
4334 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004335
cristybb503372010-05-27 20:51:26 +00004336 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004337 {
4338 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
4339 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004340 for (x=(ssize_t) image->columns; x != 0; x--)
4341 {
4342 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4343 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4344 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004345 q+=GetPixelChannels(image);
4346 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004347 }
glennrp47b9dd52010-11-24 18:12:06 +00004348
cristy3ed852e2009-09-05 21:47:34 +00004349 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4350 break;
4351 }
glennrp0fe50b42010-11-16 03:52:51 +00004352
cristy3ed852e2009-09-05 21:47:34 +00004353 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004354
cristy3ed852e2009-09-05 21:47:34 +00004355 if (image_info->ping == MagickFalse)
4356 {
4357 if (jng_color_type >= 12)
4358 {
4359 if (jng_alpha_compression_method == 0)
4360 {
4361 png_byte
4362 data[5];
4363 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4364 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004365 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004366 (void) WriteBlob(alpha_image,4,data);
4367 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4368 }
glennrp0fe50b42010-11-16 03:52:51 +00004369
cristy3ed852e2009-09-05 21:47:34 +00004370 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004371
cristy3ed852e2009-09-05 21:47:34 +00004372 if (logging != MagickFalse)
4373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004374 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004375
cristy3b6fd2e2011-05-20 12:53:50 +00004376 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004377 "%s",alpha_image->filename);
4378
4379 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004380
cristy3ed852e2009-09-05 21:47:34 +00004381 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004382 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004383 {
4384 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy4c08aed2011-07-01 19:47:50 +00004385 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00004386 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004387
cristy3ed852e2009-09-05 21:47:34 +00004388 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004389 for (x=(ssize_t) image->columns; x != 0; x--)
4390 {
4391 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
cristyed231572011-07-14 02:18:59 +00004392 q+=GetPixelChannels(image);
4393 s+=GetPixelChannels(jng_image);
cristy4c08aed2011-07-01 19:47:50 +00004394 }
glennrp0fe50b42010-11-16 03:52:51 +00004395
cristy3ed852e2009-09-05 21:47:34 +00004396 else
cristy4c08aed2011-07-01 19:47:50 +00004397 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004398 {
cristy4c08aed2011-07-01 19:47:50 +00004399 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4400 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004401 image->matte=MagickTrue;
cristyed231572011-07-14 02:18:59 +00004402 q+=GetPixelChannels(image);
4403 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004404 }
glennrp0fe50b42010-11-16 03:52:51 +00004405
cristy3ed852e2009-09-05 21:47:34 +00004406 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4407 break;
4408 }
4409 (void) RelinquishUniqueFileResource(alpha_image->filename);
4410 alpha_image=DestroyImage(alpha_image);
4411 alpha_image_info=DestroyImageInfo(alpha_image_info);
4412 if (jng_image != (Image *) NULL)
4413 jng_image=DestroyImage(jng_image);
4414 }
4415 }
4416
glennrp47b9dd52010-11-24 18:12:06 +00004417 /* Read the JNG image. */
4418
cristy3ed852e2009-09-05 21:47:34 +00004419 if (mng_info->mng_type == 0)
4420 {
4421 mng_info->mng_width=jng_width;
4422 mng_info->mng_height=jng_height;
4423 }
glennrp0fe50b42010-11-16 03:52:51 +00004424
cristy3ed852e2009-09-05 21:47:34 +00004425 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004426 {
4427 image->page.width=jng_width;
4428 image->page.height=jng_height;
4429 }
4430
cristy3ed852e2009-09-05 21:47:34 +00004431 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004432 {
4433 image->page.x=mng_info->x_off[mng_info->object_id];
4434 image->page.y=mng_info->y_off[mng_info->object_id];
4435 }
4436
cristy3ed852e2009-09-05 21:47:34 +00004437 else
glennrp0fe50b42010-11-16 03:52:51 +00004438 {
4439 image->page.y=mng_info->y_off[mng_info->object_id];
4440 }
4441
cristy3ed852e2009-09-05 21:47:34 +00004442 mng_info->image_found++;
4443 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4444 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004445
cristy3ed852e2009-09-05 21:47:34 +00004446 if (logging != MagickFalse)
4447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4448 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004449
cristy3ed852e2009-09-05 21:47:34 +00004450 return(image);
4451}
4452
4453/*
4454%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4455% %
4456% %
4457% %
4458% R e a d J N G I m a g e %
4459% %
4460% %
4461% %
4462%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4463%
4464% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4465% (including the 8-byte signature) and returns it. It allocates the memory
4466% necessary for the new Image structure and returns a pointer to the new
4467% image.
4468%
4469% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4470%
4471% The format of the ReadJNGImage method is:
4472%
4473% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4474% *exception)
4475%
4476% A description of each parameter follows:
4477%
4478% o image_info: the image info.
4479%
4480% o exception: return any errors or warnings in this structure.
4481%
4482*/
4483
4484static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4485{
4486 Image
4487 *image,
4488 *previous;
4489
4490 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004491 have_mng_structure,
4492 logging,
cristy3ed852e2009-09-05 21:47:34 +00004493 status;
4494
4495 MngInfo
4496 *mng_info;
4497
4498 char
4499 magic_number[MaxTextExtent];
4500
cristy3ed852e2009-09-05 21:47:34 +00004501 size_t
4502 count;
4503
4504 /*
4505 Open image file.
4506 */
4507 assert(image_info != (const ImageInfo *) NULL);
4508 assert(image_info->signature == MagickSignature);
4509 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4510 assert(exception != (ExceptionInfo *) NULL);
4511 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004512 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy9950d572011-10-01 18:22:35 +00004513 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004514 mng_info=(MngInfo *) NULL;
4515 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004516
cristy3ed852e2009-09-05 21:47:34 +00004517 if (status == MagickFalse)
4518 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004519
cristy3ed852e2009-09-05 21:47:34 +00004520 if (LocaleCompare(image_info->magick,"JNG") != 0)
4521 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004522
glennrp47b9dd52010-11-24 18:12:06 +00004523 /* Verify JNG signature. */
4524
cristy3ed852e2009-09-05 21:47:34 +00004525 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004526
glennrp3b8763e2011-02-21 12:08:18 +00004527 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004528 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004529
glennrp47b9dd52010-11-24 18:12:06 +00004530 /* Allocate a MngInfo structure. */
4531
cristy3ed852e2009-09-05 21:47:34 +00004532 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004533 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004534
cristy3ed852e2009-09-05 21:47:34 +00004535 if (mng_info == (MngInfo *) NULL)
4536 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004537
glennrp47b9dd52010-11-24 18:12:06 +00004538 /* Initialize members of the MngInfo structure. */
4539
cristy3ed852e2009-09-05 21:47:34 +00004540 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4541 have_mng_structure=MagickTrue;
4542
4543 mng_info->image=image;
4544 previous=image;
4545 image=ReadOneJNGImage(mng_info,image_info,exception);
4546 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004547
cristy3ed852e2009-09-05 21:47:34 +00004548 if (image == (Image *) NULL)
4549 {
4550 if (IsImageObject(previous) != MagickFalse)
4551 {
4552 (void) CloseBlob(previous);
4553 (void) DestroyImageList(previous);
4554 }
glennrp0fe50b42010-11-16 03:52:51 +00004555
cristy3ed852e2009-09-05 21:47:34 +00004556 if (logging != MagickFalse)
4557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4558 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004559
cristy3ed852e2009-09-05 21:47:34 +00004560 return((Image *) NULL);
4561 }
4562 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004563
cristy3ed852e2009-09-05 21:47:34 +00004564 if (image->columns == 0 || image->rows == 0)
4565 {
4566 if (logging != MagickFalse)
4567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4568 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004569
cristy3ed852e2009-09-05 21:47:34 +00004570 ThrowReaderException(CorruptImageError,"CorruptImage");
4571 }
glennrp0fe50b42010-11-16 03:52:51 +00004572
cristy3ed852e2009-09-05 21:47:34 +00004573 if (logging != MagickFalse)
4574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004575
cristy3ed852e2009-09-05 21:47:34 +00004576 return(image);
4577}
4578#endif
4579
4580static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4581{
4582 char
4583 page_geometry[MaxTextExtent];
4584
4585 Image
4586 *image,
4587 *previous;
4588
cristy4383ec82011-01-05 15:42:32 +00004589 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004590 logging,
4591 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004592
cristy3ed852e2009-09-05 21:47:34 +00004593 volatile int
4594 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004595 object_id,
4596 term_chunk_found,
4597 skip_to_iend;
4598
cristybb503372010-05-27 20:51:26 +00004599 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004600 image_count=0;
4601
4602 MagickBooleanType
4603 status;
4604
4605 MagickOffsetType
4606 offset;
4607
4608 MngInfo
4609 *mng_info;
4610
4611 MngBox
4612 default_fb,
4613 fb,
4614 previous_fb;
4615
4616#if defined(MNG_INSERT_LAYERS)
cristy101ab702011-10-13 13:06:32 +00004617 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004618 mng_background_color;
4619#endif
4620
4621 register unsigned char
4622 *p;
4623
cristybb503372010-05-27 20:51:26 +00004624 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004625 i;
4626
4627 size_t
4628 count;
4629
cristybb503372010-05-27 20:51:26 +00004630 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004631 loop_level;
4632
4633 volatile short
4634 skipping_loop;
4635
4636#if defined(MNG_INSERT_LAYERS)
4637 unsigned int
4638 mandatory_back=0;
4639#endif
4640
4641 volatile unsigned int
4642#ifdef MNG_OBJECT_BUFFERS
4643 mng_background_object=0,
4644#endif
4645 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4646
cristybb503372010-05-27 20:51:26 +00004647 size_t
cristy3ed852e2009-09-05 21:47:34 +00004648 default_frame_timeout,
4649 frame_timeout,
4650#if defined(MNG_INSERT_LAYERS)
4651 image_height,
4652 image_width,
4653#endif
4654 length;
4655
glennrp38ea0832010-06-02 18:50:28 +00004656 /* These delays are all measured in image ticks_per_second,
4657 * not in MNG ticks_per_second
4658 */
cristybb503372010-05-27 20:51:26 +00004659 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004660 default_frame_delay,
4661 final_delay,
4662 final_image_delay,
4663 frame_delay,
4664#if defined(MNG_INSERT_LAYERS)
4665 insert_layers,
4666#endif
4667 mng_iterations=1,
4668 simplicity=0,
4669 subframe_height=0,
4670 subframe_width=0;
4671
4672 previous_fb.top=0;
4673 previous_fb.bottom=0;
4674 previous_fb.left=0;
4675 previous_fb.right=0;
4676 default_fb.top=0;
4677 default_fb.bottom=0;
4678 default_fb.left=0;
4679 default_fb.right=0;
4680
glennrp47b9dd52010-11-24 18:12:06 +00004681 /* Open image file. */
4682
cristy3ed852e2009-09-05 21:47:34 +00004683 assert(image_info != (const ImageInfo *) NULL);
4684 assert(image_info->signature == MagickSignature);
4685 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4686 assert(exception != (ExceptionInfo *) NULL);
4687 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004688 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy9950d572011-10-01 18:22:35 +00004689 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004690 mng_info=(MngInfo *) NULL;
4691 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004692
cristy3ed852e2009-09-05 21:47:34 +00004693 if (status == MagickFalse)
4694 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004695
cristy3ed852e2009-09-05 21:47:34 +00004696 first_mng_object=MagickFalse;
4697 skipping_loop=(-1);
4698 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004699
4700 /* Allocate a MngInfo structure. */
4701
cristy73bd4a52010-10-05 11:24:23 +00004702 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004703
cristy3ed852e2009-09-05 21:47:34 +00004704 if (mng_info == (MngInfo *) NULL)
4705 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004706
glennrp47b9dd52010-11-24 18:12:06 +00004707 /* Initialize members of the MngInfo structure. */
4708
cristy3ed852e2009-09-05 21:47:34 +00004709 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4710 mng_info->image=image;
4711 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004712
4713 if (LocaleCompare(image_info->magick,"MNG") == 0)
4714 {
4715 char
4716 magic_number[MaxTextExtent];
4717
glennrp47b9dd52010-11-24 18:12:06 +00004718 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004719 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4720 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4721 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004722
4723 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004724 for (i=0; i < MNG_MAX_OBJECTS; i++)
4725 {
cristybb503372010-05-27 20:51:26 +00004726 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4727 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004728 }
4729 mng_info->exists[0]=MagickTrue;
4730 }
glennrp47b9dd52010-11-24 18:12:06 +00004731
cristy3ed852e2009-09-05 21:47:34 +00004732 first_mng_object=MagickTrue;
4733 mng_type=0;
4734#if defined(MNG_INSERT_LAYERS)
4735 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4736#endif
4737 default_frame_delay=0;
4738 default_frame_timeout=0;
4739 frame_delay=0;
4740 final_delay=1;
4741 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4742 object_id=0;
4743 skip_to_iend=MagickFalse;
4744 term_chunk_found=MagickFalse;
4745 mng_info->framing_mode=1;
4746#if defined(MNG_INSERT_LAYERS)
4747 mandatory_back=MagickFalse;
4748#endif
4749#if defined(MNG_INSERT_LAYERS)
4750 mng_background_color=image->background_color;
4751#endif
4752 default_fb=mng_info->frame;
4753 previous_fb=mng_info->frame;
4754 do
4755 {
4756 char
4757 type[MaxTextExtent];
4758
4759 if (LocaleCompare(image_info->magick,"MNG") == 0)
4760 {
4761 unsigned char
4762 *chunk;
4763
4764 /*
4765 Read a new chunk.
4766 */
4767 type[0]='\0';
4768 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4769 length=ReadBlobMSBLong(image);
4770 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4771
4772 if (logging != MagickFalse)
4773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004774 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4775 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004776
4777 if (length > PNG_UINT_31_MAX)
4778 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004779
cristy3ed852e2009-09-05 21:47:34 +00004780 if (count == 0)
4781 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004782
cristy3ed852e2009-09-05 21:47:34 +00004783 p=NULL;
4784 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004785
cristy3ed852e2009-09-05 21:47:34 +00004786 if (length)
4787 {
4788 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004789
cristy3ed852e2009-09-05 21:47:34 +00004790 if (chunk == (unsigned char *) NULL)
4791 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004792
cristybb503372010-05-27 20:51:26 +00004793 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004794 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004795
cristy3ed852e2009-09-05 21:47:34 +00004796 p=chunk;
4797 }
glennrp0fe50b42010-11-16 03:52:51 +00004798
cristy3ed852e2009-09-05 21:47:34 +00004799 (void) ReadBlobMSBLong(image); /* read crc word */
4800
4801#if !defined(JNG_SUPPORTED)
4802 if (memcmp(type,mng_JHDR,4) == 0)
4803 {
4804 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004805
cristy3ed852e2009-09-05 21:47:34 +00004806 if (mng_info->jhdr_warning == 0)
4807 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4808 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004809
cristy3ed852e2009-09-05 21:47:34 +00004810 mng_info->jhdr_warning++;
4811 }
4812#endif
4813 if (memcmp(type,mng_DHDR,4) == 0)
4814 {
4815 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004816
cristy3ed852e2009-09-05 21:47:34 +00004817 if (mng_info->dhdr_warning == 0)
4818 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4819 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004820
cristy3ed852e2009-09-05 21:47:34 +00004821 mng_info->dhdr_warning++;
4822 }
4823 if (memcmp(type,mng_MEND,4) == 0)
4824 break;
glennrp47b9dd52010-11-24 18:12:06 +00004825
cristy3ed852e2009-09-05 21:47:34 +00004826 if (skip_to_iend)
4827 {
4828 if (memcmp(type,mng_IEND,4) == 0)
4829 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004830
cristy3ed852e2009-09-05 21:47:34 +00004831 if (length)
4832 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004833
cristy3ed852e2009-09-05 21:47:34 +00004834 if (logging != MagickFalse)
4835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4836 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004837
cristy3ed852e2009-09-05 21:47:34 +00004838 continue;
4839 }
glennrp0fe50b42010-11-16 03:52:51 +00004840
cristy3ed852e2009-09-05 21:47:34 +00004841 if (memcmp(type,mng_MHDR,4) == 0)
4842 {
cristybb503372010-05-27 20:51:26 +00004843 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004844 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004845
cristybb503372010-05-27 20:51:26 +00004846 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004847 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004848
cristy3ed852e2009-09-05 21:47:34 +00004849 if (logging != MagickFalse)
4850 {
4851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004852 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004854 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004855 }
glennrp0fe50b42010-11-16 03:52:51 +00004856
cristy3ed852e2009-09-05 21:47:34 +00004857 p+=8;
cristy8182b072010-05-30 20:10:53 +00004858 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004859
cristy3ed852e2009-09-05 21:47:34 +00004860 if (mng_info->ticks_per_second == 0)
4861 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004862
cristy3ed852e2009-09-05 21:47:34 +00004863 else
4864 default_frame_delay=1UL*image->ticks_per_second/
4865 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004866
cristy3ed852e2009-09-05 21:47:34 +00004867 frame_delay=default_frame_delay;
4868 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004869
cristy3ed852e2009-09-05 21:47:34 +00004870 if (length > 16)
4871 {
4872 p+=16;
cristy8182b072010-05-30 20:10:53 +00004873 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004874 }
glennrp0fe50b42010-11-16 03:52:51 +00004875
cristy3ed852e2009-09-05 21:47:34 +00004876 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004877
cristy3ed852e2009-09-05 21:47:34 +00004878 if ((simplicity != 0) && ((simplicity | 11) == 11))
4879 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004880
cristy3ed852e2009-09-05 21:47:34 +00004881 if ((simplicity != 0) && ((simplicity | 9) == 9))
4882 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004883
cristy3ed852e2009-09-05 21:47:34 +00004884#if defined(MNG_INSERT_LAYERS)
4885 if (mng_type != 3)
4886 insert_layers=MagickTrue;
4887#endif
cristy4c08aed2011-07-01 19:47:50 +00004888 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004889 {
glennrp47b9dd52010-11-24 18:12:06 +00004890 /* Allocate next image structure. */
cristy9950d572011-10-01 18:22:35 +00004891 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004892
cristy3ed852e2009-09-05 21:47:34 +00004893 if (GetNextImageInList(image) == (Image *) NULL)
4894 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004895
cristy3ed852e2009-09-05 21:47:34 +00004896 image=SyncNextImageInList(image);
4897 mng_info->image=image;
4898 }
4899
4900 if ((mng_info->mng_width > 65535L) ||
4901 (mng_info->mng_height > 65535L))
4902 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004903
cristy3b6fd2e2011-05-20 12:53:50 +00004904 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004905 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004906 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004907
cristy3ed852e2009-09-05 21:47:34 +00004908 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004909 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004910 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004911 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004912 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004913
cristy3ed852e2009-09-05 21:47:34 +00004914 for (i=0; i < MNG_MAX_OBJECTS; i++)
4915 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004916
cristy3ed852e2009-09-05 21:47:34 +00004917 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4918 continue;
4919 }
4920
4921 if (memcmp(type,mng_TERM,4) == 0)
4922 {
4923 int
4924 repeat=0;
4925
4926
4927 if (length)
4928 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004929
cristy3ed852e2009-09-05 21:47:34 +00004930 if (repeat == 3)
4931 {
cristy8182b072010-05-30 20:10:53 +00004932 final_delay=(png_uint_32) mng_get_long(&p[2]);
4933 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004934
cristy3ed852e2009-09-05 21:47:34 +00004935 if (mng_iterations == PNG_UINT_31_MAX)
4936 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004937
cristy3ed852e2009-09-05 21:47:34 +00004938 image->iterations=mng_iterations;
4939 term_chunk_found=MagickTrue;
4940 }
glennrp0fe50b42010-11-16 03:52:51 +00004941
cristy3ed852e2009-09-05 21:47:34 +00004942 if (logging != MagickFalse)
4943 {
4944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4945 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004946
cristy3ed852e2009-09-05 21:47:34 +00004947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004948 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004949
cristy3ed852e2009-09-05 21:47:34 +00004950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004951 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004952 }
glennrp0fe50b42010-11-16 03:52:51 +00004953
cristy3ed852e2009-09-05 21:47:34 +00004954 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4955 continue;
4956 }
4957 if (memcmp(type,mng_DEFI,4) == 0)
4958 {
4959 if (mng_type == 3)
4960 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4961 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4962 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004963
cristy3ed852e2009-09-05 21:47:34 +00004964 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004965
cristy3ed852e2009-09-05 21:47:34 +00004966 if (mng_type == 2 && object_id != 0)
4967 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4968 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4969 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004970
cristy3ed852e2009-09-05 21:47:34 +00004971 if (object_id > MNG_MAX_OBJECTS)
4972 {
4973 /*
4974 Instead ofsuing a warning we should allocate a larger
4975 MngInfo structure and continue.
4976 */
4977 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4978 CoderError,"object id too large","`%s'",image->filename);
4979 object_id=MNG_MAX_OBJECTS;
4980 }
glennrp0fe50b42010-11-16 03:52:51 +00004981
cristy3ed852e2009-09-05 21:47:34 +00004982 if (mng_info->exists[object_id])
4983 if (mng_info->frozen[object_id])
4984 {
4985 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4986 (void) ThrowMagickException(&image->exception,
4987 GetMagickModule(),CoderError,
4988 "DEFI cannot redefine a frozen MNG object","`%s'",
4989 image->filename);
4990 continue;
4991 }
glennrp0fe50b42010-11-16 03:52:51 +00004992
cristy3ed852e2009-09-05 21:47:34 +00004993 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004994
cristy3ed852e2009-09-05 21:47:34 +00004995 if (length > 2)
4996 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004997
cristy3ed852e2009-09-05 21:47:34 +00004998 /*
4999 Extract object offset info.
5000 */
5001 if (length > 11)
5002 {
glennrp0fe50b42010-11-16 03:52:51 +00005003 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5004 (p[5] << 16) | (p[6] << 8) | p[7]);
5005
5006 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5007 (p[9] << 16) | (p[10] << 8) | p[11]);
5008
cristy3ed852e2009-09-05 21:47:34 +00005009 if (logging != MagickFalse)
5010 {
5011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005012 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005013 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005014
cristy3ed852e2009-09-05 21:47:34 +00005015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005016 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005017 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005018 }
5019 }
glennrp0fe50b42010-11-16 03:52:51 +00005020
cristy3ed852e2009-09-05 21:47:34 +00005021 /*
5022 Extract object clipping info.
5023 */
5024 if (length > 27)
5025 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5026 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005027
cristy3ed852e2009-09-05 21:47:34 +00005028 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5029 continue;
5030 }
5031 if (memcmp(type,mng_bKGD,4) == 0)
5032 {
5033 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005034
cristy3ed852e2009-09-05 21:47:34 +00005035 if (length > 5)
5036 {
5037 mng_info->mng_global_bkgd.red=
5038 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005039
cristy3ed852e2009-09-05 21:47:34 +00005040 mng_info->mng_global_bkgd.green=
5041 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005042
cristy3ed852e2009-09-05 21:47:34 +00005043 mng_info->mng_global_bkgd.blue=
5044 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005045
cristy3ed852e2009-09-05 21:47:34 +00005046 mng_info->have_global_bkgd=MagickTrue;
5047 }
glennrp0fe50b42010-11-16 03:52:51 +00005048
cristy3ed852e2009-09-05 21:47:34 +00005049 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5050 continue;
5051 }
5052 if (memcmp(type,mng_BACK,4) == 0)
5053 {
5054#if defined(MNG_INSERT_LAYERS)
5055 if (length > 6)
5056 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005057
cristy3ed852e2009-09-05 21:47:34 +00005058 else
5059 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005060
cristy3ed852e2009-09-05 21:47:34 +00005061 if (mandatory_back && length > 5)
5062 {
5063 mng_background_color.red=
5064 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005065
cristy3ed852e2009-09-05 21:47:34 +00005066 mng_background_color.green=
5067 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005068
cristy3ed852e2009-09-05 21:47:34 +00005069 mng_background_color.blue=
5070 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005071
cristy4c08aed2011-07-01 19:47:50 +00005072 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005073 }
glennrp0fe50b42010-11-16 03:52:51 +00005074
cristy3ed852e2009-09-05 21:47:34 +00005075#ifdef MNG_OBJECT_BUFFERS
5076 if (length > 8)
5077 mng_background_object=(p[7] << 8) | p[8];
5078#endif
5079#endif
5080 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5081 continue;
5082 }
glennrp47b9dd52010-11-24 18:12:06 +00005083
cristy3ed852e2009-09-05 21:47:34 +00005084 if (memcmp(type,mng_PLTE,4) == 0)
5085 {
glennrp47b9dd52010-11-24 18:12:06 +00005086 /* Read global PLTE. */
5087
cristy3ed852e2009-09-05 21:47:34 +00005088 if (length && (length < 769))
5089 {
5090 if (mng_info->global_plte == (png_colorp) NULL)
5091 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5092 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005093
cristybb503372010-05-27 20:51:26 +00005094 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005095 {
5096 mng_info->global_plte[i].red=p[3*i];
5097 mng_info->global_plte[i].green=p[3*i+1];
5098 mng_info->global_plte[i].blue=p[3*i+2];
5099 }
glennrp0fe50b42010-11-16 03:52:51 +00005100
cristy35ef8242010-06-03 16:24:13 +00005101 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005102 }
5103#ifdef MNG_LOOSE
5104 for ( ; i < 256; i++)
5105 {
5106 mng_info->global_plte[i].red=i;
5107 mng_info->global_plte[i].green=i;
5108 mng_info->global_plte[i].blue=i;
5109 }
glennrp0fe50b42010-11-16 03:52:51 +00005110
cristy3ed852e2009-09-05 21:47:34 +00005111 if (length)
5112 mng_info->global_plte_length=256;
5113#endif
5114 else
5115 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005116
cristy3ed852e2009-09-05 21:47:34 +00005117 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5118 continue;
5119 }
glennrp47b9dd52010-11-24 18:12:06 +00005120
cristy3ed852e2009-09-05 21:47:34 +00005121 if (memcmp(type,mng_tRNS,4) == 0)
5122 {
5123 /* read global tRNS */
5124
5125 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005126 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005127 mng_info->global_trns[i]=p[i];
5128
5129#ifdef MNG_LOOSE
5130 for ( ; i < 256; i++)
5131 mng_info->global_trns[i]=255;
5132#endif
cristy12560f32010-06-03 16:51:08 +00005133 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005134 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5135 continue;
5136 }
5137 if (memcmp(type,mng_gAMA,4) == 0)
5138 {
5139 if (length == 4)
5140 {
cristybb503372010-05-27 20:51:26 +00005141 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005142 igamma;
5143
cristy8182b072010-05-30 20:10:53 +00005144 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005145 mng_info->global_gamma=((float) igamma)*0.00001;
5146 mng_info->have_global_gama=MagickTrue;
5147 }
glennrp0fe50b42010-11-16 03:52:51 +00005148
cristy3ed852e2009-09-05 21:47:34 +00005149 else
5150 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005151
cristy3ed852e2009-09-05 21:47:34 +00005152 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5153 continue;
5154 }
5155
5156 if (memcmp(type,mng_cHRM,4) == 0)
5157 {
glennrp47b9dd52010-11-24 18:12:06 +00005158 /* Read global cHRM */
5159
cristy3ed852e2009-09-05 21:47:34 +00005160 if (length == 32)
5161 {
cristy8182b072010-05-30 20:10:53 +00005162 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5163 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5164 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005165 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005166 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005167 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005168 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005169 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005170 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005171 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005172 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005173 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005174 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005175 mng_info->have_global_chrm=MagickTrue;
5176 }
5177 else
5178 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005179
cristy3ed852e2009-09-05 21:47:34 +00005180 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5181 continue;
5182 }
glennrp47b9dd52010-11-24 18:12:06 +00005183
cristy3ed852e2009-09-05 21:47:34 +00005184 if (memcmp(type,mng_sRGB,4) == 0)
5185 {
5186 /*
5187 Read global sRGB.
5188 */
5189 if (length)
5190 {
glennrpe610a072010-08-05 17:08:46 +00005191 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005192 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005193 mng_info->have_global_srgb=MagickTrue;
5194 }
5195 else
5196 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005197
cristy3ed852e2009-09-05 21:47:34 +00005198 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5199 continue;
5200 }
glennrp47b9dd52010-11-24 18:12:06 +00005201
cristy3ed852e2009-09-05 21:47:34 +00005202 if (memcmp(type,mng_iCCP,4) == 0)
5203 {
glennrpfd05d622011-02-25 04:10:33 +00005204 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005205
5206 /*
5207 Read global iCCP.
5208 */
5209 if (length)
5210 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005211
cristy3ed852e2009-09-05 21:47:34 +00005212 continue;
5213 }
glennrp47b9dd52010-11-24 18:12:06 +00005214
cristy3ed852e2009-09-05 21:47:34 +00005215 if (memcmp(type,mng_FRAM,4) == 0)
5216 {
5217 if (mng_type == 3)
5218 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5219 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5220 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005221
cristy3ed852e2009-09-05 21:47:34 +00005222 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5223 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005224
cristy3ed852e2009-09-05 21:47:34 +00005225 frame_delay=default_frame_delay;
5226 frame_timeout=default_frame_timeout;
5227 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005228
cristy3ed852e2009-09-05 21:47:34 +00005229 if (length)
5230 if (p[0])
5231 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005232
cristy3ed852e2009-09-05 21:47:34 +00005233 if (logging != MagickFalse)
5234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5235 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005236
cristy3ed852e2009-09-05 21:47:34 +00005237 if (length > 6)
5238 {
glennrp47b9dd52010-11-24 18:12:06 +00005239 /* Note the delay and frame clipping boundaries. */
5240
cristy3ed852e2009-09-05 21:47:34 +00005241 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005242
cristybb503372010-05-27 20:51:26 +00005243 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005244 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005245
cristy3ed852e2009-09-05 21:47:34 +00005246 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005247
cristybb503372010-05-27 20:51:26 +00005248 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005249 {
5250 int
5251 change_delay,
5252 change_timeout,
5253 change_clipping;
5254
5255 change_delay=(*p++);
5256 change_timeout=(*p++);
5257 change_clipping=(*p++);
5258 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005259
cristy3ed852e2009-09-05 21:47:34 +00005260 if (change_delay)
5261 {
cristy8182b072010-05-30 20:10:53 +00005262 frame_delay=1UL*image->ticks_per_second*
5263 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005264
cristy8182b072010-05-30 20:10:53 +00005265 if (mng_info->ticks_per_second != 0)
5266 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005267
glennrpbb010dd2010-06-01 13:07:15 +00005268 else
5269 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005270
cristy3ed852e2009-09-05 21:47:34 +00005271 if (change_delay == 2)
5272 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005273
cristy3ed852e2009-09-05 21:47:34 +00005274 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005275
cristy3ed852e2009-09-05 21:47:34 +00005276 if (logging != MagickFalse)
5277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005278 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005279 }
glennrp47b9dd52010-11-24 18:12:06 +00005280
cristy3ed852e2009-09-05 21:47:34 +00005281 if (change_timeout)
5282 {
glennrpbb010dd2010-06-01 13:07:15 +00005283 frame_timeout=1UL*image->ticks_per_second*
5284 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005285
glennrpbb010dd2010-06-01 13:07:15 +00005286 if (mng_info->ticks_per_second != 0)
5287 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005288
glennrpbb010dd2010-06-01 13:07:15 +00005289 else
5290 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005291
cristy3ed852e2009-09-05 21:47:34 +00005292 if (change_delay == 2)
5293 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005294
cristy3ed852e2009-09-05 21:47:34 +00005295 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005296
cristy3ed852e2009-09-05 21:47:34 +00005297 if (logging != MagickFalse)
5298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005299 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005300 }
glennrp47b9dd52010-11-24 18:12:06 +00005301
cristy3ed852e2009-09-05 21:47:34 +00005302 if (change_clipping)
5303 {
5304 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5305 p+=17;
5306 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005307
cristy3ed852e2009-09-05 21:47:34 +00005308 if (logging != MagickFalse)
5309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005310 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005311 (double) fb.left,(double) fb.right,(double) fb.top,
5312 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005313
cristy3ed852e2009-09-05 21:47:34 +00005314 if (change_clipping == 2)
5315 default_fb=fb;
5316 }
5317 }
5318 }
5319 mng_info->clip=fb;
5320 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005321
cristybb503372010-05-27 20:51:26 +00005322 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005323 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005324
cristybb503372010-05-27 20:51:26 +00005325 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005326 -mng_info->clip.top);
5327 /*
5328 Insert a background layer behind the frame if framing_mode is 4.
5329 */
5330#if defined(MNG_INSERT_LAYERS)
5331 if (logging != MagickFalse)
5332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005333 " subframe_width=%.20g, subframe_height=%.20g",(double)
5334 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005335
cristy3ed852e2009-09-05 21:47:34 +00005336 if (insert_layers && (mng_info->framing_mode == 4) &&
5337 (subframe_width) && (subframe_height))
5338 {
glennrp47b9dd52010-11-24 18:12:06 +00005339 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005340 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005341 {
cristy9950d572011-10-01 18:22:35 +00005342 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005343
cristy3ed852e2009-09-05 21:47:34 +00005344 if (GetNextImageInList(image) == (Image *) NULL)
5345 {
5346 image=DestroyImageList(image);
5347 MngInfoFreeStruct(mng_info,&have_mng_structure);
5348 return((Image *) NULL);
5349 }
glennrp47b9dd52010-11-24 18:12:06 +00005350
cristy3ed852e2009-09-05 21:47:34 +00005351 image=SyncNextImageInList(image);
5352 }
glennrp0fe50b42010-11-16 03:52:51 +00005353
cristy3ed852e2009-09-05 21:47:34 +00005354 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005355
cristy3ed852e2009-09-05 21:47:34 +00005356 if (term_chunk_found)
5357 {
5358 image->start_loop=MagickTrue;
5359 image->iterations=mng_iterations;
5360 term_chunk_found=MagickFalse;
5361 }
glennrp0fe50b42010-11-16 03:52:51 +00005362
cristy3ed852e2009-09-05 21:47:34 +00005363 else
5364 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005365
cristy3ed852e2009-09-05 21:47:34 +00005366 image->columns=subframe_width;
5367 image->rows=subframe_height;
5368 image->page.width=subframe_width;
5369 image->page.height=subframe_height;
5370 image->page.x=mng_info->clip.left;
5371 image->page.y=mng_info->clip.top;
5372 image->background_color=mng_background_color;
5373 image->matte=MagickFalse;
5374 image->delay=0;
5375 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005376
cristy3ed852e2009-09-05 21:47:34 +00005377 if (logging != MagickFalse)
5378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005379 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005380 (double) mng_info->clip.left,(double) mng_info->clip.right,
5381 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005382 }
5383#endif
5384 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5385 continue;
5386 }
5387 if (memcmp(type,mng_CLIP,4) == 0)
5388 {
5389 unsigned int
5390 first_object,
5391 last_object;
5392
5393 /*
5394 Read CLIP.
5395 */
5396 first_object=(p[0] << 8) | p[1];
5397 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005398
cristy3ed852e2009-09-05 21:47:34 +00005399 for (i=(int) first_object; i <= (int) last_object; i++)
5400 {
5401 if (mng_info->exists[i] && !mng_info->frozen[i])
5402 {
5403 MngBox
5404 box;
5405
5406 box=mng_info->object_clip[i];
5407 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5408 }
5409 }
glennrp47b9dd52010-11-24 18:12:06 +00005410
cristy3ed852e2009-09-05 21:47:34 +00005411 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5412 continue;
5413 }
5414 if (memcmp(type,mng_SAVE,4) == 0)
5415 {
5416 for (i=1; i < MNG_MAX_OBJECTS; i++)
5417 if (mng_info->exists[i])
5418 {
5419 mng_info->frozen[i]=MagickTrue;
5420#ifdef MNG_OBJECT_BUFFERS
5421 if (mng_info->ob[i] != (MngBuffer *) NULL)
5422 mng_info->ob[i]->frozen=MagickTrue;
5423#endif
5424 }
glennrp0fe50b42010-11-16 03:52:51 +00005425
cristy3ed852e2009-09-05 21:47:34 +00005426 if (length)
5427 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005428
cristy3ed852e2009-09-05 21:47:34 +00005429 continue;
5430 }
5431
5432 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5433 {
glennrp47b9dd52010-11-24 18:12:06 +00005434 /* Read DISC or SEEK. */
5435
cristy3ed852e2009-09-05 21:47:34 +00005436 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5437 {
5438 for (i=1; i < MNG_MAX_OBJECTS; i++)
5439 MngInfoDiscardObject(mng_info,i);
5440 }
glennrp0fe50b42010-11-16 03:52:51 +00005441
cristy3ed852e2009-09-05 21:47:34 +00005442 else
5443 {
cristybb503372010-05-27 20:51:26 +00005444 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005445 j;
5446
cristybb503372010-05-27 20:51:26 +00005447 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005448 {
5449 i=p[j] << 8 | p[j+1];
5450 MngInfoDiscardObject(mng_info,i);
5451 }
5452 }
glennrp0fe50b42010-11-16 03:52:51 +00005453
cristy3ed852e2009-09-05 21:47:34 +00005454 if (length)
5455 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005456
cristy3ed852e2009-09-05 21:47:34 +00005457 continue;
5458 }
glennrp47b9dd52010-11-24 18:12:06 +00005459
cristy3ed852e2009-09-05 21:47:34 +00005460 if (memcmp(type,mng_MOVE,4) == 0)
5461 {
cristybb503372010-05-27 20:51:26 +00005462 size_t
cristy3ed852e2009-09-05 21:47:34 +00005463 first_object,
5464 last_object;
5465
glennrp47b9dd52010-11-24 18:12:06 +00005466 /* read MOVE */
5467
cristy3ed852e2009-09-05 21:47:34 +00005468 first_object=(p[0] << 8) | p[1];
5469 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005470 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005471 {
5472 if (mng_info->exists[i] && !mng_info->frozen[i])
5473 {
5474 MngPair
5475 new_pair;
5476
5477 MngPair
5478 old_pair;
5479
5480 old_pair.a=mng_info->x_off[i];
5481 old_pair.b=mng_info->y_off[i];
5482 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5483 mng_info->x_off[i]=new_pair.a;
5484 mng_info->y_off[i]=new_pair.b;
5485 }
5486 }
glennrp47b9dd52010-11-24 18:12:06 +00005487
cristy3ed852e2009-09-05 21:47:34 +00005488 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5489 continue;
5490 }
5491
5492 if (memcmp(type,mng_LOOP,4) == 0)
5493 {
cristybb503372010-05-27 20:51:26 +00005494 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005495 loop_level=chunk[0];
5496 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005497
5498 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005499 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005500
cristy3ed852e2009-09-05 21:47:34 +00005501 if (logging != MagickFalse)
5502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005503 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5504 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005505
cristy3ed852e2009-09-05 21:47:34 +00005506 if (loop_iters == 0)
5507 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005508
cristy3ed852e2009-09-05 21:47:34 +00005509 else
5510 {
5511 mng_info->loop_jump[loop_level]=TellBlob(image);
5512 mng_info->loop_count[loop_level]=loop_iters;
5513 }
glennrp0fe50b42010-11-16 03:52:51 +00005514
cristy3ed852e2009-09-05 21:47:34 +00005515 mng_info->loop_iteration[loop_level]=0;
5516 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5517 continue;
5518 }
glennrp47b9dd52010-11-24 18:12:06 +00005519
cristy3ed852e2009-09-05 21:47:34 +00005520 if (memcmp(type,mng_ENDL,4) == 0)
5521 {
5522 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005523
cristy3ed852e2009-09-05 21:47:34 +00005524 if (skipping_loop > 0)
5525 {
5526 if (skipping_loop == loop_level)
5527 {
5528 /*
5529 Found end of zero-iteration loop.
5530 */
5531 skipping_loop=(-1);
5532 mng_info->loop_active[loop_level]=0;
5533 }
5534 }
glennrp47b9dd52010-11-24 18:12:06 +00005535
cristy3ed852e2009-09-05 21:47:34 +00005536 else
5537 {
5538 if (mng_info->loop_active[loop_level] == 1)
5539 {
5540 mng_info->loop_count[loop_level]--;
5541 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005542
cristy3ed852e2009-09-05 21:47:34 +00005543 if (logging != MagickFalse)
5544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005545 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005546 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005547 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005548
cristy3ed852e2009-09-05 21:47:34 +00005549 if (mng_info->loop_count[loop_level] != 0)
5550 {
5551 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5552 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005553
cristy3ed852e2009-09-05 21:47:34 +00005554 if (offset < 0)
5555 ThrowReaderException(CorruptImageError,
5556 "ImproperImageHeader");
5557 }
glennrp47b9dd52010-11-24 18:12:06 +00005558
cristy3ed852e2009-09-05 21:47:34 +00005559 else
5560 {
5561 short
5562 last_level;
5563
5564 /*
5565 Finished loop.
5566 */
5567 mng_info->loop_active[loop_level]=0;
5568 last_level=(-1);
5569 for (i=0; i < loop_level; i++)
5570 if (mng_info->loop_active[i] == 1)
5571 last_level=(short) i;
5572 loop_level=last_level;
5573 }
5574 }
5575 }
glennrp47b9dd52010-11-24 18:12:06 +00005576
cristy3ed852e2009-09-05 21:47:34 +00005577 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5578 continue;
5579 }
glennrp47b9dd52010-11-24 18:12:06 +00005580
cristy3ed852e2009-09-05 21:47:34 +00005581 if (memcmp(type,mng_CLON,4) == 0)
5582 {
5583 if (mng_info->clon_warning == 0)
5584 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5585 CoderError,"CLON is not implemented yet","`%s'",
5586 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005587
cristy3ed852e2009-09-05 21:47:34 +00005588 mng_info->clon_warning++;
5589 }
glennrp47b9dd52010-11-24 18:12:06 +00005590
cristy3ed852e2009-09-05 21:47:34 +00005591 if (memcmp(type,mng_MAGN,4) == 0)
5592 {
5593 png_uint_16
5594 magn_first,
5595 magn_last,
5596 magn_mb,
5597 magn_ml,
5598 magn_mr,
5599 magn_mt,
5600 magn_mx,
5601 magn_my,
5602 magn_methx,
5603 magn_methy;
5604
5605 if (length > 1)
5606 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005607
cristy3ed852e2009-09-05 21:47:34 +00005608 else
5609 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005610
cristy3ed852e2009-09-05 21:47:34 +00005611 if (length > 3)
5612 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005613
cristy3ed852e2009-09-05 21:47:34 +00005614 else
5615 magn_last=magn_first;
5616#ifndef MNG_OBJECT_BUFFERS
5617 if (magn_first || magn_last)
5618 if (mng_info->magn_warning == 0)
5619 {
5620 (void) ThrowMagickException(&image->exception,
5621 GetMagickModule(),CoderError,
5622 "MAGN is not implemented yet for nonzero objects",
5623 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005624
cristy3ed852e2009-09-05 21:47:34 +00005625 mng_info->magn_warning++;
5626 }
5627#endif
5628 if (length > 4)
5629 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005630
cristy3ed852e2009-09-05 21:47:34 +00005631 else
5632 magn_methx=0;
5633
5634 if (length > 6)
5635 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005636
cristy3ed852e2009-09-05 21:47:34 +00005637 else
5638 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005639
cristy3ed852e2009-09-05 21:47:34 +00005640 if (magn_mx == 0)
5641 magn_mx=1;
5642
5643 if (length > 8)
5644 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005645
cristy3ed852e2009-09-05 21:47:34 +00005646 else
5647 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005648
cristy3ed852e2009-09-05 21:47:34 +00005649 if (magn_my == 0)
5650 magn_my=1;
5651
5652 if (length > 10)
5653 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005654
cristy3ed852e2009-09-05 21:47:34 +00005655 else
5656 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005657
cristy3ed852e2009-09-05 21:47:34 +00005658 if (magn_ml == 0)
5659 magn_ml=1;
5660
5661 if (length > 12)
5662 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005663
cristy3ed852e2009-09-05 21:47:34 +00005664 else
5665 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005666
cristy3ed852e2009-09-05 21:47:34 +00005667 if (magn_mr == 0)
5668 magn_mr=1;
5669
5670 if (length > 14)
5671 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005672
cristy3ed852e2009-09-05 21:47:34 +00005673 else
5674 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005675
cristy3ed852e2009-09-05 21:47:34 +00005676 if (magn_mt == 0)
5677 magn_mt=1;
5678
5679 if (length > 16)
5680 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005681
cristy3ed852e2009-09-05 21:47:34 +00005682 else
5683 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005684
cristy3ed852e2009-09-05 21:47:34 +00005685 if (magn_mb == 0)
5686 magn_mb=1;
5687
5688 if (length > 17)
5689 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005690
cristy3ed852e2009-09-05 21:47:34 +00005691 else
5692 magn_methy=magn_methx;
5693
glennrp47b9dd52010-11-24 18:12:06 +00005694
cristy3ed852e2009-09-05 21:47:34 +00005695 if (magn_methx > 5 || magn_methy > 5)
5696 if (mng_info->magn_warning == 0)
5697 {
5698 (void) ThrowMagickException(&image->exception,
5699 GetMagickModule(),CoderError,
5700 "Unknown MAGN method in MNG datastream","`%s'",
5701 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005702
cristy3ed852e2009-09-05 21:47:34 +00005703 mng_info->magn_warning++;
5704 }
5705#ifdef MNG_OBJECT_BUFFERS
5706 /* Magnify existing objects in the range magn_first to magn_last */
5707#endif
5708 if (magn_first == 0 || magn_last == 0)
5709 {
5710 /* Save the magnification factors for object 0 */
5711 mng_info->magn_mb=magn_mb;
5712 mng_info->magn_ml=magn_ml;
5713 mng_info->magn_mr=magn_mr;
5714 mng_info->magn_mt=magn_mt;
5715 mng_info->magn_mx=magn_mx;
5716 mng_info->magn_my=magn_my;
5717 mng_info->magn_methx=magn_methx;
5718 mng_info->magn_methy=magn_methy;
5719 }
5720 }
glennrp47b9dd52010-11-24 18:12:06 +00005721
cristy3ed852e2009-09-05 21:47:34 +00005722 if (memcmp(type,mng_PAST,4) == 0)
5723 {
5724 if (mng_info->past_warning == 0)
5725 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5726 CoderError,"PAST is not implemented yet","`%s'",
5727 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005728
cristy3ed852e2009-09-05 21:47:34 +00005729 mng_info->past_warning++;
5730 }
glennrp47b9dd52010-11-24 18:12:06 +00005731
cristy3ed852e2009-09-05 21:47:34 +00005732 if (memcmp(type,mng_SHOW,4) == 0)
5733 {
5734 if (mng_info->show_warning == 0)
5735 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5736 CoderError,"SHOW is not implemented yet","`%s'",
5737 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005738
cristy3ed852e2009-09-05 21:47:34 +00005739 mng_info->show_warning++;
5740 }
glennrp47b9dd52010-11-24 18:12:06 +00005741
cristy3ed852e2009-09-05 21:47:34 +00005742 if (memcmp(type,mng_sBIT,4) == 0)
5743 {
5744 if (length < 4)
5745 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005746
cristy3ed852e2009-09-05 21:47:34 +00005747 else
5748 {
5749 mng_info->global_sbit.gray=p[0];
5750 mng_info->global_sbit.red=p[0];
5751 mng_info->global_sbit.green=p[1];
5752 mng_info->global_sbit.blue=p[2];
5753 mng_info->global_sbit.alpha=p[3];
5754 mng_info->have_global_sbit=MagickTrue;
5755 }
5756 }
5757 if (memcmp(type,mng_pHYs,4) == 0)
5758 {
5759 if (length > 8)
5760 {
5761 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005762 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005763 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005764 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005765 mng_info->global_phys_unit_type=p[8];
5766 mng_info->have_global_phys=MagickTrue;
5767 }
glennrp47b9dd52010-11-24 18:12:06 +00005768
cristy3ed852e2009-09-05 21:47:34 +00005769 else
5770 mng_info->have_global_phys=MagickFalse;
5771 }
5772 if (memcmp(type,mng_pHYg,4) == 0)
5773 {
5774 if (mng_info->phyg_warning == 0)
5775 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5776 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005777
cristy3ed852e2009-09-05 21:47:34 +00005778 mng_info->phyg_warning++;
5779 }
5780 if (memcmp(type,mng_BASI,4) == 0)
5781 {
5782 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005783
cristy3ed852e2009-09-05 21:47:34 +00005784 if (mng_info->basi_warning == 0)
5785 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5786 CoderError,"BASI is not implemented yet","`%s'",
5787 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005788
cristy3ed852e2009-09-05 21:47:34 +00005789 mng_info->basi_warning++;
5790#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005791 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005792 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005793 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005794 (p[6] << 8) | p[7]);
5795 basi_color_type=p[8];
5796 basi_compression_method=p[9];
5797 basi_filter_type=p[10];
5798 basi_interlace_method=p[11];
5799 if (length > 11)
5800 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005801
cristy3ed852e2009-09-05 21:47:34 +00005802 else
5803 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005804
cristy3ed852e2009-09-05 21:47:34 +00005805 if (length > 13)
5806 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005807
cristy3ed852e2009-09-05 21:47:34 +00005808 else
5809 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005810
cristy3ed852e2009-09-05 21:47:34 +00005811 if (length > 15)
5812 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005813
cristy3ed852e2009-09-05 21:47:34 +00005814 else
5815 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005816
cristy3ed852e2009-09-05 21:47:34 +00005817 if (length > 17)
5818 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005819
cristy3ed852e2009-09-05 21:47:34 +00005820 else
5821 {
5822 if (basi_sample_depth == 16)
5823 basi_alpha=65535L;
5824 else
5825 basi_alpha=255;
5826 }
glennrp47b9dd52010-11-24 18:12:06 +00005827
cristy3ed852e2009-09-05 21:47:34 +00005828 if (length > 19)
5829 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005830
cristy3ed852e2009-09-05 21:47:34 +00005831 else
5832 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005833
cristy3ed852e2009-09-05 21:47:34 +00005834#endif
5835 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5836 continue;
5837 }
glennrp47b9dd52010-11-24 18:12:06 +00005838
cristy3ed852e2009-09-05 21:47:34 +00005839 if (memcmp(type,mng_IHDR,4)
5840#if defined(JNG_SUPPORTED)
5841 && memcmp(type,mng_JHDR,4)
5842#endif
5843 )
5844 {
5845 /* Not an IHDR or JHDR chunk */
5846 if (length)
5847 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005848
cristy3ed852e2009-09-05 21:47:34 +00005849 continue;
5850 }
5851/* Process IHDR */
5852 if (logging != MagickFalse)
5853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5854 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005855
cristy3ed852e2009-09-05 21:47:34 +00005856 mng_info->exists[object_id]=MagickTrue;
5857 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005858
cristy3ed852e2009-09-05 21:47:34 +00005859 if (mng_info->invisible[object_id])
5860 {
5861 if (logging != MagickFalse)
5862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5863 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005864
cristy3ed852e2009-09-05 21:47:34 +00005865 skip_to_iend=MagickTrue;
5866 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5867 continue;
5868 }
5869#if defined(MNG_INSERT_LAYERS)
5870 if (length < 8)
5871 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005872
cristy8182b072010-05-30 20:10:53 +00005873 image_width=(size_t) mng_get_long(p);
5874 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005875#endif
5876 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5877
5878 /*
5879 Insert a transparent background layer behind the entire animation
5880 if it is not full screen.
5881 */
5882#if defined(MNG_INSERT_LAYERS)
5883 if (insert_layers && mng_type && first_mng_object)
5884 {
5885 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5886 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005887 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005888 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005889 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005890 {
cristy4c08aed2011-07-01 19:47:50 +00005891 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005892 {
5893 /*
5894 Allocate next image structure.
5895 */
cristy9950d572011-10-01 18:22:35 +00005896 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005897
cristy3ed852e2009-09-05 21:47:34 +00005898 if (GetNextImageInList(image) == (Image *) NULL)
5899 {
5900 image=DestroyImageList(image);
5901 MngInfoFreeStruct(mng_info,&have_mng_structure);
5902 return((Image *) NULL);
5903 }
glennrp47b9dd52010-11-24 18:12:06 +00005904
cristy3ed852e2009-09-05 21:47:34 +00005905 image=SyncNextImageInList(image);
5906 }
5907 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005908
cristy3ed852e2009-09-05 21:47:34 +00005909 if (term_chunk_found)
5910 {
5911 image->start_loop=MagickTrue;
5912 image->iterations=mng_iterations;
5913 term_chunk_found=MagickFalse;
5914 }
glennrp47b9dd52010-11-24 18:12:06 +00005915
cristy3ed852e2009-09-05 21:47:34 +00005916 else
5917 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005918
5919 /* Make a background rectangle. */
5920
cristy3ed852e2009-09-05 21:47:34 +00005921 image->delay=0;
5922 image->columns=mng_info->mng_width;
5923 image->rows=mng_info->mng_height;
5924 image->page.width=mng_info->mng_width;
5925 image->page.height=mng_info->mng_height;
5926 image->page.x=0;
5927 image->page.y=0;
5928 image->background_color=mng_background_color;
5929 (void) SetImageBackgroundColor(image);
5930 if (logging != MagickFalse)
5931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005932 " Inserted transparent background layer, W=%.20g, H=%.20g",
5933 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005934 }
5935 }
5936 /*
5937 Insert a background layer behind the upcoming image if
5938 framing_mode is 3, and we haven't already inserted one.
5939 */
5940 if (insert_layers && (mng_info->framing_mode == 3) &&
5941 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5942 (simplicity & 0x08)))
5943 {
cristy4c08aed2011-07-01 19:47:50 +00005944 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005945 {
5946 /*
5947 Allocate next image structure.
5948 */
cristy9950d572011-10-01 18:22:35 +00005949 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005950
cristy3ed852e2009-09-05 21:47:34 +00005951 if (GetNextImageInList(image) == (Image *) NULL)
5952 {
5953 image=DestroyImageList(image);
5954 MngInfoFreeStruct(mng_info,&have_mng_structure);
5955 return((Image *) NULL);
5956 }
glennrp47b9dd52010-11-24 18:12:06 +00005957
cristy3ed852e2009-09-05 21:47:34 +00005958 image=SyncNextImageInList(image);
5959 }
glennrp0fe50b42010-11-16 03:52:51 +00005960
cristy3ed852e2009-09-05 21:47:34 +00005961 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005962
cristy3ed852e2009-09-05 21:47:34 +00005963 if (term_chunk_found)
5964 {
5965 image->start_loop=MagickTrue;
5966 image->iterations=mng_iterations;
5967 term_chunk_found=MagickFalse;
5968 }
glennrp0fe50b42010-11-16 03:52:51 +00005969
cristy3ed852e2009-09-05 21:47:34 +00005970 else
5971 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005972
cristy3ed852e2009-09-05 21:47:34 +00005973 image->delay=0;
5974 image->columns=subframe_width;
5975 image->rows=subframe_height;
5976 image->page.width=subframe_width;
5977 image->page.height=subframe_height;
5978 image->page.x=mng_info->clip.left;
5979 image->page.y=mng_info->clip.top;
5980 image->background_color=mng_background_color;
5981 image->matte=MagickFalse;
5982 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005983
cristy3ed852e2009-09-05 21:47:34 +00005984 if (logging != MagickFalse)
5985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005986 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005987 (double) mng_info->clip.left,(double) mng_info->clip.right,
5988 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005989 }
5990#endif /* MNG_INSERT_LAYERS */
5991 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005992
cristy4c08aed2011-07-01 19:47:50 +00005993 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005994 {
5995 /*
5996 Allocate next image structure.
5997 */
cristy9950d572011-10-01 18:22:35 +00005998 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005999
cristy3ed852e2009-09-05 21:47:34 +00006000 if (GetNextImageInList(image) == (Image *) NULL)
6001 {
6002 image=DestroyImageList(image);
6003 MngInfoFreeStruct(mng_info,&have_mng_structure);
6004 return((Image *) NULL);
6005 }
glennrp47b9dd52010-11-24 18:12:06 +00006006
cristy3ed852e2009-09-05 21:47:34 +00006007 image=SyncNextImageInList(image);
6008 }
6009 mng_info->image=image;
6010 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6011 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006012
cristy3ed852e2009-09-05 21:47:34 +00006013 if (status == MagickFalse)
6014 break;
glennrp0fe50b42010-11-16 03:52:51 +00006015
cristy3ed852e2009-09-05 21:47:34 +00006016 if (term_chunk_found)
6017 {
6018 image->start_loop=MagickTrue;
6019 term_chunk_found=MagickFalse;
6020 }
glennrp0fe50b42010-11-16 03:52:51 +00006021
cristy3ed852e2009-09-05 21:47:34 +00006022 else
6023 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006024
cristy3ed852e2009-09-05 21:47:34 +00006025 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6026 {
6027 image->delay=frame_delay;
6028 frame_delay=default_frame_delay;
6029 }
glennrp0fe50b42010-11-16 03:52:51 +00006030
cristy3ed852e2009-09-05 21:47:34 +00006031 else
6032 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006033
cristy3ed852e2009-09-05 21:47:34 +00006034 image->page.width=mng_info->mng_width;
6035 image->page.height=mng_info->mng_height;
6036 image->page.x=mng_info->x_off[object_id];
6037 image->page.y=mng_info->y_off[object_id];
6038 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006039
cristy3ed852e2009-09-05 21:47:34 +00006040 /*
6041 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6042 */
glennrp47b9dd52010-11-24 18:12:06 +00006043
cristy3ed852e2009-09-05 21:47:34 +00006044 if (logging != MagickFalse)
6045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6046 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6047 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006048
cristybb503372010-05-27 20:51:26 +00006049 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006050
cristy3ed852e2009-09-05 21:47:34 +00006051 if (offset < 0)
6052 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6053 }
6054
6055 previous=image;
6056 mng_info->image=image;
6057 mng_info->mng_type=mng_type;
6058 mng_info->object_id=object_id;
6059
6060 if (memcmp(type,mng_IHDR,4) == 0)
6061 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006062
cristy3ed852e2009-09-05 21:47:34 +00006063#if defined(JNG_SUPPORTED)
6064 else
6065 image=ReadOneJNGImage(mng_info,image_info,exception);
6066#endif
6067
6068 if (image == (Image *) NULL)
6069 {
6070 if (IsImageObject(previous) != MagickFalse)
6071 {
6072 (void) DestroyImageList(previous);
6073 (void) CloseBlob(previous);
6074 }
glennrp47b9dd52010-11-24 18:12:06 +00006075
cristy3ed852e2009-09-05 21:47:34 +00006076 MngInfoFreeStruct(mng_info,&have_mng_structure);
6077 return((Image *) NULL);
6078 }
glennrp0fe50b42010-11-16 03:52:51 +00006079
cristy3ed852e2009-09-05 21:47:34 +00006080 if (image->columns == 0 || image->rows == 0)
6081 {
6082 (void) CloseBlob(image);
6083 image=DestroyImageList(image);
6084 MngInfoFreeStruct(mng_info,&have_mng_structure);
6085 return((Image *) NULL);
6086 }
glennrp0fe50b42010-11-16 03:52:51 +00006087
cristy3ed852e2009-09-05 21:47:34 +00006088 mng_info->image=image;
6089
6090 if (mng_type)
6091 {
6092 MngBox
6093 crop_box;
6094
6095 if (mng_info->magn_methx || mng_info->magn_methy)
6096 {
6097 png_uint_32
6098 magnified_height,
6099 magnified_width;
6100
6101 if (logging != MagickFalse)
6102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6103 " Processing MNG MAGN chunk");
6104
6105 if (mng_info->magn_methx == 1)
6106 {
6107 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006108
cristy3ed852e2009-09-05 21:47:34 +00006109 if (image->columns > 1)
6110 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006111
cristy3ed852e2009-09-05 21:47:34 +00006112 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006113 magnified_width += (png_uint_32)
6114 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006115 }
glennrp47b9dd52010-11-24 18:12:06 +00006116
cristy3ed852e2009-09-05 21:47:34 +00006117 else
6118 {
cristy4e5bc842010-06-09 13:56:01 +00006119 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006120
cristy3ed852e2009-09-05 21:47:34 +00006121 if (image->columns > 1)
6122 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006123
cristy3ed852e2009-09-05 21:47:34 +00006124 if (image->columns > 2)
6125 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006126
cristy3ed852e2009-09-05 21:47:34 +00006127 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006128 magnified_width += (png_uint_32)
6129 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006130 }
glennrp47b9dd52010-11-24 18:12:06 +00006131
cristy3ed852e2009-09-05 21:47:34 +00006132 if (mng_info->magn_methy == 1)
6133 {
6134 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006135
cristy3ed852e2009-09-05 21:47:34 +00006136 if (image->rows > 1)
6137 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006138
cristy3ed852e2009-09-05 21:47:34 +00006139 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006140 magnified_height += (png_uint_32)
6141 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006142 }
glennrp47b9dd52010-11-24 18:12:06 +00006143
cristy3ed852e2009-09-05 21:47:34 +00006144 else
6145 {
cristy4e5bc842010-06-09 13:56:01 +00006146 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006147
cristy3ed852e2009-09-05 21:47:34 +00006148 if (image->rows > 1)
6149 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006150
cristy3ed852e2009-09-05 21:47:34 +00006151 if (image->rows > 2)
6152 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006153
cristy3ed852e2009-09-05 21:47:34 +00006154 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006155 magnified_height += (png_uint_32)
6156 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006157 }
glennrp47b9dd52010-11-24 18:12:06 +00006158
cristy3ed852e2009-09-05 21:47:34 +00006159 if (magnified_height > image->rows ||
6160 magnified_width > image->columns)
6161 {
6162 Image
6163 *large_image;
6164
6165 int
6166 yy;
6167
cristy4c08aed2011-07-01 19:47:50 +00006168 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006169 *next,
6170 *prev;
6171
6172 png_uint_16
6173 magn_methx,
6174 magn_methy;
6175
cristy4c08aed2011-07-01 19:47:50 +00006176 ssize_t
6177 m,
6178 y;
6179
6180 register Quantum
6181 *n,
6182 *q;
6183
6184 register ssize_t
6185 x;
6186
glennrp47b9dd52010-11-24 18:12:06 +00006187 /* Allocate next image structure. */
6188
cristy3ed852e2009-09-05 21:47:34 +00006189 if (logging != MagickFalse)
6190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6191 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006192
cristy9950d572011-10-01 18:22:35 +00006193 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006194
cristy3ed852e2009-09-05 21:47:34 +00006195 if (GetNextImageInList(image) == (Image *) NULL)
6196 {
6197 image=DestroyImageList(image);
6198 MngInfoFreeStruct(mng_info,&have_mng_structure);
6199 return((Image *) NULL);
6200 }
6201
6202 large_image=SyncNextImageInList(image);
6203
6204 large_image->columns=magnified_width;
6205 large_image->rows=magnified_height;
6206
6207 magn_methx=mng_info->magn_methx;
6208 magn_methy=mng_info->magn_methy;
6209
glennrp3faa9a32011-04-23 14:00:25 +00006210#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006211#define QM unsigned short
6212 if (magn_methx != 1 || magn_methy != 1)
6213 {
6214 /*
6215 Scale pixels to unsigned shorts to prevent
6216 overflow of intermediate values of interpolations
6217 */
cristybb503372010-05-27 20:51:26 +00006218 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006219 {
6220 q=GetAuthenticPixels(image,0,y,image->columns,1,
6221 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006222
cristybb503372010-05-27 20:51:26 +00006223 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006224 {
cristy4c08aed2011-07-01 19:47:50 +00006225 SetPixelRed(image,ScaleQuantumToShort(
6226 GetPixelRed(image,q)),q);
6227 SetPixelGreen(image,ScaleQuantumToShort(
6228 GetPixelGreen(image,q)),q);
6229 SetPixelBlue(image,ScaleQuantumToShort(
6230 GetPixelBlue(image,q)),q);
6231 SetPixelAlpha(image,ScaleQuantumToShort(
6232 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006233 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006234 }
glennrp47b9dd52010-11-24 18:12:06 +00006235
cristy3ed852e2009-09-05 21:47:34 +00006236 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6237 break;
6238 }
6239 }
6240#else
6241#define QM Quantum
6242#endif
6243
6244 if (image->matte != MagickFalse)
6245 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006246
cristy3ed852e2009-09-05 21:47:34 +00006247 else
6248 {
cristy4c08aed2011-07-01 19:47:50 +00006249 large_image->background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00006250 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006251
cristy3ed852e2009-09-05 21:47:34 +00006252 if (magn_methx == 4)
6253 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006254
cristy3ed852e2009-09-05 21:47:34 +00006255 if (magn_methx == 5)
6256 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006257
cristy3ed852e2009-09-05 21:47:34 +00006258 if (magn_methy == 4)
6259 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006260
cristy3ed852e2009-09-05 21:47:34 +00006261 if (magn_methy == 5)
6262 magn_methy=3;
6263 }
6264
6265 /* magnify the rows into the right side of the large image */
6266
6267 if (logging != MagickFalse)
6268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006269 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006270 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006271 yy=0;
6272 length=(size_t) image->columns;
cristy4c08aed2011-07-01 19:47:50 +00006273 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6274 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006275
cristy4c08aed2011-07-01 19:47:50 +00006276 if ((prev == (Quantum *) NULL) ||
6277 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006278 {
6279 image=DestroyImageList(image);
6280 MngInfoFreeStruct(mng_info,&have_mng_structure);
6281 ThrowReaderException(ResourceLimitError,
6282 "MemoryAllocationFailed");
6283 }
glennrp47b9dd52010-11-24 18:12:06 +00006284
cristy3ed852e2009-09-05 21:47:34 +00006285 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6286 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006287
cristybb503372010-05-27 20:51:26 +00006288 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006289 {
6290 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006291 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006292
cristybb503372010-05-27 20:51:26 +00006293 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6294 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006295
cristybb503372010-05-27 20:51:26 +00006296 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6297 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006298
cristybb503372010-05-27 20:51:26 +00006299 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006300 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006301
cristy3ed852e2009-09-05 21:47:34 +00006302 else
cristybb503372010-05-27 20:51:26 +00006303 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006304
cristy3ed852e2009-09-05 21:47:34 +00006305 n=prev;
6306 prev=next;
6307 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006308
cristybb503372010-05-27 20:51:26 +00006309 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006310 {
6311 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6312 exception);
6313 (void) CopyMagickMemory(next,n,length);
6314 }
glennrp47b9dd52010-11-24 18:12:06 +00006315
cristy3ed852e2009-09-05 21:47:34 +00006316 for (i=0; i < m; i++, yy++)
6317 {
cristy4c08aed2011-07-01 19:47:50 +00006318 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006319 *pixels;
6320
cristybb503372010-05-27 20:51:26 +00006321 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006322 pixels=prev;
6323 n=next;
6324 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006325 1,exception);
cristy3ed852e2009-09-05 21:47:34 +00006326 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00006327
cristybb503372010-05-27 20:51:26 +00006328 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006329 {
glennrpfd05d622011-02-25 04:10:33 +00006330 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006331 /*
6332 if (image->storage_class == PseudoClass)
6333 {
6334 }
6335 */
6336
6337 if (magn_methy <= 1)
6338 {
glennrpbb4f99d2011-05-22 11:13:17 +00006339 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006340 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
glennrp847370c2011-07-05 17:37:15 +00006341 SetPixelGreen(large_image,GetPixelGreen(image,
6342 pixels),q);
6343 SetPixelBlue(large_image,GetPixelBlue(image,
6344 pixels),q);
6345 SetPixelAlpha(large_image,GetPixelAlpha(image,
6346 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006347 }
glennrp47b9dd52010-11-24 18:12:06 +00006348
cristy3ed852e2009-09-05 21:47:34 +00006349 else if (magn_methy == 2 || magn_methy == 4)
6350 {
6351 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006352 {
glennrp847370c2011-07-05 17:37:15 +00006353 SetPixelRed(large_image,GetPixelRed(image,
6354 pixels),q);
6355 SetPixelGreen(large_image,GetPixelGreen(image,
6356 pixels),q);
6357 SetPixelBlue(large_image,GetPixelBlue(image,
6358 pixels),q);
6359 SetPixelAlpha(large_image,GetPixelAlpha(image,
6360 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006361 }
glennrp47b9dd52010-11-24 18:12:06 +00006362
cristy3ed852e2009-09-05 21:47:34 +00006363 else
6364 {
6365 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006366 SetPixelRed(large_image,((QM) (((ssize_t)
6367 (2*i*(GetPixelRed(image,n)
6368 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006369 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006370 +GetPixelRed(image,pixels)))),q);
6371 SetPixelGreen(large_image,((QM) (((ssize_t)
6372 (2*i*(GetPixelGreen(image,n)
6373 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006374 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006375 +GetPixelGreen(image,pixels)))),q);
6376 SetPixelBlue(large_image,((QM) (((ssize_t)
6377 (2*i*(GetPixelBlue(image,n)
6378 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006379 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006380 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006381
cristy3ed852e2009-09-05 21:47:34 +00006382 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006383 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6384 (2*i*(GetPixelAlpha(image,n)
6385 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006386 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006387 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006388 }
glennrp47b9dd52010-11-24 18:12:06 +00006389
cristy3ed852e2009-09-05 21:47:34 +00006390 if (magn_methy == 4)
6391 {
6392 /* Replicate nearest */
6393 if (i <= ((m+1) << 1))
glennrp847370c2011-07-05 17:37:15 +00006394 SetPixelAlpha(large_image,GetPixelAlpha(image,
6395 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006396 else
glennrp847370c2011-07-05 17:37:15 +00006397 SetPixelAlpha(large_image,GetPixelAlpha(image,
6398 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006399 }
6400 }
glennrp47b9dd52010-11-24 18:12:06 +00006401
cristy3ed852e2009-09-05 21:47:34 +00006402 else /* if (magn_methy == 3 || magn_methy == 5) */
6403 {
6404 /* Replicate nearest */
6405 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006406 {
glennrp847370c2011-07-05 17:37:15 +00006407 SetPixelRed(large_image,GetPixelRed(image,
6408 pixels),q);
6409 SetPixelGreen(large_image,GetPixelGreen(image,
6410 pixels),q);
6411 SetPixelBlue(large_image,GetPixelBlue(image,
6412 pixels),q);
6413 SetPixelAlpha(large_image,GetPixelAlpha(image,
6414 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006415 }
glennrp47b9dd52010-11-24 18:12:06 +00006416
cristy3ed852e2009-09-05 21:47:34 +00006417 else
glennrpbb4f99d2011-05-22 11:13:17 +00006418 {
cristy4c08aed2011-07-01 19:47:50 +00006419 SetPixelRed(large_image,GetPixelRed(image,n),q);
glennrp847370c2011-07-05 17:37:15 +00006420 SetPixelGreen(large_image,GetPixelGreen(image,n),
6421 q);
6422 SetPixelBlue(large_image,GetPixelBlue(image,n),
6423 q);
6424 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6425 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006426 }
glennrp47b9dd52010-11-24 18:12:06 +00006427
cristy3ed852e2009-09-05 21:47:34 +00006428 if (magn_methy == 5)
6429 {
cristy4c08aed2011-07-01 19:47:50 +00006430 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6431 (GetPixelAlpha(image,n)
6432 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006433 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006434 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006435 }
6436 }
cristyed231572011-07-14 02:18:59 +00006437 n+=GetPixelChannels(image);
6438 q+=GetPixelChannels(large_image);
6439 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006440 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006441
cristy3ed852e2009-09-05 21:47:34 +00006442 if (SyncAuthenticPixels(large_image,exception) == 0)
6443 break;
glennrp47b9dd52010-11-24 18:12:06 +00006444
cristy3ed852e2009-09-05 21:47:34 +00006445 } /* i */
6446 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006447
cristy4c08aed2011-07-01 19:47:50 +00006448 prev=(Quantum *) RelinquishMagickMemory(prev);
6449 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006450
6451 length=image->columns;
6452
6453 if (logging != MagickFalse)
6454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6455 " Delete original image");
6456
6457 DeleteImageFromList(&image);
6458
6459 image=large_image;
6460
6461 mng_info->image=image;
6462
6463 /* magnify the columns */
6464 if (logging != MagickFalse)
6465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006466 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006467
cristybb503372010-05-27 20:51:26 +00006468 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006469 {
cristy4c08aed2011-07-01 19:47:50 +00006470 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006471 *pixels;
6472
6473 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyed231572011-07-14 02:18:59 +00006474 pixels=q+(image->columns-length)*GetPixelChannels(image);
6475 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006476
cristybb503372010-05-27 20:51:26 +00006477 for (x=(ssize_t) (image->columns-length);
6478 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006479 {
cristyed231572011-07-14 02:18:59 +00006480 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006481
cristybb503372010-05-27 20:51:26 +00006482 if (x == (ssize_t) (image->columns-length))
6483 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006484
cristybb503372010-05-27 20:51:26 +00006485 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6486 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006487
cristybb503372010-05-27 20:51:26 +00006488 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6489 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006490
cristybb503372010-05-27 20:51:26 +00006491 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006492 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006493
cristy3ed852e2009-09-05 21:47:34 +00006494 else
cristybb503372010-05-27 20:51:26 +00006495 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006496
cristy3ed852e2009-09-05 21:47:34 +00006497 for (i=0; i < m; i++)
6498 {
6499 if (magn_methx <= 1)
6500 {
6501 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006502 SetPixelRed(image,GetPixelRed(image,pixels),q);
6503 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6504 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6505 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006506 }
glennrp47b9dd52010-11-24 18:12:06 +00006507
cristy3ed852e2009-09-05 21:47:34 +00006508 else if (magn_methx == 2 || magn_methx == 4)
6509 {
6510 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006511 {
cristy4c08aed2011-07-01 19:47:50 +00006512 SetPixelRed(image,GetPixelRed(image,pixels),q);
6513 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6514 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6515 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006516 }
glennrp47b9dd52010-11-24 18:12:06 +00006517
cristyed231572011-07-14 02:18:59 +00006518 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006519 else
6520 {
6521 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006522 SetPixelRed(image,(QM) ((2*i*(
6523 GetPixelRed(image,n)
6524 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006525 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006526 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006527
cristy4c08aed2011-07-01 19:47:50 +00006528 SetPixelGreen(image,(QM) ((2*i*(
6529 GetPixelGreen(image,n)
6530 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006531 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006532 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006533
cristy4c08aed2011-07-01 19:47:50 +00006534 SetPixelBlue(image,(QM) ((2*i*(
6535 GetPixelBlue(image,n)
6536 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006537 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006538 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006539 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006540 SetPixelAlpha(image,(QM) ((2*i*(
6541 GetPixelAlpha(image,n)
6542 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006543 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006544 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006545 }
glennrp47b9dd52010-11-24 18:12:06 +00006546
cristy3ed852e2009-09-05 21:47:34 +00006547 if (magn_methx == 4)
6548 {
6549 /* Replicate nearest */
6550 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006551 {
cristy4c08aed2011-07-01 19:47:50 +00006552 SetPixelAlpha(image,
6553 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006554 }
cristy3ed852e2009-09-05 21:47:34 +00006555 else
glennrpbb4f99d2011-05-22 11:13:17 +00006556 {
cristy4c08aed2011-07-01 19:47:50 +00006557 SetPixelAlpha(image,
6558 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006559 }
cristy3ed852e2009-09-05 21:47:34 +00006560 }
6561 }
glennrp47b9dd52010-11-24 18:12:06 +00006562
cristy3ed852e2009-09-05 21:47:34 +00006563 else /* if (magn_methx == 3 || magn_methx == 5) */
6564 {
6565 /* Replicate nearest */
6566 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006567 {
cristy4c08aed2011-07-01 19:47:50 +00006568 SetPixelRed(image,GetPixelRed(image,pixels),q);
6569 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6570 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6571 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006572 }
glennrp47b9dd52010-11-24 18:12:06 +00006573
cristy3ed852e2009-09-05 21:47:34 +00006574 else
glennrpbb4f99d2011-05-22 11:13:17 +00006575 {
cristy4c08aed2011-07-01 19:47:50 +00006576 SetPixelRed(image,GetPixelRed(image,n),q);
6577 SetPixelGreen(image,GetPixelGreen(image,n),q);
6578 SetPixelBlue(image,GetPixelBlue(image,n),q);
6579 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006580 }
glennrp47b9dd52010-11-24 18:12:06 +00006581
cristy3ed852e2009-09-05 21:47:34 +00006582 if (magn_methx == 5)
6583 {
6584 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006585 SetPixelAlpha(image,
6586 (QM) ((2*i*( GetPixelAlpha(image,n)
6587 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006588 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006589 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006590 }
6591 }
cristyed231572011-07-14 02:18:59 +00006592 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006593 }
cristyed231572011-07-14 02:18:59 +00006594 n+=GetPixelChannels(image);
6595 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006596 }
glennrp47b9dd52010-11-24 18:12:06 +00006597
cristy3ed852e2009-09-05 21:47:34 +00006598 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6599 break;
6600 }
glennrp3faa9a32011-04-23 14:00:25 +00006601#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006602 if (magn_methx != 1 || magn_methy != 1)
6603 {
6604 /*
6605 Rescale pixels to Quantum
6606 */
cristybb503372010-05-27 20:51:26 +00006607 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006608 {
6609 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006610
cristybb503372010-05-27 20:51:26 +00006611 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006612 {
cristy4c08aed2011-07-01 19:47:50 +00006613 SetPixelRed(image,ScaleShortToQuantum(
6614 GetPixelRed(image,q)),q);
6615 SetPixelGreen(image,ScaleShortToQuantum(
6616 GetPixelGreen(image,q)),q);
6617 SetPixelBlue(image,ScaleShortToQuantum(
6618 GetPixelBlue(image,q)),q);
6619 SetPixelAlpha(image,ScaleShortToQuantum(
6620 GetPixelAlpha(image,q)),q);
cristyed231572011-07-14 02:18:59 +00006621 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006622 }
glennrp47b9dd52010-11-24 18:12:06 +00006623
cristy3ed852e2009-09-05 21:47:34 +00006624 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6625 break;
6626 }
6627 }
6628#endif
6629 if (logging != MagickFalse)
6630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6631 " Finished MAGN processing");
6632 }
6633 }
6634
6635 /*
6636 Crop_box is with respect to the upper left corner of the MNG.
6637 */
6638 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6639 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6640 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6641 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6642 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6643 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6644 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6645 if ((crop_box.left != (mng_info->image_box.left
6646 +mng_info->x_off[object_id])) ||
6647 (crop_box.right != (mng_info->image_box.right
6648 +mng_info->x_off[object_id])) ||
6649 (crop_box.top != (mng_info->image_box.top
6650 +mng_info->y_off[object_id])) ||
6651 (crop_box.bottom != (mng_info->image_box.bottom
6652 +mng_info->y_off[object_id])))
6653 {
6654 if (logging != MagickFalse)
6655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6656 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006657
cristy3ed852e2009-09-05 21:47:34 +00006658 if ((crop_box.left < crop_box.right) &&
6659 (crop_box.top < crop_box.bottom))
6660 {
6661 Image
6662 *im;
6663
6664 RectangleInfo
6665 crop_info;
6666
6667 /*
6668 Crop_info is with respect to the upper left corner of
6669 the image.
6670 */
6671 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6672 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006673 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6674 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006675 image->page.width=image->columns;
6676 image->page.height=image->rows;
6677 image->page.x=0;
6678 image->page.y=0;
6679 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006680
cristy3ed852e2009-09-05 21:47:34 +00006681 if (im != (Image *) NULL)
6682 {
6683 image->columns=im->columns;
6684 image->rows=im->rows;
6685 im=DestroyImage(im);
6686 image->page.width=image->columns;
6687 image->page.height=image->rows;
6688 image->page.x=crop_box.left;
6689 image->page.y=crop_box.top;
6690 }
6691 }
glennrp47b9dd52010-11-24 18:12:06 +00006692
cristy3ed852e2009-09-05 21:47:34 +00006693 else
6694 {
6695 /*
6696 No pixels in crop area. The MNG spec still requires
6697 a layer, though, so make a single transparent pixel in
6698 the top left corner.
6699 */
6700 image->columns=1;
6701 image->rows=1;
6702 image->colors=2;
6703 (void) SetImageBackgroundColor(image);
6704 image->page.width=1;
6705 image->page.height=1;
6706 image->page.x=0;
6707 image->page.y=0;
6708 }
6709 }
6710#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6711 image=mng_info->image;
6712#endif
6713 }
6714
glennrp2b013e42010-11-24 16:55:50 +00006715#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6716 /* PNG does not handle depths greater than 16 so reduce it even
6717 * if lossy
6718 */
6719 if (image->depth > 16)
6720 image->depth=16;
6721#endif
6722
glennrp3faa9a32011-04-23 14:00:25 +00006723#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006724 if (LosslessReduceDepthOK(image) != MagickFalse)
6725 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006726#endif
glennrpd6afd542010-11-19 01:53:05 +00006727
cristy3ed852e2009-09-05 21:47:34 +00006728 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006729
cristy3ed852e2009-09-05 21:47:34 +00006730 if (image_info->number_scenes != 0)
6731 {
6732 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006733 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006734 break;
6735 }
glennrpd6afd542010-11-19 01:53:05 +00006736
cristy3ed852e2009-09-05 21:47:34 +00006737 if (logging != MagickFalse)
6738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6739 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006740
cristy3ed852e2009-09-05 21:47:34 +00006741 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006742
cristy3ed852e2009-09-05 21:47:34 +00006743 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006744
cristy3ed852e2009-09-05 21:47:34 +00006745 if (logging != MagickFalse)
6746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6747 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006748
cristy3ed852e2009-09-05 21:47:34 +00006749#if defined(MNG_INSERT_LAYERS)
6750 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6751 (mng_info->mng_height))
6752 {
6753 /*
6754 Insert a background layer if nothing else was found.
6755 */
6756 if (logging != MagickFalse)
6757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6758 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006759
cristy4c08aed2011-07-01 19:47:50 +00006760 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006761 {
6762 /*
6763 Allocate next image structure.
6764 */
cristy9950d572011-10-01 18:22:35 +00006765 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006766 if (GetNextImageInList(image) == (Image *) NULL)
6767 {
6768 image=DestroyImageList(image);
6769 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006770
cristy3ed852e2009-09-05 21:47:34 +00006771 if (logging != MagickFalse)
6772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6773 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006774
cristy3ed852e2009-09-05 21:47:34 +00006775 return((Image *) NULL);
6776 }
6777 image=SyncNextImageInList(image);
6778 }
6779 image->columns=mng_info->mng_width;
6780 image->rows=mng_info->mng_height;
6781 image->page.width=mng_info->mng_width;
6782 image->page.height=mng_info->mng_height;
6783 image->page.x=0;
6784 image->page.y=0;
6785 image->background_color=mng_background_color;
6786 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006787
cristy3ed852e2009-09-05 21:47:34 +00006788 if (image_info->ping == MagickFalse)
6789 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006790
cristy3ed852e2009-09-05 21:47:34 +00006791 mng_info->image_found++;
6792 }
6793#endif
6794 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006795
cristy3ed852e2009-09-05 21:47:34 +00006796 if (mng_iterations == 1)
6797 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006798
cristy3ed852e2009-09-05 21:47:34 +00006799 while (GetPreviousImageInList(image) != (Image *) NULL)
6800 {
6801 image_count++;
6802 if (image_count > 10*mng_info->image_found)
6803 {
6804 if (logging != MagickFalse)
6805 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006806
cristy3ed852e2009-09-05 21:47:34 +00006807 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6808 CoderError,"Linked list is corrupted, beginning of list not found",
6809 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006810
cristy3ed852e2009-09-05 21:47:34 +00006811 return((Image *) NULL);
6812 }
glennrp0fe50b42010-11-16 03:52:51 +00006813
cristy3ed852e2009-09-05 21:47:34 +00006814 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006815
cristy3ed852e2009-09-05 21:47:34 +00006816 if (GetNextImageInList(image) == (Image *) NULL)
6817 {
6818 if (logging != MagickFalse)
6819 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006820
cristy3ed852e2009-09-05 21:47:34 +00006821 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6822 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6823 image_info->filename);
6824 }
6825 }
glennrp47b9dd52010-11-24 18:12:06 +00006826
cristy3ed852e2009-09-05 21:47:34 +00006827 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6828 GetNextImageInList(image) ==
6829 (Image *) NULL)
6830 {
6831 if (logging != MagickFalse)
6832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6833 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006834
cristy3ed852e2009-09-05 21:47:34 +00006835 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6836 CoderError,"image->next for first image is NULL but shouldn't be.",
6837 "`%s'",image_info->filename);
6838 }
glennrp47b9dd52010-11-24 18:12:06 +00006839
cristy3ed852e2009-09-05 21:47:34 +00006840 if (mng_info->image_found == 0)
6841 {
6842 if (logging != MagickFalse)
6843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6844 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006845
cristy3ed852e2009-09-05 21:47:34 +00006846 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6847 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006848
cristy3ed852e2009-09-05 21:47:34 +00006849 if (image != (Image *) NULL)
6850 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006851
cristy3ed852e2009-09-05 21:47:34 +00006852 MngInfoFreeStruct(mng_info,&have_mng_structure);
6853 return((Image *) NULL);
6854 }
6855
6856 if (mng_info->ticks_per_second)
6857 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6858 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006859
cristy3ed852e2009-09-05 21:47:34 +00006860 else
6861 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006862
cristy3ed852e2009-09-05 21:47:34 +00006863 /* Find final nonzero image delay */
6864 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006865
cristy3ed852e2009-09-05 21:47:34 +00006866 while (GetNextImageInList(image) != (Image *) NULL)
6867 {
6868 if (image->delay)
6869 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006870
cristy3ed852e2009-09-05 21:47:34 +00006871 image=GetNextImageInList(image);
6872 }
glennrp0fe50b42010-11-16 03:52:51 +00006873
cristy3ed852e2009-09-05 21:47:34 +00006874 if (final_delay < final_image_delay)
6875 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006876
cristy3ed852e2009-09-05 21:47:34 +00006877 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006878
cristy3ed852e2009-09-05 21:47:34 +00006879 if (logging != MagickFalse)
6880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006881 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6882 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006883
cristy3ed852e2009-09-05 21:47:34 +00006884 if (logging != MagickFalse)
6885 {
6886 int
6887 scene;
6888
6889 scene=0;
6890 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006891
cristy3ed852e2009-09-05 21:47:34 +00006892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6893 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006894
cristy3ed852e2009-09-05 21:47:34 +00006895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006896 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006897
cristy3ed852e2009-09-05 21:47:34 +00006898 while (GetNextImageInList(image) != (Image *) NULL)
6899 {
6900 image=GetNextImageInList(image);
6901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006902 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006903 }
6904 }
6905
6906 image=GetFirstImageInList(image);
6907#ifdef MNG_COALESCE_LAYERS
6908 if (insert_layers)
6909 {
6910 Image
6911 *next_image,
6912 *next;
6913
cristybb503372010-05-27 20:51:26 +00006914 size_t
cristy3ed852e2009-09-05 21:47:34 +00006915 scene;
6916
6917 if (logging != MagickFalse)
6918 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006919
cristy3ed852e2009-09-05 21:47:34 +00006920 scene=image->scene;
6921 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006922
cristy3ed852e2009-09-05 21:47:34 +00006923 if (next_image == (Image *) NULL)
6924 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006925
cristy3ed852e2009-09-05 21:47:34 +00006926 image=DestroyImageList(image);
6927 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006928
cristy3ed852e2009-09-05 21:47:34 +00006929 for (next=image; next != (Image *) NULL; next=next_image)
6930 {
6931 next->page.width=mng_info->mng_width;
6932 next->page.height=mng_info->mng_height;
6933 next->page.x=0;
6934 next->page.y=0;
6935 next->scene=scene++;
6936 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006937
cristy3ed852e2009-09-05 21:47:34 +00006938 if (next_image == (Image *) NULL)
6939 break;
glennrp47b9dd52010-11-24 18:12:06 +00006940
cristy3ed852e2009-09-05 21:47:34 +00006941 if (next->delay == 0)
6942 {
6943 scene--;
6944 next_image->previous=GetPreviousImageInList(next);
6945 if (GetPreviousImageInList(next) == (Image *) NULL)
6946 image=next_image;
6947 else
6948 next->previous->next=next_image;
6949 next=DestroyImage(next);
6950 }
6951 }
6952 }
6953#endif
6954
6955 while (GetNextImageInList(image) != (Image *) NULL)
6956 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006957
cristy3ed852e2009-09-05 21:47:34 +00006958 image->dispose=BackgroundDispose;
6959
6960 if (logging != MagickFalse)
6961 {
6962 int
6963 scene;
6964
6965 scene=0;
6966 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006967
cristy3ed852e2009-09-05 21:47:34 +00006968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6969 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006970
cristy3ed852e2009-09-05 21:47:34 +00006971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006972 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6973 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006974
cristy3ed852e2009-09-05 21:47:34 +00006975 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006976 {
6977 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006978
cristyf2faecf2010-05-28 19:19:36 +00006979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006980 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6981 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006982 }
6983 }
glennrp47b9dd52010-11-24 18:12:06 +00006984
cristy3ed852e2009-09-05 21:47:34 +00006985 image=GetFirstImageInList(image);
6986 MngInfoFreeStruct(mng_info,&have_mng_structure);
6987 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006988
cristy3ed852e2009-09-05 21:47:34 +00006989 if (logging != MagickFalse)
6990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006991
cristy3ed852e2009-09-05 21:47:34 +00006992 return(GetFirstImageInList(image));
6993}
glennrp25c1e2b2010-03-25 01:39:56 +00006994#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006995static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6996{
6997 printf("Your PNG library is too old: You have libpng-%s\n",
6998 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006999
cristy3ed852e2009-09-05 21:47:34 +00007000 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7001 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007002
cristy3ed852e2009-09-05 21:47:34 +00007003 return(Image *) NULL;
7004}
glennrp47b9dd52010-11-24 18:12:06 +00007005
cristy3ed852e2009-09-05 21:47:34 +00007006static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7007{
7008 return(ReadPNGImage(image_info,exception));
7009}
glennrp25c1e2b2010-03-25 01:39:56 +00007010#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007011#endif
7012
7013/*
7014%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7015% %
7016% %
7017% %
7018% R e g i s t e r P N G I m a g e %
7019% %
7020% %
7021% %
7022%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7023%
7024% RegisterPNGImage() adds properties for the PNG image format to
7025% the list of supported formats. The properties include the image format
7026% tag, a method to read and/or write the format, whether the format
7027% supports the saving of more than one frame to the same file or blob,
7028% whether the format supports native in-memory I/O, and a brief
7029% description of the format.
7030%
7031% The format of the RegisterPNGImage method is:
7032%
cristybb503372010-05-27 20:51:26 +00007033% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007034%
7035*/
cristybb503372010-05-27 20:51:26 +00007036ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007037{
7038 char
7039 version[MaxTextExtent];
7040
7041 MagickInfo
7042 *entry;
7043
7044 static const char
7045 *PNGNote=
7046 {
7047 "See http://www.libpng.org/ for details about the PNG format."
7048 },
glennrp47b9dd52010-11-24 18:12:06 +00007049
cristy3ed852e2009-09-05 21:47:34 +00007050 *JNGNote=
7051 {
7052 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7053 "format."
7054 },
glennrp47b9dd52010-11-24 18:12:06 +00007055
cristy3ed852e2009-09-05 21:47:34 +00007056 *MNGNote=
7057 {
7058 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7059 "format."
7060 };
7061
7062 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007063
cristy3ed852e2009-09-05 21:47:34 +00007064#if defined(PNG_LIBPNG_VER_STRING)
7065 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7066 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007067
cristy3ed852e2009-09-05 21:47:34 +00007068 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7069 {
7070 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7071 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7072 MaxTextExtent);
7073 }
7074#endif
glennrp47b9dd52010-11-24 18:12:06 +00007075
cristy3ed852e2009-09-05 21:47:34 +00007076 entry=SetMagickInfo("MNG");
7077 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007078
cristy3ed852e2009-09-05 21:47:34 +00007079#if defined(MAGICKCORE_PNG_DELEGATE)
7080 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7081 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7082#endif
glennrp47b9dd52010-11-24 18:12:06 +00007083
cristy3ed852e2009-09-05 21:47:34 +00007084 entry->magick=(IsImageFormatHandler *) IsMNG;
7085 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007086
cristy3ed852e2009-09-05 21:47:34 +00007087 if (*version != '\0')
7088 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007089
cristy3ed852e2009-09-05 21:47:34 +00007090 entry->module=ConstantString("PNG");
7091 entry->note=ConstantString(MNGNote);
7092 (void) RegisterMagickInfo(entry);
7093
7094 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007095
cristy3ed852e2009-09-05 21:47:34 +00007096#if defined(MAGICKCORE_PNG_DELEGATE)
7097 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7098 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7099#endif
glennrp47b9dd52010-11-24 18:12:06 +00007100
cristy3ed852e2009-09-05 21:47:34 +00007101 entry->magick=(IsImageFormatHandler *) IsPNG;
7102 entry->adjoin=MagickFalse;
7103 entry->description=ConstantString("Portable Network Graphics");
7104 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007105
cristy3ed852e2009-09-05 21:47:34 +00007106 if (*version != '\0')
7107 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007108
cristy3ed852e2009-09-05 21:47:34 +00007109 entry->note=ConstantString(PNGNote);
7110 (void) RegisterMagickInfo(entry);
7111
7112 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007113
cristy3ed852e2009-09-05 21:47:34 +00007114#if defined(MAGICKCORE_PNG_DELEGATE)
7115 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7116 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7117#endif
glennrp47b9dd52010-11-24 18:12:06 +00007118
cristy3ed852e2009-09-05 21:47:34 +00007119 entry->magick=(IsImageFormatHandler *) IsPNG;
7120 entry->adjoin=MagickFalse;
7121 entry->description=ConstantString(
7122 "8-bit indexed with optional binary transparency");
7123 entry->module=ConstantString("PNG");
7124 (void) RegisterMagickInfo(entry);
7125
7126 entry=SetMagickInfo("PNG24");
7127 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007128
cristy3ed852e2009-09-05 21:47:34 +00007129#if defined(ZLIB_VERSION)
7130 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7131 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007132
cristy3ed852e2009-09-05 21:47:34 +00007133 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7134 {
7135 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7136 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7137 }
7138#endif
glennrp47b9dd52010-11-24 18:12:06 +00007139
cristy3ed852e2009-09-05 21:47:34 +00007140 if (*version != '\0')
7141 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007142
cristy3ed852e2009-09-05 21:47:34 +00007143#if defined(MAGICKCORE_PNG_DELEGATE)
7144 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7145 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7146#endif
glennrp47b9dd52010-11-24 18:12:06 +00007147
cristy3ed852e2009-09-05 21:47:34 +00007148 entry->magick=(IsImageFormatHandler *) IsPNG;
7149 entry->adjoin=MagickFalse;
7150 entry->description=ConstantString("opaque 24-bit RGB");
7151 entry->module=ConstantString("PNG");
7152 (void) RegisterMagickInfo(entry);
7153
7154 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007155
cristy3ed852e2009-09-05 21:47:34 +00007156#if defined(MAGICKCORE_PNG_DELEGATE)
7157 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7158 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7159#endif
glennrp47b9dd52010-11-24 18:12:06 +00007160
cristy3ed852e2009-09-05 21:47:34 +00007161 entry->magick=(IsImageFormatHandler *) IsPNG;
7162 entry->adjoin=MagickFalse;
7163 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7164 entry->module=ConstantString("PNG");
7165 (void) RegisterMagickInfo(entry);
7166
7167 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007168
cristy3ed852e2009-09-05 21:47:34 +00007169#if defined(JNG_SUPPORTED)
7170#if defined(MAGICKCORE_PNG_DELEGATE)
7171 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7172 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7173#endif
7174#endif
glennrp47b9dd52010-11-24 18:12:06 +00007175
cristy3ed852e2009-09-05 21:47:34 +00007176 entry->magick=(IsImageFormatHandler *) IsJNG;
7177 entry->adjoin=MagickFalse;
7178 entry->description=ConstantString("JPEG Network Graphics");
7179 entry->module=ConstantString("PNG");
7180 entry->note=ConstantString(JNGNote);
7181 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007182
cristy18b17442009-10-25 18:36:48 +00007183#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007184 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007185#endif
glennrp47b9dd52010-11-24 18:12:06 +00007186
cristy3ed852e2009-09-05 21:47:34 +00007187 return(MagickImageCoderSignature);
7188}
7189
7190/*
7191%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7192% %
7193% %
7194% %
7195% U n r e g i s t e r P N G I m a g e %
7196% %
7197% %
7198% %
7199%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7200%
7201% UnregisterPNGImage() removes format registrations made by the
7202% PNG module from the list of supported formats.
7203%
7204% The format of the UnregisterPNGImage method is:
7205%
7206% UnregisterPNGImage(void)
7207%
7208*/
7209ModuleExport void UnregisterPNGImage(void)
7210{
7211 (void) UnregisterMagickInfo("MNG");
7212 (void) UnregisterMagickInfo("PNG");
7213 (void) UnregisterMagickInfo("PNG8");
7214 (void) UnregisterMagickInfo("PNG24");
7215 (void) UnregisterMagickInfo("PNG32");
7216 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007217
cristy3ed852e2009-09-05 21:47:34 +00007218#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007219 if (ping_semaphore != (SemaphoreInfo *) NULL)
7220 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007221#endif
7222}
7223
7224#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007225#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007226/*
7227%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7228% %
7229% %
7230% %
7231% W r i t e M N G I m a g e %
7232% %
7233% %
7234% %
7235%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7236%
7237% WriteMNGImage() writes an image in the Portable Network Graphics
7238% Group's "Multiple-image Network Graphics" encoded image format.
7239%
7240% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7241%
7242% The format of the WriteMNGImage method is:
7243%
cristy1e178e72011-08-28 19:44:34 +00007244% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7245% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007246%
7247% A description of each parameter follows.
7248%
7249% o image_info: the image info.
7250%
7251% o image: The image.
7252%
cristy1e178e72011-08-28 19:44:34 +00007253% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007254%
7255% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7256% "To do" under ReadPNGImage):
7257%
cristy3ed852e2009-09-05 21:47:34 +00007258% Preserve all unknown and not-yet-handled known chunks found in input
7259% PNG file and copy them into output PNG files according to the PNG
7260% copying rules.
7261%
7262% Write the iCCP chunk at MNG level when (icc profile length > 0)
7263%
7264% Improve selection of color type (use indexed-colour or indexed-colour
7265% with tRNS when 256 or fewer unique RGBA values are present).
7266%
7267% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7268% This will be complicated if we limit ourselves to generating MNG-LC
7269% files. For now we ignore disposal method 3 and simply overlay the next
7270% image on it.
7271%
7272% Check for identical PLTE's or PLTE/tRNS combinations and use a
7273% global MNG PLTE or PLTE/tRNS combination when appropriate.
7274% [mostly done 15 June 1999 but still need to take care of tRNS]
7275%
7276% Check for identical sRGB and replace with a global sRGB (and remove
7277% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7278% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7279% local gAMA/cHRM with local sRGB if appropriate).
7280%
7281% Check for identical sBIT chunks and write global ones.
7282%
7283% Provide option to skip writing the signature tEXt chunks.
7284%
7285% Use signatures to detect identical objects and reuse the first
7286% instance of such objects instead of writing duplicate objects.
7287%
7288% Use a smaller-than-32k value of compression window size when
7289% appropriate.
7290%
7291% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7292% ancillary text chunks and save profiles.
7293%
7294% Provide an option to force LC files (to ensure exact framing rate)
7295% instead of VLC.
7296%
7297% Provide an option to force VLC files instead of LC, even when offsets
7298% are present. This will involve expanding the embedded images with a
7299% transparent region at the top and/or left.
7300*/
7301
cristy3ed852e2009-09-05 21:47:34 +00007302static void
glennrpcf002022011-01-30 02:38:15 +00007303Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007304 png_info *ping_info, unsigned char *profile_type, unsigned char
7305 *profile_description, unsigned char *profile_data, png_uint_32 length)
7306{
cristy3ed852e2009-09-05 21:47:34 +00007307 png_textp
7308 text;
7309
cristybb503372010-05-27 20:51:26 +00007310 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007311 i;
7312
7313 unsigned char
7314 *sp;
7315
7316 png_charp
7317 dp;
7318
7319 png_uint_32
7320 allocated_length,
7321 description_length;
7322
7323 unsigned char
7324 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007325
cristy3ed852e2009-09-05 21:47:34 +00007326 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7327 return;
7328
7329 if (image_info->verbose)
7330 {
glennrp0fe50b42010-11-16 03:52:51 +00007331 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7332 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007333 }
glennrp0fe50b42010-11-16 03:52:51 +00007334
cristy3ed852e2009-09-05 21:47:34 +00007335 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7336 description_length=(png_uint_32) strlen((const char *) profile_description);
7337 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7338 + description_length);
7339 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7340 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7341 text[0].key[0]='\0';
7342 (void) ConcatenateMagickString(text[0].key,
7343 "Raw profile type ",MaxTextExtent);
7344 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7345 sp=profile_data;
7346 dp=text[0].text;
7347 *dp++='\n';
7348 (void) CopyMagickString(dp,(const char *) profile_description,
7349 allocated_length);
7350 dp+=description_length;
7351 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007352 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007353 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007354 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007355
cristybb503372010-05-27 20:51:26 +00007356 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007357 {
7358 if (i%36 == 0)
7359 *dp++='\n';
7360 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7361 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7362 }
glennrp47b9dd52010-11-24 18:12:06 +00007363
cristy3ed852e2009-09-05 21:47:34 +00007364 *dp++='\n';
7365 *dp='\0';
7366 text[0].text_length=(png_size_t) (dp-text[0].text);
7367 text[0].compression=image_info->compression == NoCompression ||
7368 (image_info->compression == UndefinedCompression &&
7369 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007370
cristy3ed852e2009-09-05 21:47:34 +00007371 if (text[0].text_length <= allocated_length)
7372 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007373
cristy3ed852e2009-09-05 21:47:34 +00007374 png_free(ping,text[0].text);
7375 png_free(ping,text[0].key);
7376 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007377}
7378
glennrpcf002022011-01-30 02:38:15 +00007379static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007380 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007381{
7382 char
7383 *name;
7384
7385 const StringInfo
7386 *profile;
7387
7388 unsigned char
7389 *data;
7390
7391 png_uint_32 length;
7392
7393 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007394
7395 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7396 {
cristy3ed852e2009-09-05 21:47:34 +00007397 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007398
cristy3ed852e2009-09-05 21:47:34 +00007399 if (profile != (const StringInfo *) NULL)
7400 {
7401 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007402 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007403
glennrp47b9dd52010-11-24 18:12:06 +00007404 if (LocaleNCompare(name,string,11) == 0)
7405 {
7406 if (logging != MagickFalse)
7407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7408 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007409
glennrpcf002022011-01-30 02:38:15 +00007410 ping_profile=CloneStringInfo(profile);
7411 data=GetStringInfoDatum(ping_profile),
7412 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007413 data[4]=data[3];
7414 data[3]=data[2];
7415 data[2]=data[1];
7416 data[1]=data[0];
7417 (void) WriteBlobMSBULong(image,length-5); /* data length */
7418 (void) WriteBlob(image,length-1,data+1);
7419 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007420 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007421 }
cristy3ed852e2009-09-05 21:47:34 +00007422 }
glennrp47b9dd52010-11-24 18:12:06 +00007423
cristy3ed852e2009-09-05 21:47:34 +00007424 name=GetNextImageProfile(image);
7425 }
glennrp47b9dd52010-11-24 18:12:06 +00007426
cristy3ed852e2009-09-05 21:47:34 +00007427 return(MagickTrue);
7428}
7429
glennrpb9cfe272010-12-21 15:08:06 +00007430
cristy3ed852e2009-09-05 21:47:34 +00007431/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007432static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +00007433 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007434{
7435 Image
7436 *image;
7437
7438 ImageInfo
7439 *image_info;
7440
cristy3ed852e2009-09-05 21:47:34 +00007441 char
7442 s[2];
7443
7444 const char
7445 *name,
7446 *property,
7447 *value;
7448
7449 const StringInfo
7450 *profile;
7451
cristy3ed852e2009-09-05 21:47:34 +00007452 int
cristy3ed852e2009-09-05 21:47:34 +00007453 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007454 pass;
7455
glennrpe9c26dc2010-05-30 01:56:35 +00007456 png_byte
7457 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007458
glennrp39992b42010-11-14 00:03:43 +00007459 png_color
7460 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007461
glennrp5af765f2010-03-30 11:12:18 +00007462 png_color_16
7463 ping_background,
7464 ping_trans_color;
7465
cristy3ed852e2009-09-05 21:47:34 +00007466 png_info
7467 *ping_info;
7468
7469 png_struct
7470 *ping;
7471
glennrp5af765f2010-03-30 11:12:18 +00007472 png_uint_32
7473 ping_height,
7474 ping_width;
7475
cristybb503372010-05-27 20:51:26 +00007476 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007477 y;
7478
7479 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007480 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007481 logging,
glennrp58e01762011-01-07 15:28:54 +00007482 matte,
7483
glennrpda8f3a72011-02-27 23:54:12 +00007484 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007485 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007486 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007487 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007488 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007489 ping_have_bKGD,
7490 ping_have_pHYs,
7491 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007492
7493 ping_exclude_bKGD,
7494 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007495 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007496 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007497 ping_exclude_gAMA,
7498 ping_exclude_iCCP,
7499 /* ping_exclude_iTXt, */
7500 ping_exclude_oFFs,
7501 ping_exclude_pHYs,
7502 ping_exclude_sRGB,
7503 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007504 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007505 ping_exclude_vpAg,
7506 ping_exclude_zCCP, /* hex-encoded iCCP */
7507 ping_exclude_zTXt,
7508
glennrp8d3d6e52011-04-19 04:39:51 +00007509 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007510 ping_need_colortype_warning,
7511
glennrp82b3c532011-03-22 19:20:54 +00007512 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007513 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007514 tried_333,
7515 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007516
7517 QuantumInfo
7518 *quantum_info;
7519
cristybb503372010-05-27 20:51:26 +00007520 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007521 i,
7522 x;
7523
7524 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007525 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007526
glennrp5af765f2010-03-30 11:12:18 +00007527 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007528 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007529 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007530 ping_color_type,
7531 ping_interlace_method,
7532 ping_compression_method,
7533 ping_filter_method,
7534 ping_num_trans;
7535
cristybb503372010-05-27 20:51:26 +00007536 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007537 image_depth,
7538 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007539
cristybb503372010-05-27 20:51:26 +00007540 size_t
cristy3ed852e2009-09-05 21:47:34 +00007541 quality,
7542 rowbytes,
7543 save_image_depth;
7544
glennrpdfd70802010-11-14 01:23:35 +00007545 int
glennrpfd05d622011-02-25 04:10:33 +00007546 j,
glennrpf09bded2011-01-08 01:15:59 +00007547 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007548 number_opaque,
7549 number_semitransparent,
7550 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007551 ping_pHYs_unit_type;
7552
7553 png_uint_32
7554 ping_pHYs_x_resolution,
7555 ping_pHYs_y_resolution;
7556
cristy3ed852e2009-09-05 21:47:34 +00007557 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007558 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007559
glennrpb9cfe272010-12-21 15:08:06 +00007560 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7561 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007562 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007563 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007564
cristy3ed852e2009-09-05 21:47:34 +00007565#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007566 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007567#endif
7568
glennrp5af765f2010-03-30 11:12:18 +00007569 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007570 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007571 ping_color_type=0,
7572 ping_interlace_method=0,
7573 ping_compression_method=0,
7574 ping_filter_method=0,
7575 ping_num_trans = 0;
7576
7577 ping_background.red = 0;
7578 ping_background.green = 0;
7579 ping_background.blue = 0;
7580 ping_background.gray = 0;
7581 ping_background.index = 0;
7582
7583 ping_trans_color.red=0;
7584 ping_trans_color.green=0;
7585 ping_trans_color.blue=0;
7586 ping_trans_color.gray=0;
7587
glennrpdfd70802010-11-14 01:23:35 +00007588 ping_pHYs_unit_type = 0;
7589 ping_pHYs_x_resolution = 0;
7590 ping_pHYs_y_resolution = 0;
7591
glennrpda8f3a72011-02-27 23:54:12 +00007592 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007593 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007594 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007595 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007596 ping_have_bKGD=MagickFalse;
7597 ping_have_pHYs=MagickFalse;
7598 ping_have_tRNS=MagickFalse;
7599
glennrp0e8ea192010-12-24 18:00:33 +00007600 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7601 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007602 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007603 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007604 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007605 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7606 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7607 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7608 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7609 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7610 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007611 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007612 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7613 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7614 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7615
glennrp8d3d6e52011-04-19 04:39:51 +00007616 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007617 ping_need_colortype_warning = MagickFalse;
7618
cristy0d57eec2011-09-04 22:13:56 +00007619 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7620 * i.e., eliminate the ICC profile and set image->rendering_intent.
7621 * Note that this will not involve any changes to the actual pixels
7622 * but merely passes information to applications that read the resulting
7623 * PNG image.
7624 */
7625 if (ping_exclude_sRGB == MagickFalse)
7626 {
7627 char
7628 *name;
7629
7630 const StringInfo
7631 *profile;
7632
7633 ResetImageProfileIterator(image);
7634 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7635 {
7636 profile=GetImageProfile(image,name);
7637
7638 if (profile != (StringInfo *) NULL)
7639 {
7640 if ((LocaleCompare(name,"ICC") == 0) ||
glennrp29a106e2011-09-06 17:11:42 +00007641 (LocaleCompare(name,"ICM") == 0))
7642 {
glennrpee7b4c02011-10-04 01:21:09 +00007643 int
7644 icheck;
7645
7646 /* 0: not a known sRGB profile
7647 * 1: HP-Microsoft sRGB v2
7648 * 2: ICC sRGB v4 perceptual
7649 * 3: ICC sRGB v2 perceptual no black-compensation
7650 */
7651 png_uint_32
7652 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7653 check_len[4] = {0, 3144, 60960, 3052};
7654
7655 png_uint_32
7656 length,
7657 profile_crc;
7658
cristy0d57eec2011-09-04 22:13:56 +00007659 unsigned char
7660 *data;
7661
glennrp29a106e2011-09-06 17:11:42 +00007662 length=(png_uint_32) GetStringInfoLength(profile);
7663
glennrpee7b4c02011-10-04 01:21:09 +00007664 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007665 {
glennrpee7b4c02011-10-04 01:21:09 +00007666 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007667 {
glennrpee7b4c02011-10-04 01:21:09 +00007668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7669 " Got a %lu-byte ICC profile (potentially sRGB)",
7670 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007671
glennrpee7b4c02011-10-04 01:21:09 +00007672 data=GetStringInfoDatum(profile);
7673 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007674
glennrpee7b4c02011-10-04 01:21:09 +00007675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007676 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007677
7678 if (profile_crc == check_crc[icheck])
7679 {
7680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7681 " It is sRGB.");
7682 if (image->rendering_intent==UndefinedIntent)
7683 image->rendering_intent=PerceptualIntent;
7684 break;
7685 }
glennrp29a106e2011-09-06 17:11:42 +00007686 }
glennrp29a106e2011-09-06 17:11:42 +00007687 }
glennrpee7b4c02011-10-04 01:21:09 +00007688 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007690 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007691 (unsigned long) length);
7692 }
cristy0d57eec2011-09-04 22:13:56 +00007693 }
7694 name=GetNextImageProfile(image);
7695 }
7696 }
7697
glennrp8bb3a022010-12-13 20:40:04 +00007698 number_opaque = 0;
7699 number_semitransparent = 0;
7700 number_transparent = 0;
7701
glennrpfd05d622011-02-25 04:10:33 +00007702 if (logging != MagickFalse)
7703 {
7704 if (image->storage_class == UndefinedClass)
7705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7706 " storage_class=UndefinedClass");
7707 if (image->storage_class == DirectClass)
7708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7709 " storage_class=DirectClass");
7710 if (image->storage_class == PseudoClass)
7711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7712 " storage_class=PseudoClass");
7713 }
glennrp28af3712011-04-06 18:07:30 +00007714
glennrp7e65e932011-08-19 02:31:16 +00007715 if (image->storage_class == PseudoClass &&
7716 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7717 (mng_info->write_png_colortype != 0 &&
7718 mng_info->write_png_colortype != 4)))
7719 {
7720 (void) SyncImage(image);
7721 image->storage_class = DirectClass;
7722 }
7723
glennrpc6c391a2011-04-27 02:23:56 +00007724 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007725 {
glennrpc6c391a2011-04-27 02:23:56 +00007726 if (image->storage_class != PseudoClass && image->colormap != NULL)
7727 {
7728 /* Free the bogus colormap; it can cause trouble later */
7729 if (logging != MagickFalse)
7730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7731 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007732 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007733 image->colormap=NULL;
7734 }
glennrp28af3712011-04-06 18:07:30 +00007735 }
glennrpbb4f99d2011-05-22 11:13:17 +00007736
cristy510d06a2011-07-06 23:43:54 +00007737 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristye941a752011-10-15 01:52:48 +00007738 (void) TransformImageColorspace(image,RGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007739
glennrp3241bd02010-12-12 04:36:28 +00007740 /*
7741 Sometimes we get PseudoClass images whose RGB values don't match
7742 the colors in the colormap. This code syncs the RGB values.
7743 */
7744 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7745 (void) SyncImage(image);
7746
glennrpa6a06632011-01-19 15:15:34 +00007747#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7748 if (image->depth > 8)
7749 {
7750 if (logging != MagickFalse)
7751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7752 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7753
7754 image->depth=8;
7755 }
7756#endif
7757
glennrp8e58efd2011-05-20 12:16:29 +00007758 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007759 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7760 {
cristy4c08aed2011-07-01 19:47:50 +00007761 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007762 *r;
7763
7764 ExceptionInfo
7765 *exception;
7766
7767 exception=(&image->exception);
7768
7769 if (image->depth > 8)
7770 {
7771#if MAGICKCORE_QUANTUM_DEPTH > 16
7772 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007773 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007774
7775 for (y=0; y < (ssize_t) image->rows; y++)
7776 {
7777 r=GetAuthenticPixels(image,0,y,image->columns,1,
7778 exception);
7779
cristy4c08aed2011-07-01 19:47:50 +00007780 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007781 break;
7782
7783 for (x=0; x < (ssize_t) image->columns; x++)
7784 {
glennrp54cf7972011-08-06 14:28:09 +00007785 LBR16PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007786 r++;
7787 }
glennrpbb4f99d2011-05-22 11:13:17 +00007788
glennrp8e58efd2011-05-20 12:16:29 +00007789 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7790 break;
7791 }
7792
7793 if (image->storage_class == PseudoClass && image->colormap != NULL)
7794 {
cristy3e08f112011-05-24 13:19:30 +00007795 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007796 {
glennrp91d99252011-06-25 14:30:13 +00007797 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007798 }
7799 }
7800#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7801 }
7802
7803 else if (image->depth > 4)
7804 {
7805#if MAGICKCORE_QUANTUM_DEPTH > 8
7806 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007807 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007808
7809 for (y=0; y < (ssize_t) image->rows; y++)
7810 {
7811 r=GetAuthenticPixels(image,0,y,image->columns,1,
7812 exception);
7813
cristy4c08aed2011-07-01 19:47:50 +00007814 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007815 break;
7816
7817 for (x=0; x < (ssize_t) image->columns; x++)
7818 {
glennrp54cf7972011-08-06 14:28:09 +00007819 LBR08PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007820 r++;
7821 }
glennrpbb4f99d2011-05-22 11:13:17 +00007822
glennrp8e58efd2011-05-20 12:16:29 +00007823 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7824 break;
7825 }
7826
7827 if (image->storage_class == PseudoClass && image->colormap != NULL)
7828 {
cristy3e08f112011-05-24 13:19:30 +00007829 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007830 {
glennrp91d99252011-06-25 14:30:13 +00007831 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007832 }
7833 }
7834#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7835 }
7836 else
7837 if (image->depth > 2)
7838 {
7839 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007840 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007841
7842 for (y=0; y < (ssize_t) image->rows; y++)
7843 {
7844 r=GetAuthenticPixels(image,0,y,image->columns,1,
7845 exception);
7846
cristy4c08aed2011-07-01 19:47:50 +00007847 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007848 break;
7849
7850 for (x=0; x < (ssize_t) image->columns; x++)
7851 {
glennrp54cf7972011-08-06 14:28:09 +00007852 LBR04PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007853 r++;
7854 }
glennrpbb4f99d2011-05-22 11:13:17 +00007855
glennrp8e58efd2011-05-20 12:16:29 +00007856 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7857 break;
7858 }
7859
7860 if (image->storage_class == PseudoClass && image->colormap != NULL)
7861 {
cristy3e08f112011-05-24 13:19:30 +00007862 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007863 {
glennrp91d99252011-06-25 14:30:13 +00007864 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007865 }
7866 }
7867 }
7868
7869 else if (image->depth > 1)
7870 {
7871 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007872 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007873
7874 for (y=0; y < (ssize_t) image->rows; y++)
7875 {
7876 r=GetAuthenticPixels(image,0,y,image->columns,1,
7877 exception);
7878
cristy4c08aed2011-07-01 19:47:50 +00007879 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007880 break;
7881
7882 for (x=0; x < (ssize_t) image->columns; x++)
7883 {
glennrp54cf7972011-08-06 14:28:09 +00007884 LBR02PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007885 r++;
7886 }
glennrpbb4f99d2011-05-22 11:13:17 +00007887
glennrp8e58efd2011-05-20 12:16:29 +00007888 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7889 break;
7890 }
7891
7892 if (image->storage_class == PseudoClass && image->colormap != NULL)
7893 {
cristy3e08f112011-05-24 13:19:30 +00007894 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007895 {
glennrp91d99252011-06-25 14:30:13 +00007896 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007897 }
7898 }
7899 }
7900 else
7901 {
7902 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007903 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007904
7905 for (y=0; y < (ssize_t) image->rows; y++)
7906 {
7907 r=GetAuthenticPixels(image,0,y,image->columns,1,
7908 exception);
7909
cristy4c08aed2011-07-01 19:47:50 +00007910 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007911 break;
7912
7913 for (x=0; x < (ssize_t) image->columns; x++)
7914 {
glennrp54cf7972011-08-06 14:28:09 +00007915 LBR01PixelRGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007916 r++;
7917 }
glennrpbb4f99d2011-05-22 11:13:17 +00007918
glennrp8e58efd2011-05-20 12:16:29 +00007919 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7920 break;
7921 }
7922
7923 if (image->storage_class == PseudoClass && image->colormap != NULL)
7924 {
cristy3e08f112011-05-24 13:19:30 +00007925 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007926 {
glennrp91d99252011-06-25 14:30:13 +00007927 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007928 }
7929 }
7930 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007931 }
7932
glennrp67b9c1a2011-04-22 18:47:36 +00007933 /* To do: set to next higher multiple of 8 */
7934 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007935 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007936
glennrp2b013e42010-11-24 16:55:50 +00007937#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7938 /* PNG does not handle depths greater than 16 so reduce it even
7939 * if lossy
7940 */
glennrp8e58efd2011-05-20 12:16:29 +00007941 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007942 image->depth=16;
7943#endif
7944
glennrp3faa9a32011-04-23 14:00:25 +00007945#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007946 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007947 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007948 image->depth = 8;
7949#endif
7950
glennrpc8c2f062011-02-25 19:00:33 +00007951 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007952 * we reduce the transparency to binary and run again, then if there
7953 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
glennrp8ca51ad2011-05-12 21:22:32 +00007954 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7955 * palette. Then (To do) we take care of a final reduction that is only
7956 * needed if there are still 256 colors present and one of them has both
7957 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007958 */
glennrp82b3c532011-03-22 19:20:54 +00007959
glennrp8ca51ad2011-05-12 21:22:32 +00007960 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007961 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007962 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007963
glennrp8ca51ad2011-05-12 21:22:32 +00007964 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007965 {
7966 /* BUILD_PALETTE
7967 *
7968 * Sometimes we get DirectClass images that have 256 colors or fewer.
7969 * This code will build a colormap.
7970 *
7971 * Also, sometimes we get PseudoClass images with an out-of-date
7972 * colormap. This code will replace the colormap with a new one.
7973 * Sometimes we get PseudoClass images that have more than 256 colors.
7974 * This code will delete the colormap and change the image to
7975 * DirectClass.
7976 *
cristy4c08aed2011-07-01 19:47:50 +00007977 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00007978 * even though it sometimes contains left-over non-opaque values.
7979 *
7980 * Also we gather some information (number of opaque, transparent,
7981 * and semitransparent pixels, and whether the image has any non-gray
7982 * pixels or only black-and-white pixels) that we might need later.
7983 *
7984 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7985 * we need to check for bogus non-opaque values, at least.
7986 */
glennrp3c218112010-11-27 15:31:26 +00007987
glennrpd71e86a2011-02-24 01:28:37 +00007988 ExceptionInfo
7989 *exception;
glennrp3c218112010-11-27 15:31:26 +00007990
glennrpd71e86a2011-02-24 01:28:37 +00007991 int
7992 n;
glennrp3c218112010-11-27 15:31:26 +00007993
cristy101ab702011-10-13 13:06:32 +00007994 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00007995 opaque[260],
7996 semitransparent[260],
7997 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007998
cristy4c08aed2011-07-01 19:47:50 +00007999 register const Quantum
8000 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008001
cristy4c08aed2011-07-01 19:47:50 +00008002 register Quantum
8003 *q,
glennrpfd05d622011-02-25 04:10:33 +00008004 *r;
8005
glennrpd71e86a2011-02-24 01:28:37 +00008006 if (logging != MagickFalse)
8007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8008 " Enter BUILD_PALETTE:");
8009
8010 if (logging != MagickFalse)
8011 {
glennrp03812ae2010-12-24 01:31:34 +00008012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008013 " image->columns=%.20g",(double) image->columns);
8014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8015 " image->rows=%.20g",(double) image->rows);
8016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8017 " image->matte=%.20g",(double) image->matte);
8018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8019 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008020
glennrpfd05d622011-02-25 04:10:33 +00008021 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008022 {
8023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008024 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008026 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008027
glennrpd71e86a2011-02-24 01:28:37 +00008028 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008029 {
glennrpd71e86a2011-02-24 01:28:37 +00008030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8031 " %d (%d,%d,%d,%d)",
8032 (int) i,
8033 (int) image->colormap[i].red,
8034 (int) image->colormap[i].green,
8035 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008036 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008037 }
glennrp2cc891a2010-12-24 13:44:32 +00008038
glennrpd71e86a2011-02-24 01:28:37 +00008039 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8040 {
8041 if (i > 255)
8042 {
8043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8044 " %d (%d,%d,%d,%d)",
8045 (int) i,
8046 (int) image->colormap[i].red,
8047 (int) image->colormap[i].green,
8048 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008049 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008050 }
8051 }
glennrp03812ae2010-12-24 01:31:34 +00008052 }
glennrp7ddcc222010-12-11 05:01:05 +00008053
glennrpd71e86a2011-02-24 01:28:37 +00008054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8055 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008056
glennrpd71e86a2011-02-24 01:28:37 +00008057 if (image->colors == 0)
8058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8059 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008060
glennrp8d3d6e52011-04-19 04:39:51 +00008061 if (ping_preserve_colormap == MagickFalse)
8062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8063 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008064 }
8065
8066 exception=(&image->exception);
8067
8068 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008069 number_opaque = 0;
8070 number_semitransparent = 0;
8071 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008072
8073 for (y=0; y < (ssize_t) image->rows; y++)
8074 {
8075 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8076
cristyacd2ed22011-08-30 01:44:23 +00008077 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008078 break;
8079
8080 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008081 {
glennrp4737d522011-04-29 03:33:42 +00008082 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008083 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008084 {
8085 if (number_opaque < 259)
8086 {
8087 if (number_opaque == 0)
8088 {
cristy101ab702011-10-13 13:06:32 +00008089 GetPixelInfoPixel(image, q, opaque);
cristy4c08aed2011-07-01 19:47:50 +00008090 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008091 number_opaque=1;
8092 }
glennrp2cc891a2010-12-24 13:44:32 +00008093
glennrpd71e86a2011-02-24 01:28:37 +00008094 for (i=0; i< (ssize_t) number_opaque; i++)
8095 {
cristy4c08aed2011-07-01 19:47:50 +00008096 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008097 break;
8098 }
glennrp7ddcc222010-12-11 05:01:05 +00008099
glennrpd71e86a2011-02-24 01:28:37 +00008100 if (i == (ssize_t) number_opaque &&
8101 number_opaque < 259)
8102 {
8103 number_opaque++;
cristy101ab702011-10-13 13:06:32 +00008104 GetPixelInfoPixel(image, q, opaque+i);
cristy4c08aed2011-07-01 19:47:50 +00008105 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008106 }
8107 }
8108 }
cristy4c08aed2011-07-01 19:47:50 +00008109 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008110 {
8111 if (number_transparent < 259)
8112 {
8113 if (number_transparent == 0)
8114 {
cristy101ab702011-10-13 13:06:32 +00008115 GetPixelInfoPixel(image, q, transparent);
cristy4c08aed2011-07-01 19:47:50 +00008116 ping_trans_color.red=(unsigned short)
8117 GetPixelRed(image,q);
8118 ping_trans_color.green=(unsigned short)
8119 GetPixelGreen(image,q);
8120 ping_trans_color.blue=(unsigned short)
8121 GetPixelBlue(image,q);
8122 ping_trans_color.gray=(unsigned short)
8123 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008124 number_transparent = 1;
8125 }
8126
8127 for (i=0; i< (ssize_t) number_transparent; i++)
8128 {
cristy4c08aed2011-07-01 19:47:50 +00008129 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008130 break;
8131 }
8132
8133 if (i == (ssize_t) number_transparent &&
8134 number_transparent < 259)
8135 {
8136 number_transparent++;
cristy101ab702011-10-13 13:06:32 +00008137 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008138 }
8139 }
8140 }
8141 else
8142 {
8143 if (number_semitransparent < 259)
8144 {
8145 if (number_semitransparent == 0)
8146 {
cristy101ab702011-10-13 13:06:32 +00008147 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008148 number_semitransparent = 1;
8149 }
8150
8151 for (i=0; i< (ssize_t) number_semitransparent; i++)
8152 {
cristy4c08aed2011-07-01 19:47:50 +00008153 if (IsPixelEquivalent(image,q, semitransparent+i)
8154 && GetPixelAlpha(image,q) ==
8155 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008156 break;
8157 }
8158
8159 if (i == (ssize_t) number_semitransparent &&
8160 number_semitransparent < 259)
8161 {
8162 number_semitransparent++;
cristy101ab702011-10-13 13:06:32 +00008163 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008164 }
8165 }
8166 }
cristyed231572011-07-14 02:18:59 +00008167 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008168 }
8169 }
8170
cristy4054bfb2011-08-29 23:41:39 +00008171 if (mng_info->write_png8 == MagickFalse &&
8172 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008173 {
8174 /* Add the background color to the palette, if it
8175 * isn't already there.
8176 */
glennrpc6c391a2011-04-27 02:23:56 +00008177 if (logging != MagickFalse)
8178 {
8179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8180 " Check colormap for background (%d,%d,%d)",
8181 (int) image->background_color.red,
8182 (int) image->background_color.green,
8183 (int) image->background_color.blue);
8184 }
glennrpd71e86a2011-02-24 01:28:37 +00008185 for (i=0; i<number_opaque; i++)
8186 {
glennrpca7ad3a2011-04-26 04:44:54 +00008187 if (opaque[i].red == image->background_color.red &&
8188 opaque[i].green == image->background_color.green &&
8189 opaque[i].blue == image->background_color.blue)
8190 break;
glennrpd71e86a2011-02-24 01:28:37 +00008191 }
glennrpd71e86a2011-02-24 01:28:37 +00008192 if (number_opaque < 259 && i == number_opaque)
8193 {
glennrp8e045c82011-04-27 16:40:27 +00008194 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008195 ping_background.index = i;
8196 if (logging != MagickFalse)
8197 {
8198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8199 " background_color index is %d",(int) i);
8200 }
8201
glennrpd71e86a2011-02-24 01:28:37 +00008202 }
glennrpa080bc32011-03-11 18:03:44 +00008203 else if (logging != MagickFalse)
8204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8205 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008206 }
8207
8208 image_colors=number_opaque+number_transparent+number_semitransparent;
8209
glennrpa080bc32011-03-11 18:03:44 +00008210 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8211 {
8212 /* No room for the background color; remove it. */
8213 number_opaque--;
8214 image_colors--;
8215 }
8216
glennrpd71e86a2011-02-24 01:28:37 +00008217 if (logging != MagickFalse)
8218 {
8219 if (image_colors > 256)
8220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8221 " image has more than 256 colors");
8222
8223 else
8224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8225 " image has %d colors",image_colors);
8226 }
8227
glennrp8d3d6e52011-04-19 04:39:51 +00008228 if (ping_preserve_colormap != MagickFalse)
8229 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008230
glennrpfd05d622011-02-25 04:10:33 +00008231 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008232 {
8233 ping_have_color=MagickFalse;
8234 ping_have_non_bw=MagickFalse;
8235
8236 if(image_colors > 256)
8237 {
8238 for (y=0; y < (ssize_t) image->rows; y++)
8239 {
8240 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8241
cristyacd2ed22011-08-30 01:44:23 +00008242 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008243 break;
8244
glennrpe5e6b802011-07-20 14:44:40 +00008245 s=q;
8246 for (x=0; x < (ssize_t) image->columns; x++)
8247 {
8248 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8249 GetPixelRed(image,s) != GetPixelBlue(image,s))
8250 {
8251 ping_have_color=MagickTrue;
8252 ping_have_non_bw=MagickTrue;
8253 break;
8254 }
8255 s+=GetPixelChannels(image);
8256 }
8257
8258 if (ping_have_color != MagickFalse)
8259 break;
8260
glennrpd71e86a2011-02-24 01:28:37 +00008261 /* Worst case is black-and-white; we are looking at every
8262 * pixel twice.
8263 */
8264
glennrpd71e86a2011-02-24 01:28:37 +00008265 if (ping_have_non_bw == MagickFalse)
8266 {
8267 s=q;
8268 for (x=0; x < (ssize_t) image->columns; x++)
8269 {
cristy4c08aed2011-07-01 19:47:50 +00008270 if (GetPixelRed(image,s) != 0 &&
8271 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008272 {
8273 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008274 break;
glennrpd71e86a2011-02-24 01:28:37 +00008275 }
cristyed231572011-07-14 02:18:59 +00008276 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008277 }
glennrpe5e6b802011-07-20 14:44:40 +00008278 }
glennrpd71e86a2011-02-24 01:28:37 +00008279 }
glennrpbb4f99d2011-05-22 11:13:17 +00008280 }
8281 }
glennrpd71e86a2011-02-24 01:28:37 +00008282
8283 if (image_colors < 257)
8284 {
cristy101ab702011-10-13 13:06:32 +00008285 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008286 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008287
glennrpd71e86a2011-02-24 01:28:37 +00008288 /*
8289 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008290 */
8291
glennrpd71e86a2011-02-24 01:28:37 +00008292 if (logging != MagickFalse)
8293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8294 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008295
glennrpd71e86a2011-02-24 01:28:37 +00008296 /* Sort palette, transparent first */;
8297
8298 n = 0;
8299
8300 for (i=0; i<number_transparent; i++)
8301 colormap[n++] = transparent[i];
8302
8303 for (i=0; i<number_semitransparent; i++)
8304 colormap[n++] = semitransparent[i];
8305
8306 for (i=0; i<number_opaque; i++)
8307 colormap[n++] = opaque[i];
8308
glennrpc6c391a2011-04-27 02:23:56 +00008309 ping_background.index +=
8310 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008311
glennrpd71e86a2011-02-24 01:28:37 +00008312 /* image_colors < 257; search the colormap instead of the pixels
8313 * to get ping_have_color and ping_have_non_bw
8314 */
8315 for (i=0; i<n; i++)
8316 {
8317 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008318 {
glennrpd71e86a2011-02-24 01:28:37 +00008319 if (colormap[i].red != colormap[i].green ||
8320 colormap[i].red != colormap[i].blue)
8321 {
8322 ping_have_color=MagickTrue;
8323 ping_have_non_bw=MagickTrue;
8324 break;
8325 }
8326 }
8327
8328 if (ping_have_non_bw == MagickFalse)
8329 {
8330 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008331 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008332 }
glennrp8bb3a022010-12-13 20:40:04 +00008333 }
8334
glennrpd71e86a2011-02-24 01:28:37 +00008335 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8336 (number_transparent == 0 && number_semitransparent == 0)) &&
8337 (((mng_info->write_png_colortype-1) ==
8338 PNG_COLOR_TYPE_PALETTE) ||
8339 (mng_info->write_png_colortype == 0)))
8340 {
glennrp6185c532011-01-14 17:58:40 +00008341 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008342 {
glennrpd71e86a2011-02-24 01:28:37 +00008343 if (n != (ssize_t) image_colors)
8344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8345 " image_colors (%d) and n (%d) don't match",
8346 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008347
glennrpd71e86a2011-02-24 01:28:37 +00008348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8349 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008350 }
glennrp03812ae2010-12-24 01:31:34 +00008351
glennrpd71e86a2011-02-24 01:28:37 +00008352 image->colors = image_colors;
8353
cristy018f07f2011-09-04 21:15:19 +00008354 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008355 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008356 ThrowWriterException(ResourceLimitError,
8357 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008358
8359 for (i=0; i< (ssize_t) image_colors; i++)
8360 image->colormap[i] = colormap[i];
8361
8362 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008363 {
glennrpd71e86a2011-02-24 01:28:37 +00008364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8365 " image->colors=%d (%d)",
8366 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008367
glennrpd71e86a2011-02-24 01:28:37 +00008368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8369 " Update the pixel indexes");
8370 }
glennrp6185c532011-01-14 17:58:40 +00008371
glennrpfd05d622011-02-25 04:10:33 +00008372 /* Sync the pixel indices with the new colormap */
8373
glennrpd71e86a2011-02-24 01:28:37 +00008374 for (y=0; y < (ssize_t) image->rows; y++)
8375 {
8376 q=GetAuthenticPixels(image,0,y,image->columns,1,
8377 exception);
glennrp6185c532011-01-14 17:58:40 +00008378
cristyacd2ed22011-08-30 01:44:23 +00008379 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008380 break;
glennrp6185c532011-01-14 17:58:40 +00008381
glennrpbb4f99d2011-05-22 11:13:17 +00008382
glennrpd71e86a2011-02-24 01:28:37 +00008383 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008384 {
glennrpd71e86a2011-02-24 01:28:37 +00008385 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008386 {
glennrpd71e86a2011-02-24 01:28:37 +00008387 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008388 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8389 image->colormap[i].red == GetPixelRed(image,q) &&
8390 image->colormap[i].green == GetPixelGreen(image,q) &&
8391 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008392 {
cristy4c08aed2011-07-01 19:47:50 +00008393 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008394 break;
glennrp6185c532011-01-14 17:58:40 +00008395 }
glennrp6185c532011-01-14 17:58:40 +00008396 }
cristyed231572011-07-14 02:18:59 +00008397 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008398 }
glennrp6185c532011-01-14 17:58:40 +00008399
glennrpd71e86a2011-02-24 01:28:37 +00008400 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8401 break;
8402 }
8403 }
8404 }
8405
8406 if (logging != MagickFalse)
8407 {
8408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8409 " image->colors=%d", (int) image->colors);
8410
8411 if (image->colormap != NULL)
8412 {
8413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008414 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008415
8416 for (i=0; i < (ssize_t) image->colors; i++)
8417 {
cristy72988482011-03-29 16:34:38 +00008418 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008419 {
8420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8421 " %d (%d,%d,%d,%d)",
8422 (int) i,
8423 (int) image->colormap[i].red,
8424 (int) image->colormap[i].green,
8425 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008426 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008427 }
glennrp6185c532011-01-14 17:58:40 +00008428 }
8429 }
glennrp03812ae2010-12-24 01:31:34 +00008430
glennrpd71e86a2011-02-24 01:28:37 +00008431 if (number_transparent < 257)
8432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8433 " number_transparent = %d",
8434 number_transparent);
8435 else
glennrp03812ae2010-12-24 01:31:34 +00008436
glennrpd71e86a2011-02-24 01:28:37 +00008437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8438 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008439
glennrpd71e86a2011-02-24 01:28:37 +00008440 if (number_opaque < 257)
8441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8442 " number_opaque = %d",
8443 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008444
glennrpd71e86a2011-02-24 01:28:37 +00008445 else
8446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8447 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008448
glennrpd71e86a2011-02-24 01:28:37 +00008449 if (number_semitransparent < 257)
8450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8451 " number_semitransparent = %d",
8452 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008453
glennrpd71e86a2011-02-24 01:28:37 +00008454 else
8455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8456 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008457
glennrpd71e86a2011-02-24 01:28:37 +00008458 if (ping_have_non_bw == MagickFalse)
8459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8460 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008461
glennrpd71e86a2011-02-24 01:28:37 +00008462 else if (ping_have_color == MagickFalse)
8463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8464 " All pixels and the background are gray");
8465
8466 else
8467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8468 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008469
glennrp03812ae2010-12-24 01:31:34 +00008470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8471 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008472 }
glennrpfd05d622011-02-25 04:10:33 +00008473
glennrpc8c2f062011-02-25 19:00:33 +00008474 if (mng_info->write_png8 == MagickFalse)
8475 break;
glennrpfd05d622011-02-25 04:10:33 +00008476
glennrpc8c2f062011-02-25 19:00:33 +00008477 /* Make any reductions necessary for the PNG8 format */
8478 if (image_colors <= 256 &&
8479 image_colors != 0 && image->colormap != NULL &&
8480 number_semitransparent == 0 &&
8481 number_transparent <= 1)
8482 break;
8483
8484 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008485 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8486 * transparent color so if more than one is transparent we merge
8487 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008488 */
glennrp130fc452011-08-20 03:43:18 +00008489 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008490 {
8491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8492 " Thresholding the alpha channel to binary");
8493
8494 for (y=0; y < (ssize_t) image->rows; y++)
8495 {
8496 r=GetAuthenticPixels(image,0,y,image->columns,1,
8497 exception);
8498
cristy4c08aed2011-07-01 19:47:50 +00008499 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008500 break;
8501
8502 for (x=0; x < (ssize_t) image->columns; x++)
8503 {
glennrpf73547f2011-08-20 04:40:26 +00008504 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008505 {
cristy101ab702011-10-13 13:06:32 +00008506 SetPixelPixelInfo(image,&image->background_color,r);
cristy4c08aed2011-07-01 19:47:50 +00008507 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008508 }
8509 else
cristy4c08aed2011-07-01 19:47:50 +00008510 SetPixelAlpha(image,OpaqueAlpha,r);
cristyed231572011-07-14 02:18:59 +00008511 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008512 }
glennrpbb4f99d2011-05-22 11:13:17 +00008513
glennrpc8c2f062011-02-25 19:00:33 +00008514 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8515 break;
8516
8517 if (image_colors != 0 && image_colors <= 256 &&
8518 image->colormap != NULL)
8519 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008520 image->colormap[i].alpha =
8521 (image->colormap[i].alpha > TransparentAlpha/2 ?
8522 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008523 }
8524 continue;
8525 }
8526
8527 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008528 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8529 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8530 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008531 */
glennrpd3371642011-03-22 19:42:23 +00008532 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8533 {
8534 if (logging != MagickFalse)
8535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8536 " Quantizing the background color to 4-4-4");
8537
8538 tried_444 = MagickTrue;
8539
glennrp91d99252011-06-25 14:30:13 +00008540 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008541
8542 if (logging != MagickFalse)
8543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8544 " Quantizing the pixel colors to 4-4-4");
8545
8546 if (image->colormap == NULL)
8547 {
8548 for (y=0; y < (ssize_t) image->rows; y++)
8549 {
8550 r=GetAuthenticPixels(image,0,y,image->columns,1,
8551 exception);
8552
cristy4c08aed2011-07-01 19:47:50 +00008553 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008554 break;
8555
8556 for (x=0; x < (ssize_t) image->columns; x++)
8557 {
cristy4c08aed2011-07-01 19:47:50 +00008558 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008559 LBR04PixelRGB(r);
glennrpd3371642011-03-22 19:42:23 +00008560 r++;
8561 }
glennrpbb4f99d2011-05-22 11:13:17 +00008562
glennrpd3371642011-03-22 19:42:23 +00008563 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8564 break;
8565 }
8566 }
8567
8568 else /* Should not reach this; colormap already exists and
8569 must be <= 256 */
8570 {
8571 if (logging != MagickFalse)
8572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8573 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008574
glennrpd3371642011-03-22 19:42:23 +00008575 for (i=0; i<image_colors; i++)
8576 {
glennrp91d99252011-06-25 14:30:13 +00008577 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008578 }
8579 }
8580 continue;
8581 }
8582
glennrp82b3c532011-03-22 19:20:54 +00008583 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8584 {
8585 if (logging != MagickFalse)
8586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8587 " Quantizing the background color to 3-3-3");
8588
8589 tried_333 = MagickTrue;
8590
glennrp91d99252011-06-25 14:30:13 +00008591 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008592
8593 if (logging != MagickFalse)
8594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008595 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008596
8597 if (image->colormap == NULL)
8598 {
8599 for (y=0; y < (ssize_t) image->rows; y++)
8600 {
8601 r=GetAuthenticPixels(image,0,y,image->columns,1,
8602 exception);
8603
cristy4c08aed2011-07-01 19:47:50 +00008604 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008605 break;
8606
8607 for (x=0; x < (ssize_t) image->columns; x++)
8608 {
cristy4c08aed2011-07-01 19:47:50 +00008609 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8610 LBR03RGB(r);
glennrp82b3c532011-03-22 19:20:54 +00008611 r++;
8612 }
glennrpbb4f99d2011-05-22 11:13:17 +00008613
glennrp82b3c532011-03-22 19:20:54 +00008614 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8615 break;
8616 }
8617 }
8618
8619 else /* Should not reach this; colormap already exists and
8620 must be <= 256 */
8621 {
8622 if (logging != MagickFalse)
8623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008624 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008625 for (i=0; i<image_colors; i++)
8626 {
glennrp91d99252011-06-25 14:30:13 +00008627 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008628 }
glennrpd3371642011-03-22 19:42:23 +00008629 }
8630 continue;
glennrp82b3c532011-03-22 19:20:54 +00008631 }
glennrpc8c2f062011-02-25 19:00:33 +00008632
glennrp8ca51ad2011-05-12 21:22:32 +00008633 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008634 {
8635 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008637 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008638
glennrp8ca51ad2011-05-12 21:22:32 +00008639 tried_332 = MagickTrue;
8640
glennrp3faa9a32011-04-23 14:00:25 +00008641 /* Red and green were already done so we only quantize the blue
8642 * channel
8643 */
8644
glennrp91d99252011-06-25 14:30:13 +00008645 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008646
glennrpc8c2f062011-02-25 19:00:33 +00008647 if (logging != MagickFalse)
8648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008649 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008650
glennrpc8c2f062011-02-25 19:00:33 +00008651 if (image->colormap == NULL)
8652 {
8653 for (y=0; y < (ssize_t) image->rows; y++)
8654 {
8655 r=GetAuthenticPixels(image,0,y,image->columns,1,
8656 exception);
8657
cristy4c08aed2011-07-01 19:47:50 +00008658 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008659 break;
8660
8661 for (x=0; x < (ssize_t) image->columns; x++)
8662 {
cristy4c08aed2011-07-01 19:47:50 +00008663 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008664 LBR02PixelBlue(r);
glennrp52a479c2011-02-26 21:14:38 +00008665 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008666 }
glennrpbb4f99d2011-05-22 11:13:17 +00008667
glennrpc8c2f062011-02-25 19:00:33 +00008668 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8669 break;
8670 }
8671 }
glennrpfd05d622011-02-25 04:10:33 +00008672
glennrpc8c2f062011-02-25 19:00:33 +00008673 else /* Should not reach this; colormap already exists and
8674 must be <= 256 */
8675 {
8676 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008678 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008679 for (i=0; i<image_colors; i++)
8680 {
glennrp91d99252011-06-25 14:30:13 +00008681 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008682 }
8683 }
8684 continue;
8685 }
8686 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008687
8688 if (image_colors == 0 || image_colors > 256)
8689 {
8690 /* Take care of special case with 256 colors + 1 transparent
8691 * color. We don't need to quantize to 2-3-2-1; we only need to
8692 * eliminate one color, so we'll merge the two darkest red
8693 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8694 */
8695 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8696 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8697 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8698 {
8699 image->background_color.red=ScaleCharToQuantum(0x24);
8700 }
glennrpbb4f99d2011-05-22 11:13:17 +00008701
glennrp8ca51ad2011-05-12 21:22:32 +00008702 if (image->colormap == NULL)
8703 {
8704 for (y=0; y < (ssize_t) image->rows; y++)
8705 {
8706 r=GetAuthenticPixels(image,0,y,image->columns,1,
8707 exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008708
cristy4c08aed2011-07-01 19:47:50 +00008709 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008710 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008711
glennrp8ca51ad2011-05-12 21:22:32 +00008712 for (x=0; x < (ssize_t) image->columns; x++)
8713 {
cristy4c08aed2011-07-01 19:47:50 +00008714 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8715 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8716 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8717 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008718 {
cristy4c08aed2011-07-01 19:47:50 +00008719 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008720 }
cristyed231572011-07-14 02:18:59 +00008721 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008722 }
glennrpbb4f99d2011-05-22 11:13:17 +00008723
glennrp8ca51ad2011-05-12 21:22:32 +00008724 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8725 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008726
glennrp8ca51ad2011-05-12 21:22:32 +00008727 }
8728 }
8729
8730 else
8731 {
8732 for (i=0; i<image_colors; i++)
8733 {
8734 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8735 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8736 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8737 {
8738 image->colormap[i].red=ScaleCharToQuantum(0x24);
8739 }
8740 }
8741 }
8742 }
glennrpd71e86a2011-02-24 01:28:37 +00008743 }
glennrpfd05d622011-02-25 04:10:33 +00008744 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008745
glennrpfd05d622011-02-25 04:10:33 +00008746 /* If we are excluding the tRNS chunk and there is transparency,
8747 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8748 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008749 */
glennrp0e8ea192010-12-24 18:00:33 +00008750 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8751 (number_transparent != 0 || number_semitransparent != 0))
8752 {
glennrpd17915c2011-04-29 14:24:22 +00008753 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008754
8755 if (ping_have_color == MagickFalse)
8756 mng_info->write_png_colortype = 5;
8757
8758 else
8759 mng_info->write_png_colortype = 7;
8760
glennrp8d579662011-02-23 02:05:02 +00008761 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008762 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008763 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008764
glennrp0e8ea192010-12-24 18:00:33 +00008765 }
8766
glennrpfd05d622011-02-25 04:10:33 +00008767 /* See if cheap transparency is possible. It is only possible
8768 * when there is a single transparent color, no semitransparent
8769 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008770 * as the transparent color. We only need this information if
8771 * we are writing a PNG with colortype 0 or 2, and we have not
8772 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008773 */
glennrp5a39f372011-02-25 04:52:16 +00008774 if (number_transparent == 1 &&
8775 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008776 {
8777 ping_have_cheap_transparency = MagickTrue;
8778
8779 if (number_semitransparent != 0)
8780 ping_have_cheap_transparency = MagickFalse;
8781
8782 else if (image_colors == 0 || image_colors > 256 ||
8783 image->colormap == NULL)
8784 {
8785 ExceptionInfo
8786 *exception;
8787
cristy4c08aed2011-07-01 19:47:50 +00008788 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008789 *q;
8790
8791 exception=(&image->exception);
8792
8793 for (y=0; y < (ssize_t) image->rows; y++)
8794 {
8795 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8796
cristyacd2ed22011-08-30 01:44:23 +00008797 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008798 break;
8799
8800 for (x=0; x < (ssize_t) image->columns; x++)
8801 {
cristy4c08aed2011-07-01 19:47:50 +00008802 if (GetPixelAlpha(image,q) != TransparentAlpha &&
glennrp847370c2011-07-05 17:37:15 +00008803 (unsigned short) GetPixelRed(image,q) ==
8804 ping_trans_color.red &&
8805 (unsigned short) GetPixelGreen(image,q) ==
8806 ping_trans_color.green &&
8807 (unsigned short) GetPixelBlue(image,q) ==
8808 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008809 {
8810 ping_have_cheap_transparency = MagickFalse;
8811 break;
8812 }
8813
cristyed231572011-07-14 02:18:59 +00008814 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008815 }
glennrpbb4f99d2011-05-22 11:13:17 +00008816
glennrpfd05d622011-02-25 04:10:33 +00008817 if (ping_have_cheap_transparency == MagickFalse)
8818 break;
8819 }
8820 }
8821 else
8822 {
glennrp67b9c1a2011-04-22 18:47:36 +00008823 /* Assuming that image->colormap[0] is the one transparent color
8824 * and that all others are opaque.
8825 */
glennrpfd05d622011-02-25 04:10:33 +00008826 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008827 for (i=1; i<image_colors; i++)
8828 if (image->colormap[i].red == image->colormap[0].red &&
8829 image->colormap[i].green == image->colormap[0].green &&
8830 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008831 {
glennrp67b9c1a2011-04-22 18:47:36 +00008832 ping_have_cheap_transparency = MagickFalse;
8833 break;
glennrpfd05d622011-02-25 04:10:33 +00008834 }
8835 }
glennrpbb4f99d2011-05-22 11:13:17 +00008836
glennrpfd05d622011-02-25 04:10:33 +00008837 if (logging != MagickFalse)
8838 {
8839 if (ping_have_cheap_transparency == MagickFalse)
8840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8841 " Cheap transparency is not possible.");
8842
8843 else
8844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8845 " Cheap transparency is possible.");
8846 }
8847 }
8848 else
8849 ping_have_cheap_transparency = MagickFalse;
8850
glennrp8640fb52010-11-23 15:48:26 +00008851 image_depth=image->depth;
8852
glennrp26c990a2010-11-23 02:23:20 +00008853 quantum_info = (QuantumInfo *) NULL;
8854 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008855 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008856 image_matte=image->matte;
8857
glennrp0fe50b42010-11-16 03:52:51 +00008858 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008859 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008860
glennrp52a479c2011-02-26 21:14:38 +00008861 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8862 (image->colors == 0 || image->colormap == NULL))
8863 {
glennrp52a479c2011-02-26 21:14:38 +00008864 image_info=DestroyImageInfo(image_info);
8865 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008866 (void) ThrowMagickException(&IMimage->exception,
8867 GetMagickModule(),CoderError,
8868 "Cannot write PNG8 or color-type 3; colormap is NULL",
8869 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008870#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8871 UnlockSemaphoreInfo(ping_semaphore);
8872#endif
8873 return(MagickFalse);
8874 }
8875
cristy3ed852e2009-09-05 21:47:34 +00008876 /*
8877 Allocate the PNG structures
8878 */
8879#ifdef PNG_USER_MEM_SUPPORTED
8880 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008881 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8882 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008883
cristy3ed852e2009-09-05 21:47:34 +00008884#else
8885 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008886 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008887
cristy3ed852e2009-09-05 21:47:34 +00008888#endif
8889 if (ping == (png_struct *) NULL)
8890 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008891
cristy3ed852e2009-09-05 21:47:34 +00008892 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008893
cristy3ed852e2009-09-05 21:47:34 +00008894 if (ping_info == (png_info *) NULL)
8895 {
8896 png_destroy_write_struct(&ping,(png_info **) NULL);
8897 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8898 }
glennrp0fe50b42010-11-16 03:52:51 +00008899
cristy3ed852e2009-09-05 21:47:34 +00008900 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008901 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008902
glennrp5af765f2010-03-30 11:12:18 +00008903 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008904 {
8905 /*
8906 PNG write failed.
8907 */
8908#ifdef PNG_DEBUG
8909 if (image_info->verbose)
8910 (void) printf("PNG write has failed.\n");
8911#endif
8912 png_destroy_write_struct(&ping,&ping_info);
8913#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008914 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008915#endif
glennrpda8f3a72011-02-27 23:54:12 +00008916 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008917 (void) CloseBlob(image);
8918 image_info=DestroyImageInfo(image_info);
8919 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008920 return(MagickFalse);
8921 }
8922 /*
8923 Prepare PNG for writing.
8924 */
8925#if defined(PNG_MNG_FEATURES_SUPPORTED)
8926 if (mng_info->write_mng)
8927 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008928
cristy3ed852e2009-09-05 21:47:34 +00008929#else
8930# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8931 if (mng_info->write_mng)
8932 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008933
cristy3ed852e2009-09-05 21:47:34 +00008934# endif
8935#endif
glennrp2b013e42010-11-24 16:55:50 +00008936
cristy3ed852e2009-09-05 21:47:34 +00008937 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008938
cristy4e5bc842010-06-09 13:56:01 +00008939 ping_width=(png_uint_32) image->columns;
8940 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008941
cristy3ed852e2009-09-05 21:47:34 +00008942 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8943 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008944
cristy3ed852e2009-09-05 21:47:34 +00008945 if (mng_info->write_png_depth != 0)
8946 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008947
cristy3ed852e2009-09-05 21:47:34 +00008948 /* Adjust requested depth to next higher valid depth if necessary */
8949 if (image_depth > 8)
8950 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008951
cristy3ed852e2009-09-05 21:47:34 +00008952 if ((image_depth > 4) && (image_depth < 8))
8953 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008954
cristy3ed852e2009-09-05 21:47:34 +00008955 if (image_depth == 3)
8956 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008957
cristy3ed852e2009-09-05 21:47:34 +00008958 if (logging != MagickFalse)
8959 {
8960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008961 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008963 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008965 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008967 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008969 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008970 }
glennrp8640fb52010-11-23 15:48:26 +00008971
cristy3ed852e2009-09-05 21:47:34 +00008972 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008973 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008974
glennrp26f37912010-12-23 16:22:42 +00008975
cristy3ed852e2009-09-05 21:47:34 +00008976#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008977 if (ping_exclude_pHYs == MagickFalse)
8978 {
cristy3ed852e2009-09-05 21:47:34 +00008979 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8980 (!mng_info->write_mng || !mng_info->equal_physs))
8981 {
glennrp0fe50b42010-11-16 03:52:51 +00008982 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8984 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008985
8986 if (image->units == PixelsPerInchResolution)
8987 {
glennrpdfd70802010-11-14 01:23:35 +00008988 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008989 ping_pHYs_x_resolution=
8990 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8991 ping_pHYs_y_resolution=
8992 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008993 }
glennrpdfd70802010-11-14 01:23:35 +00008994
cristy3ed852e2009-09-05 21:47:34 +00008995 else if (image->units == PixelsPerCentimeterResolution)
8996 {
glennrpdfd70802010-11-14 01:23:35 +00008997 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008998 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8999 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009000 }
glennrp991d11d2010-11-12 21:55:28 +00009001
cristy3ed852e2009-09-05 21:47:34 +00009002 else
9003 {
glennrpdfd70802010-11-14 01:23:35 +00009004 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9005 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
9006 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00009007 }
glennrp991d11d2010-11-12 21:55:28 +00009008
glennrp823b55c2011-03-14 18:46:46 +00009009 if (logging != MagickFalse)
9010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9011 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9012 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9013 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009014 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009015 }
glennrp26f37912010-12-23 16:22:42 +00009016 }
cristy3ed852e2009-09-05 21:47:34 +00009017#endif
glennrpa521b2f2010-10-29 04:11:03 +00009018
glennrp26f37912010-12-23 16:22:42 +00009019 if (ping_exclude_bKGD == MagickFalse)
9020 {
glennrpa521b2f2010-10-29 04:11:03 +00009021 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009022 {
glennrpa521b2f2010-10-29 04:11:03 +00009023 unsigned int
9024 mask;
cristy3ed852e2009-09-05 21:47:34 +00009025
glennrpa521b2f2010-10-29 04:11:03 +00009026 mask=0xffff;
9027 if (ping_bit_depth == 8)
9028 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009029
glennrpa521b2f2010-10-29 04:11:03 +00009030 if (ping_bit_depth == 4)
9031 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009032
glennrpa521b2f2010-10-29 04:11:03 +00009033 if (ping_bit_depth == 2)
9034 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009035
glennrpa521b2f2010-10-29 04:11:03 +00009036 if (ping_bit_depth == 1)
9037 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009038
glennrpa521b2f2010-10-29 04:11:03 +00009039 ping_background.red=(png_uint_16)
9040 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009041
glennrpa521b2f2010-10-29 04:11:03 +00009042 ping_background.green=(png_uint_16)
9043 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009044
glennrpa521b2f2010-10-29 04:11:03 +00009045 ping_background.blue=(png_uint_16)
9046 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009047
9048 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009049 }
cristy3ed852e2009-09-05 21:47:34 +00009050
glennrp0fe50b42010-11-16 03:52:51 +00009051 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009052 {
9053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9054 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9056 " background_color index is %d",
9057 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009058
9059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9060 " ping_bit_depth=%d",ping_bit_depth);
9061 }
glennrp0fe50b42010-11-16 03:52:51 +00009062
9063 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009064 }
glennrp0fe50b42010-11-16 03:52:51 +00009065
cristy3ed852e2009-09-05 21:47:34 +00009066 /*
9067 Select the color type.
9068 */
9069 matte=image_matte;
9070 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009071
glennrp1273f7b2011-02-24 03:20:30 +00009072 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009073 {
glennrp0fe50b42010-11-16 03:52:51 +00009074
glennrpfd05d622011-02-25 04:10:33 +00009075 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009076 for reducing the sample depth from 8. */
9077
glennrp0fe50b42010-11-16 03:52:51 +00009078 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009079
glennrp8bb3a022010-12-13 20:40:04 +00009080 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009081
9082 /*
9083 Set image palette.
9084 */
9085 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9086
glennrp0fe50b42010-11-16 03:52:51 +00009087 if (logging != MagickFalse)
9088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9089 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009090 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009091
9092 for (i=0; i < (ssize_t) number_colors; i++)
9093 {
9094 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9095 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9096 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9097 if (logging != MagickFalse)
9098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009099#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009100 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009101#else
9102 " %5ld (%5d,%5d,%5d)",
9103#endif
glennrp0fe50b42010-11-16 03:52:51 +00009104 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9105
9106 }
glennrp2b013e42010-11-24 16:55:50 +00009107
glennrp8bb3a022010-12-13 20:40:04 +00009108 ping_have_PLTE=MagickTrue;
9109 image_depth=ping_bit_depth;
9110 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009111
glennrp58e01762011-01-07 15:28:54 +00009112 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009113 {
glennrp0fe50b42010-11-16 03:52:51 +00009114 /*
9115 Identify which colormap entry is transparent.
9116 */
9117 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009118 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009119
glennrp8bb3a022010-12-13 20:40:04 +00009120 for (i=0; i < (ssize_t) number_transparent; i++)
9121 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009122
glennrp0fe50b42010-11-16 03:52:51 +00009123
glennrp2cc891a2010-12-24 13:44:32 +00009124 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009125 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009126
9127 if (ping_num_trans == 0)
9128 ping_have_tRNS=MagickFalse;
9129
glennrp8bb3a022010-12-13 20:40:04 +00009130 else
9131 ping_have_tRNS=MagickTrue;
9132 }
glennrp0fe50b42010-11-16 03:52:51 +00009133
glennrp1273f7b2011-02-24 03:20:30 +00009134 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009135 {
glennrp1273f7b2011-02-24 03:20:30 +00009136 /*
9137 * Identify which colormap entry is the background color.
9138 */
9139
glennrp4f25bd02011-01-01 18:51:28 +00009140 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9141 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9142 break;
glennrp0fe50b42010-11-16 03:52:51 +00009143
glennrp4f25bd02011-01-01 18:51:28 +00009144 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009145
9146 if (logging != MagickFalse)
9147 {
9148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9149 " background_color index is %d",
9150 (int) ping_background.index);
9151 }
glennrp4f25bd02011-01-01 18:51:28 +00009152 }
cristy3ed852e2009-09-05 21:47:34 +00009153 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009154
glennrp7e65e932011-08-19 02:31:16 +00009155 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009156 {
9157 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009158 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009159 }
glennrp0fe50b42010-11-16 03:52:51 +00009160
glennrp7e65e932011-08-19 02:31:16 +00009161 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009162 {
9163 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009164 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009165 }
glennrp0fe50b42010-11-16 03:52:51 +00009166
glennrp8bb3a022010-12-13 20:40:04 +00009167 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009168 {
glennrp5af765f2010-03-30 11:12:18 +00009169 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009170
glennrp8bb3a022010-12-13 20:40:04 +00009171 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009172 {
glennrp5af765f2010-03-30 11:12:18 +00009173 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009174
glennrp5af765f2010-03-30 11:12:18 +00009175 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9176 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009177 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009178
glennrp8bb3a022010-12-13 20:40:04 +00009179 else
9180 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009181
9182 if (logging != MagickFalse)
9183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9184 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009185 }
glennrp0fe50b42010-11-16 03:52:51 +00009186
glennrp7c4c9e62011-03-21 20:23:32 +00009187 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009188 {
9189 if (logging != MagickFalse)
9190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009191 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009192
glennrpd6bf1612010-12-17 17:28:54 +00009193 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009194 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009195
glennrpd6bf1612010-12-17 17:28:54 +00009196 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009197 {
glennrp5af765f2010-03-30 11:12:18 +00009198 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009199 image_matte=MagickFalse;
9200 }
glennrp0fe50b42010-11-16 03:52:51 +00009201
glennrpd6bf1612010-12-17 17:28:54 +00009202 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009203 {
glennrp5af765f2010-03-30 11:12:18 +00009204 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009205 image_matte=MagickTrue;
9206 }
glennrp0fe50b42010-11-16 03:52:51 +00009207
glennrp5aa37f62011-01-02 03:07:57 +00009208 if (image_info->type == PaletteType ||
9209 image_info->type == PaletteMatteType)
9210 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9211
glennrp7c4c9e62011-03-21 20:23:32 +00009212 if (mng_info->write_png_colortype == 0 &&
9213 (image_info->type == UndefinedType ||
9214 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009215 {
glennrp5aa37f62011-01-02 03:07:57 +00009216 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009217 {
glennrp5aa37f62011-01-02 03:07:57 +00009218 if (image_matte == MagickFalse)
9219 {
9220 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9221 image_matte=MagickFalse;
9222 }
glennrp0fe50b42010-11-16 03:52:51 +00009223
glennrp0b206f52011-01-07 04:55:32 +00009224 else
glennrp5aa37f62011-01-02 03:07:57 +00009225 {
9226 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9227 image_matte=MagickTrue;
9228 }
9229 }
9230 else
glennrp8bb3a022010-12-13 20:40:04 +00009231 {
glennrp5aa37f62011-01-02 03:07:57 +00009232 if (image_matte == MagickFalse)
9233 {
9234 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9235 image_matte=MagickFalse;
9236 }
glennrp8bb3a022010-12-13 20:40:04 +00009237
glennrp0b206f52011-01-07 04:55:32 +00009238 else
glennrp5aa37f62011-01-02 03:07:57 +00009239 {
9240 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9241 image_matte=MagickTrue;
9242 }
9243 }
glennrp0fe50b42010-11-16 03:52:51 +00009244 }
glennrp5aa37f62011-01-02 03:07:57 +00009245
cristy3ed852e2009-09-05 21:47:34 +00009246 }
glennrp0fe50b42010-11-16 03:52:51 +00009247
cristy3ed852e2009-09-05 21:47:34 +00009248 if (logging != MagickFalse)
9249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009250 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009251
glennrp5af765f2010-03-30 11:12:18 +00009252 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009253 {
9254 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9255 ping_color_type == PNG_COLOR_TYPE_RGB ||
9256 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9257 ping_bit_depth=8;
9258 }
cristy3ed852e2009-09-05 21:47:34 +00009259
glennrpd6bf1612010-12-17 17:28:54 +00009260 old_bit_depth=ping_bit_depth;
9261
glennrp5af765f2010-03-30 11:12:18 +00009262 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009263 {
glennrp8d579662011-02-23 02:05:02 +00009264 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9265 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009266 }
glennrp8640fb52010-11-23 15:48:26 +00009267
glennrp5af765f2010-03-30 11:12:18 +00009268 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009269 {
cristy35ef8242010-06-03 16:24:13 +00009270 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009271 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009272
9273 if (image->colors == 0)
9274 {
glennrp0fe50b42010-11-16 03:52:51 +00009275 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00009276 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00009277 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009278 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009279 }
9280
cristy35ef8242010-06-03 16:24:13 +00009281 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009282 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009283 }
glennrp2b013e42010-11-24 16:55:50 +00009284
glennrpd6bf1612010-12-17 17:28:54 +00009285 if (logging != MagickFalse)
9286 {
9287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9288 " Number of colors: %.20g",(double) image_colors);
9289
9290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9291 " Tentative PNG bit depth: %d",ping_bit_depth);
9292 }
9293
9294 if (ping_bit_depth < (int) mng_info->write_png_depth)
9295 ping_bit_depth = mng_info->write_png_depth;
9296 }
glennrp2cc891a2010-12-24 13:44:32 +00009297
glennrp5af765f2010-03-30 11:12:18 +00009298 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009299
cristy3ed852e2009-09-05 21:47:34 +00009300 if (logging != MagickFalse)
9301 {
9302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009303 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009304
cristy3ed852e2009-09-05 21:47:34 +00009305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009306 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009307
cristy3ed852e2009-09-05 21:47:34 +00009308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009309 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009310
cristy3ed852e2009-09-05 21:47:34 +00009311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009312
glennrp8640fb52010-11-23 15:48:26 +00009313 " image->depth: %.20g",(double) image->depth);
9314
9315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009316 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009317 }
9318
glennrp58e01762011-01-07 15:28:54 +00009319 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009320 {
glennrp4f25bd02011-01-01 18:51:28 +00009321 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009322 {
glennrp7c4c9e62011-03-21 20:23:32 +00009323 if (mng_info->write_png_colortype == 0)
9324 {
9325 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009326
glennrp7c4c9e62011-03-21 20:23:32 +00009327 if (ping_have_color != MagickFalse)
9328 ping_color_type=PNG_COLOR_TYPE_RGBA;
9329 }
glennrp4f25bd02011-01-01 18:51:28 +00009330
9331 /*
9332 * Determine if there is any transparent color.
9333 */
9334 if (number_transparent + number_semitransparent == 0)
9335 {
9336 /*
9337 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9338 */
glennrpa6a06632011-01-19 15:15:34 +00009339
glennrp4f25bd02011-01-01 18:51:28 +00009340 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009341
9342 if (mng_info->write_png_colortype == 0)
9343 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009344 }
9345
9346 else
9347 {
9348 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009349 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009350
9351 mask=0xffff;
9352
9353 if (ping_bit_depth == 8)
9354 mask=0x00ff;
9355
9356 if (ping_bit_depth == 4)
9357 mask=0x000f;
9358
9359 if (ping_bit_depth == 2)
9360 mask=0x0003;
9361
9362 if (ping_bit_depth == 1)
9363 mask=0x0001;
9364
9365 ping_trans_color.red=(png_uint_16)
9366 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9367
9368 ping_trans_color.green=(png_uint_16)
9369 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9370
9371 ping_trans_color.blue=(png_uint_16)
9372 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9373
9374 ping_trans_color.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009375 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009376 image->colormap)) & mask);
9377
9378 ping_trans_color.index=(png_byte) 0;
9379
9380 ping_have_tRNS=MagickTrue;
9381 }
9382
9383 if (ping_have_tRNS != MagickFalse)
9384 {
9385 /*
glennrpfd05d622011-02-25 04:10:33 +00009386 * Determine if there is one and only one transparent color
9387 * and if so if it is fully transparent.
9388 */
9389 if (ping_have_cheap_transparency == MagickFalse)
9390 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009391 }
9392
9393 if (ping_have_tRNS != MagickFalse)
9394 {
glennrp7c4c9e62011-03-21 20:23:32 +00009395 if (mng_info->write_png_colortype == 0)
9396 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009397
9398 if (image_depth == 8)
9399 {
9400 ping_trans_color.red&=0xff;
9401 ping_trans_color.green&=0xff;
9402 ping_trans_color.blue&=0xff;
9403 ping_trans_color.gray&=0xff;
9404 }
9405 }
9406 }
cristy3ed852e2009-09-05 21:47:34 +00009407 else
9408 {
cristy3ed852e2009-09-05 21:47:34 +00009409 if (image_depth == 8)
9410 {
glennrp5af765f2010-03-30 11:12:18 +00009411 ping_trans_color.red&=0xff;
9412 ping_trans_color.green&=0xff;
9413 ping_trans_color.blue&=0xff;
9414 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009415 }
9416 }
9417 }
glennrp8640fb52010-11-23 15:48:26 +00009418
cristy3ed852e2009-09-05 21:47:34 +00009419 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009420
glennrp2e09f552010-11-14 00:38:48 +00009421 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009422 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009423
glennrp39992b42010-11-14 00:03:43 +00009424 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009425 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009426 ping_have_color == MagickFalse &&
9427 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009428 {
cristy35ef8242010-06-03 16:24:13 +00009429 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009430
cristy3ed852e2009-09-05 21:47:34 +00009431 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009432 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009433
glennrp7c4c9e62011-03-21 20:23:32 +00009434 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009435 {
glennrp5af765f2010-03-30 11:12:18 +00009436 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009437
cristy3ed852e2009-09-05 21:47:34 +00009438 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009439 {
9440 if (logging != MagickFalse)
9441 {
9442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9443 " Scaling ping_trans_color (0)");
9444 }
9445 ping_trans_color.gray*=0x0101;
9446 }
cristy3ed852e2009-09-05 21:47:34 +00009447 }
glennrp0fe50b42010-11-16 03:52:51 +00009448
cristy3ed852e2009-09-05 21:47:34 +00009449 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9450 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009451
glennrp136ee3a2011-04-27 15:47:45 +00009452 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009453 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009454 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009455
cristy3ed852e2009-09-05 21:47:34 +00009456 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009457 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009458
cristy3ed852e2009-09-05 21:47:34 +00009459 else
9460 {
glennrp5af765f2010-03-30 11:12:18 +00009461 ping_bit_depth=8;
9462 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009463 {
9464 if(!mng_info->write_png_depth)
9465 {
glennrp5af765f2010-03-30 11:12:18 +00009466 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009467
cristy35ef8242010-06-03 16:24:13 +00009468 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009469 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009470 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009471 }
9472 }
glennrp2b013e42010-11-24 16:55:50 +00009473
glennrp0fe50b42010-11-16 03:52:51 +00009474 else if (ping_color_type ==
9475 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009476 mng_info->IsPalette)
9477 {
cristy3ed852e2009-09-05 21:47:34 +00009478 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009479
cristy3ed852e2009-09-05 21:47:34 +00009480 int
9481 depth_4_ok=MagickTrue,
9482 depth_2_ok=MagickTrue,
9483 depth_1_ok=MagickTrue;
9484
cristybb503372010-05-27 20:51:26 +00009485 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009486 {
9487 unsigned char
9488 intensity;
9489
9490 intensity=ScaleQuantumToChar(image->colormap[i].red);
9491
9492 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9493 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9494 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9495 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009496 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009497 depth_1_ok=MagickFalse;
9498 }
glennrp2b013e42010-11-24 16:55:50 +00009499
cristy3ed852e2009-09-05 21:47:34 +00009500 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009501 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009502
cristy3ed852e2009-09-05 21:47:34 +00009503 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009504 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009505
cristy3ed852e2009-09-05 21:47:34 +00009506 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009507 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009508 }
9509 }
glennrp2b013e42010-11-24 16:55:50 +00009510
glennrp5af765f2010-03-30 11:12:18 +00009511 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009512 }
glennrp0fe50b42010-11-16 03:52:51 +00009513
cristy3ed852e2009-09-05 21:47:34 +00009514 else
glennrp0fe50b42010-11-16 03:52:51 +00009515
cristy3ed852e2009-09-05 21:47:34 +00009516 if (mng_info->IsPalette)
9517 {
glennrp17a14852010-05-10 03:01:59 +00009518 number_colors=image_colors;
9519
cristy3ed852e2009-09-05 21:47:34 +00009520 if (image_depth <= 8)
9521 {
cristy3ed852e2009-09-05 21:47:34 +00009522 /*
9523 Set image palette.
9524 */
glennrp5af765f2010-03-30 11:12:18 +00009525 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009526
glennrp58e01762011-01-07 15:28:54 +00009527 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009528 {
glennrp9c1eb072010-06-06 22:19:15 +00009529 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009530
glennrp3b51f0e2010-11-27 18:14:08 +00009531 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9533 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009534 }
glennrp0fe50b42010-11-16 03:52:51 +00009535
cristy3ed852e2009-09-05 21:47:34 +00009536 else
9537 {
cristybb503372010-05-27 20:51:26 +00009538 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009539 {
9540 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9541 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9542 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9543 }
glennrp0fe50b42010-11-16 03:52:51 +00009544
glennrp3b51f0e2010-11-27 18:14:08 +00009545 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009547 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009548 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009549
glennrp39992b42010-11-14 00:03:43 +00009550 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009551 }
glennrp0fe50b42010-11-16 03:52:51 +00009552
cristy3ed852e2009-09-05 21:47:34 +00009553 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009554 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009555 {
cristybefe4d22010-06-07 01:18:58 +00009556 size_t
9557 one;
9558
glennrp5af765f2010-03-30 11:12:18 +00009559 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009560 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009561
cristy94b11832011-09-08 19:46:03 +00009562 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009563 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009564 }
glennrp0fe50b42010-11-16 03:52:51 +00009565
glennrp5af765f2010-03-30 11:12:18 +00009566 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009567
glennrp58e01762011-01-07 15:28:54 +00009568 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009569 {
glennrp0fe50b42010-11-16 03:52:51 +00009570 /*
glennrpd6bf1612010-12-17 17:28:54 +00009571 * Set up trans_colors array.
9572 */
glennrp0fe50b42010-11-16 03:52:51 +00009573 assert(number_colors <= 256);
9574
glennrpd6bf1612010-12-17 17:28:54 +00009575 ping_num_trans=(unsigned short) (number_transparent +
9576 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009577
9578 if (ping_num_trans == 0)
9579 ping_have_tRNS=MagickFalse;
9580
glennrpd6bf1612010-12-17 17:28:54 +00009581 else
glennrp0fe50b42010-11-16 03:52:51 +00009582 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009583 if (logging != MagickFalse)
9584 {
9585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9586 " Scaling ping_trans_color (1)");
9587 }
glennrpd6bf1612010-12-17 17:28:54 +00009588 ping_have_tRNS=MagickTrue;
9589
9590 for (i=0; i < ping_num_trans; i++)
9591 {
cristy4c08aed2011-07-01 19:47:50 +00009592 ping_trans_alpha[i]= (png_byte)
9593 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009594 }
glennrp0fe50b42010-11-16 03:52:51 +00009595 }
9596 }
cristy3ed852e2009-09-05 21:47:34 +00009597 }
9598 }
glennrp0fe50b42010-11-16 03:52:51 +00009599
cristy3ed852e2009-09-05 21:47:34 +00009600 else
9601 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009602
cristy3ed852e2009-09-05 21:47:34 +00009603 if (image_depth < 8)
9604 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009605
cristy3ed852e2009-09-05 21:47:34 +00009606 if ((save_image_depth == 16) && (image_depth == 8))
9607 {
glennrp4f25bd02011-01-01 18:51:28 +00009608 if (logging != MagickFalse)
9609 {
9610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9611 " Scaling ping_trans_color from (%d,%d,%d)",
9612 (int) ping_trans_color.red,
9613 (int) ping_trans_color.green,
9614 (int) ping_trans_color.blue);
9615 }
9616
glennrp5af765f2010-03-30 11:12:18 +00009617 ping_trans_color.red*=0x0101;
9618 ping_trans_color.green*=0x0101;
9619 ping_trans_color.blue*=0x0101;
9620 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009621
9622 if (logging != MagickFalse)
9623 {
9624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9625 " to (%d,%d,%d)",
9626 (int) ping_trans_color.red,
9627 (int) ping_trans_color.green,
9628 (int) ping_trans_color.blue);
9629 }
cristy3ed852e2009-09-05 21:47:34 +00009630 }
9631 }
9632
cristy4383ec82011-01-05 15:42:32 +00009633 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9634 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009635
cristy3ed852e2009-09-05 21:47:34 +00009636 /*
9637 Adjust background and transparency samples in sub-8-bit grayscale files.
9638 */
glennrp5af765f2010-03-30 11:12:18 +00009639 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009640 PNG_COLOR_TYPE_GRAY)
9641 {
9642 png_uint_16
9643 maxval;
9644
cristy35ef8242010-06-03 16:24:13 +00009645 size_t
9646 one=1;
9647
cristy22ffd972010-06-03 16:51:47 +00009648 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009649
glennrp4f25bd02011-01-01 18:51:28 +00009650 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009651 {
cristy3ed852e2009-09-05 21:47:34 +00009652
glennrpa521b2f2010-10-29 04:11:03 +00009653 ping_background.gray=(png_uint_16)
cristy101ab702011-10-13 13:06:32 +00009654 ((maxval/255.)*((GetPixelInfoIntensity(&image->background_color)))
glennrp847370c2011-07-05 17:37:15 +00009655 +.5);
cristy3ed852e2009-09-05 21:47:34 +00009656
9657 if (logging != MagickFalse)
9658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009659 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9661 " background_color index is %d",
9662 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009663
glennrp991d11d2010-11-12 21:55:28 +00009664 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009665 }
cristy3ed852e2009-09-05 21:47:34 +00009666
glennrp3e3e20f2011-06-09 04:21:43 +00009667 if (logging != MagickFalse)
9668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9669 " Scaling ping_trans_color.gray from %d",
9670 (int)ping_trans_color.gray);
9671
glennrp9be9b1c2011-06-09 12:21:45 +00009672 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009673 ping_trans_color.gray)+.5);
9674
9675 if (logging != MagickFalse)
9676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9677 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009678 }
glennrp17a14852010-05-10 03:01:59 +00009679
glennrp26f37912010-12-23 16:22:42 +00009680 if (ping_exclude_bKGD == MagickFalse)
9681 {
glennrp1273f7b2011-02-24 03:20:30 +00009682 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009683 {
9684 /*
9685 Identify which colormap entry is the background color.
9686 */
9687
glennrp17a14852010-05-10 03:01:59 +00009688 number_colors=image_colors;
9689
glennrpa521b2f2010-10-29 04:11:03 +00009690 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9691 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009692 break;
9693
9694 ping_background.index=(png_byte) i;
9695
glennrp3b51f0e2010-11-27 18:14:08 +00009696 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009697 {
9698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009699 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009700 }
glennrp0fe50b42010-11-16 03:52:51 +00009701
cristy13d07042010-11-21 20:56:18 +00009702 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009703 {
9704 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009705
9706 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009707 {
9708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9709 " background =(%d,%d,%d)",
9710 (int) ping_background.red,
9711 (int) ping_background.green,
9712 (int) ping_background.blue);
9713 }
9714 }
glennrpa521b2f2010-10-29 04:11:03 +00009715
glennrpd6bf1612010-12-17 17:28:54 +00009716 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009717 {
glennrp3b51f0e2010-11-27 18:14:08 +00009718 if (logging != MagickFalse)
9719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9720 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009721 ping_have_bKGD = MagickFalse;
9722 }
glennrp17a14852010-05-10 03:01:59 +00009723 }
glennrp26f37912010-12-23 16:22:42 +00009724 }
glennrp17a14852010-05-10 03:01:59 +00009725
cristy3ed852e2009-09-05 21:47:34 +00009726 if (logging != MagickFalse)
9727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009728 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009729 /*
9730 Initialize compression level and filtering.
9731 */
9732 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009733 {
9734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9735 " Setting up deflate compression");
9736
9737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9738 " Compression buffer size: 32768");
9739 }
9740
cristy3ed852e2009-09-05 21:47:34 +00009741 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009742
cristy3ed852e2009-09-05 21:47:34 +00009743 if (logging != MagickFalse)
9744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9745 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009746
cristy4054bfb2011-08-29 23:41:39 +00009747 png_set_compression_mem_level(ping, 9);
9748
glennrp10d739e2011-06-29 18:00:52 +00009749 /* Untangle the "-quality" setting:
9750
9751 Undefined is 0; the default is used.
9752 Default is 75
9753
9754 10's digit:
9755
9756 0: Use Z_HUFFMAN_ONLY strategy with the
9757 zlib default compression level
9758
9759 1-9: the zlib compression level
9760
9761 1's digit:
9762
9763 0-4: the PNG filter method
9764
9765 5: libpng adaptive filtering if compression level > 5
9766 libpng filter type "none" if compression level <= 5
9767 or if image is grayscale or palette
9768
9769 6: libpng adaptive filtering
9770
9771 7: "LOCO" filtering (intrapixel differing) if writing
9772 a MNG, othewise "none". Did not work in IM-6.7.0-9
9773 and earlier because of a missing "else".
9774
9775 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009776 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009777
9778 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009779 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009780
9781 Note that using the -quality option, not all combinations of
9782 PNG filter type, zlib compression level, and zlib compression
cristy5e6be1e2011-07-16 01:23:39 +00009783 strategy are possible. This will be addressed soon in a
cristy4054bfb2011-08-29 23:41:39 +00009784 release that accomodates "-define PNG:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009785
9786 */
9787
cristy3ed852e2009-09-05 21:47:34 +00009788 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9789 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009790
glennrp18682582011-06-30 18:11:47 +00009791 if (quality <= 9)
9792 {
9793 if (mng_info->write_png_compression_strategy == 0)
9794 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9795 }
9796
9797 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009798 {
9799 int
9800 level;
9801
cristybb503372010-05-27 20:51:26 +00009802 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009803
glennrp18682582011-06-30 18:11:47 +00009804 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009805 }
glennrp0fe50b42010-11-16 03:52:51 +00009806
glennrp18682582011-06-30 18:11:47 +00009807 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009808 {
glennrp18682582011-06-30 18:11:47 +00009809 if ((quality %10) == 8 || (quality %10) == 9)
9810 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009811 }
glennrp0fe50b42010-11-16 03:52:51 +00009812
glennrp18682582011-06-30 18:11:47 +00009813 if (mng_info->write_png_compression_filter == 0)
9814 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9815
cristy3ed852e2009-09-05 21:47:34 +00009816 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009817 {
glennrp18682582011-06-30 18:11:47 +00009818 if (mng_info->write_png_compression_level)
9819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9820 " Compression level: %d",
9821 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009822
glennrp18682582011-06-30 18:11:47 +00009823 if (mng_info->write_png_compression_strategy)
9824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9825 " Compression strategy: %d",
9826 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009827
glennrp18682582011-06-30 18:11:47 +00009828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9829 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009830
cristy4054bfb2011-08-29 23:41:39 +00009831 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9833 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009834 else if (mng_info->write_png_compression_filter == 0 ||
9835 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9837 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009838 else
9839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9840 " Base filter method: %d",
9841 (int) mng_info->write_png_compression_filter-1);
9842 }
glennrp2b013e42010-11-24 16:55:50 +00009843
glennrp18682582011-06-30 18:11:47 +00009844 if (mng_info->write_png_compression_level != 0)
9845 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9846
9847 if (mng_info->write_png_compression_filter == 6)
9848 {
9849 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9850 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9851 (quality < 50))
9852 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9853 else
9854 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9855 }
cristy4054bfb2011-08-29 23:41:39 +00009856 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009857 mng_info->write_png_compression_filter == 10)
9858 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9859
9860 else if (mng_info->write_png_compression_filter == 8)
9861 {
9862#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9863 if (mng_info->write_mng)
9864 {
9865 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9866 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9867 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9868 }
9869#endif
cristy4054bfb2011-08-29 23:41:39 +00009870 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009871 }
9872
9873 else if (mng_info->write_png_compression_filter == 9)
9874 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9875
9876 else if (mng_info->write_png_compression_filter != 0)
9877 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9878 mng_info->write_png_compression_filter-1);
9879
9880 if (mng_info->write_png_compression_strategy != 0)
9881 png_set_compression_strategy(ping,
9882 mng_info->write_png_compression_strategy-1);
9883
cristy0d57eec2011-09-04 22:13:56 +00009884 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9885 if (ping_exclude_sRGB != MagickFalse ||
9886 (image->rendering_intent == UndefinedIntent))
9887 {
9888 if ((ping_exclude_tEXt == MagickFalse ||
9889 ping_exclude_zTXt == MagickFalse) &&
9890 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009891 {
9892 ResetImageProfileIterator(image);
9893 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009894 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009895 profile=GetImageProfile(image,name);
9896
9897 if (profile != (StringInfo *) NULL)
9898 {
glennrp5af765f2010-03-30 11:12:18 +00009899#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009900 if ((LocaleCompare(name,"ICC") == 0) ||
9901 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009902 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009903
9904 if (ping_exclude_iCCP == MagickFalse)
9905 {
cristy9f027d12011-09-21 01:17:17 +00009906 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009907#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009908 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009909#else
9910 (png_const_bytep) GetStringInfoDatum(profile),
9911#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009912 (png_uint_32) GetStringInfoLength(profile));
9913 }
glennrp26f37912010-12-23 16:22:42 +00009914 }
glennrp0fe50b42010-11-16 03:52:51 +00009915
glennrpc8cbc5d2011-01-01 00:12:34 +00009916 else
cristy3ed852e2009-09-05 21:47:34 +00009917#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009918 if (ping_exclude_zCCP == MagickFalse)
9919 {
glennrpcf002022011-01-30 02:38:15 +00009920 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009921 (unsigned char *) name,(unsigned char *) name,
9922 GetStringInfoDatum(profile),
9923 (png_uint_32) GetStringInfoLength(profile));
9924 }
9925 }
glennrp0b206f52011-01-07 04:55:32 +00009926
glennrpc8cbc5d2011-01-01 00:12:34 +00009927 if (logging != MagickFalse)
9928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9929 " Setting up text chunk with %s profile",name);
9930
9931 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009932 }
cristy0d57eec2011-09-04 22:13:56 +00009933 }
cristy3ed852e2009-09-05 21:47:34 +00009934 }
9935
9936#if defined(PNG_WRITE_sRGB_SUPPORTED)
9937 if ((mng_info->have_write_global_srgb == 0) &&
9938 ((image->rendering_intent != UndefinedIntent) ||
9939 (image->colorspace == sRGBColorspace)))
9940 {
glennrp26f37912010-12-23 16:22:42 +00009941 if (ping_exclude_sRGB == MagickFalse)
9942 {
9943 /*
9944 Note image rendering intent.
9945 */
9946 if (logging != MagickFalse)
9947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9948 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009949
glennrp26f37912010-12-23 16:22:42 +00009950 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009951 Magick_RenderingIntent_to_PNG_RenderingIntent(
9952 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009953 }
cristy3ed852e2009-09-05 21:47:34 +00009954 }
glennrp26f37912010-12-23 16:22:42 +00009955
glennrp5af765f2010-03-30 11:12:18 +00009956 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009957#endif
9958 {
glennrp2cc891a2010-12-24 13:44:32 +00009959 if (ping_exclude_gAMA == MagickFalse &&
9960 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009961 (image->gamma < .45 || image->gamma > .46)))
9962 {
cristy3ed852e2009-09-05 21:47:34 +00009963 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9964 {
9965 /*
9966 Note image gamma.
9967 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9968 */
9969 if (logging != MagickFalse)
9970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9971 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009972
cristy3ed852e2009-09-05 21:47:34 +00009973 png_set_gAMA(ping,ping_info,image->gamma);
9974 }
glennrp26f37912010-12-23 16:22:42 +00009975 }
glennrp2b013e42010-11-24 16:55:50 +00009976
glennrp26f37912010-12-23 16:22:42 +00009977 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009978 {
glennrp26f37912010-12-23 16:22:42 +00009979 if ((mng_info->have_write_global_chrm == 0) &&
9980 (image->chromaticity.red_primary.x != 0.0))
9981 {
9982 /*
9983 Note image chromaticity.
9984 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9985 */
9986 PrimaryInfo
9987 bp,
9988 gp,
9989 rp,
9990 wp;
cristy3ed852e2009-09-05 21:47:34 +00009991
glennrp26f37912010-12-23 16:22:42 +00009992 wp=image->chromaticity.white_point;
9993 rp=image->chromaticity.red_primary;
9994 gp=image->chromaticity.green_primary;
9995 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009996
glennrp26f37912010-12-23 16:22:42 +00009997 if (logging != MagickFalse)
9998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9999 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010000
glennrp26f37912010-12-23 16:22:42 +000010001 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10002 bp.x,bp.y);
10003 }
10004 }
cristy3ed852e2009-09-05 21:47:34 +000010005 }
glennrpdfd70802010-11-14 01:23:35 +000010006
glennrp5af765f2010-03-30 11:12:18 +000010007 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010008
10009 if (mng_info->write_mng)
10010 png_set_sig_bytes(ping,8);
10011
10012 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
10013
glennrpd6bf1612010-12-17 17:28:54 +000010014 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010015 {
10016 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010017 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010018 {
glennrp5af765f2010-03-30 11:12:18 +000010019 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010020
glennrp5af765f2010-03-30 11:12:18 +000010021 if (ping_bit_depth < 8)
10022 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010023 }
glennrp0fe50b42010-11-16 03:52:51 +000010024
cristy3ed852e2009-09-05 21:47:34 +000010025 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010026 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010027 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010028 }
10029
glennrp0e8ea192010-12-24 18:00:33 +000010030 if (ping_need_colortype_warning != MagickFalse ||
10031 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010032 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010033 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010034 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010035 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010036 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010037 {
10038 if (logging != MagickFalse)
10039 {
glennrp0e8ea192010-12-24 18:00:33 +000010040 if (ping_need_colortype_warning != MagickFalse)
10041 {
10042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10043 " Image has transparency but tRNS chunk was excluded");
10044 }
10045
cristy3ed852e2009-09-05 21:47:34 +000010046 if (mng_info->write_png_depth)
10047 {
10048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10049 " Defined PNG:bit-depth=%u, Computed depth=%u",
10050 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010051 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010052 }
glennrp0e8ea192010-12-24 18:00:33 +000010053
cristy3ed852e2009-09-05 21:47:34 +000010054 if (mng_info->write_png_colortype)
10055 {
10056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10057 " Defined PNG:color-type=%u, Computed color type=%u",
10058 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010059 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010060 }
10061 }
glennrp0e8ea192010-12-24 18:00:33 +000010062
glennrp3bd2e412010-08-10 13:34:52 +000010063 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +000010064 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
10065 }
10066
glennrp58e01762011-01-07 15:28:54 +000010067 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010068 {
10069 /* Add an opaque matte channel */
10070 image->matte = MagickTrue;
cristye941a752011-10-15 01:52:48 +000010071 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010072
glennrpb4a13412010-05-05 12:47:19 +000010073 if (logging != MagickFalse)
10074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10075 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010076 }
10077
glennrp0e319732011-01-25 21:53:13 +000010078 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010079 {
glennrp991d11d2010-11-12 21:55:28 +000010080 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010081 {
glennrp991d11d2010-11-12 21:55:28 +000010082 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010083 if (logging != MagickFalse)
10084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10085 " Setting ping_have_tRNS=MagickTrue.");
10086 }
glennrpe9c26dc2010-05-30 01:56:35 +000010087 }
10088
cristy3ed852e2009-09-05 21:47:34 +000010089 if (logging != MagickFalse)
10090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10091 " Writing PNG header chunks");
10092
glennrp5af765f2010-03-30 11:12:18 +000010093 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10094 ping_bit_depth,ping_color_type,
10095 ping_interlace_method,ping_compression_method,
10096 ping_filter_method);
10097
glennrp39992b42010-11-14 00:03:43 +000010098 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10099 {
glennrpf09bded2011-01-08 01:15:59 +000010100 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010101
glennrp3b51f0e2010-11-27 18:14:08 +000010102 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010103 {
glennrp8640fb52010-11-23 15:48:26 +000010104 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010105 {
glennrpd6bf1612010-12-17 17:28:54 +000010106 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010108 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10109 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010110 (int) palette[i].red,
10111 (int) palette[i].green,
10112 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010113 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010114 (int) ping_trans_alpha[i]);
10115 else
10116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010117 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010118 (int) i,
10119 (int) palette[i].red,
10120 (int) palette[i].green,
10121 (int) palette[i].blue);
10122 }
glennrp39992b42010-11-14 00:03:43 +000010123 }
glennrp39992b42010-11-14 00:03:43 +000010124 }
10125
glennrp26f37912010-12-23 16:22:42 +000010126 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010127 {
glennrp26f37912010-12-23 16:22:42 +000010128 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010129 {
glennrp26f37912010-12-23 16:22:42 +000010130 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010131 if (logging)
10132 {
10133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10134 " Setting up bKGD chunk");
10135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10136 " background color = (%d,%d,%d)",
10137 (int) ping_background.red,
10138 (int) ping_background.green,
10139 (int) ping_background.blue);
10140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10141 " index = %d, gray=%d",
10142 (int) ping_background.index,
10143 (int) ping_background.gray);
10144 }
10145 }
glennrp26f37912010-12-23 16:22:42 +000010146 }
10147
10148 if (ping_exclude_pHYs == MagickFalse)
10149 {
10150 if (ping_have_pHYs != MagickFalse)
10151 {
10152 png_set_pHYs(ping,ping_info,
10153 ping_pHYs_x_resolution,
10154 ping_pHYs_y_resolution,
10155 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010156
10157 if (logging)
10158 {
10159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10160 " Setting up pHYs chunk");
10161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10162 " x_resolution=%lu",
10163 (unsigned long) ping_pHYs_x_resolution);
10164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10165 " y_resolution=%lu",
10166 (unsigned long) ping_pHYs_y_resolution);
10167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10168 " unit_type=%lu",
10169 (unsigned long) ping_pHYs_unit_type);
10170 }
glennrp26f37912010-12-23 16:22:42 +000010171 }
glennrpdfd70802010-11-14 01:23:35 +000010172 }
10173
10174#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010175 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010176 {
glennrp26f37912010-12-23 16:22:42 +000010177 if (image->page.x || image->page.y)
10178 {
10179 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10180 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010181
glennrp26f37912010-12-23 16:22:42 +000010182 if (logging != MagickFalse)
10183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10184 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10185 (int) image->page.x, (int) image->page.y);
10186 }
glennrpdfd70802010-11-14 01:23:35 +000010187 }
10188#endif
10189
glennrpda8f3a72011-02-27 23:54:12 +000010190 if (mng_info->need_blob != MagickFalse)
10191 {
10192 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10193 MagickFalse)
10194 png_error(ping,"WriteBlob Failed");
10195
10196 ping_have_blob=MagickTrue;
10197 }
10198
cristy3ed852e2009-09-05 21:47:34 +000010199 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010200
glennrp39992b42010-11-14 00:03:43 +000010201 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010202 {
glennrp3b51f0e2010-11-27 18:14:08 +000010203 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010204 {
10205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10206 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10207 }
10208
10209 if (ping_color_type == 3)
10210 (void) png_set_tRNS(ping, ping_info,
10211 ping_trans_alpha,
10212 ping_num_trans,
10213 NULL);
10214
10215 else
10216 {
10217 (void) png_set_tRNS(ping, ping_info,
10218 NULL,
10219 0,
10220 &ping_trans_color);
10221
glennrp3b51f0e2010-11-27 18:14:08 +000010222 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010223 {
10224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010225 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010226 (int) ping_trans_color.red,
10227 (int) ping_trans_color.green,
10228 (int) ping_trans_color.blue);
10229 }
10230 }
glennrp991d11d2010-11-12 21:55:28 +000010231 }
10232
cristy3ed852e2009-09-05 21:47:34 +000010233 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010234 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010235
cristy3ed852e2009-09-05 21:47:34 +000010236 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010237
cristy3ed852e2009-09-05 21:47:34 +000010238 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010239 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010240
glennrp26f37912010-12-23 16:22:42 +000010241 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010242 {
glennrp4f25bd02011-01-01 18:51:28 +000010243 if ((image->page.width != 0 && image->page.width != image->columns) ||
10244 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010245 {
10246 unsigned char
10247 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010248
glennrp26f37912010-12-23 16:22:42 +000010249 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10250 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010251 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010252 PNGLong(chunk+4,(png_uint_32) image->page.width);
10253 PNGLong(chunk+8,(png_uint_32) image->page.height);
10254 chunk[12]=0; /* unit = pixels */
10255 (void) WriteBlob(image,13,chunk);
10256 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10257 }
cristy3ed852e2009-09-05 21:47:34 +000010258 }
10259
10260#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010261 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010262#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010263 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010264#undef PNG_HAVE_IDAT
10265#endif
10266
10267 png_set_packing(ping);
10268 /*
10269 Allocate memory.
10270 */
10271 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010272 if (image_depth > 8)
10273 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010274 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010275 {
glennrpb4a13412010-05-05 12:47:19 +000010276 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010277 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010278 break;
glennrp0fe50b42010-11-16 03:52:51 +000010279
glennrpb4a13412010-05-05 12:47:19 +000010280 case PNG_COLOR_TYPE_GRAY_ALPHA:
10281 rowbytes*=2;
10282 break;
glennrp0fe50b42010-11-16 03:52:51 +000010283
glennrpb4a13412010-05-05 12:47:19 +000010284 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010285 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010286 break;
glennrp0fe50b42010-11-16 03:52:51 +000010287
glennrpb4a13412010-05-05 12:47:19 +000010288 default:
10289 break;
cristy3ed852e2009-09-05 21:47:34 +000010290 }
glennrp3b51f0e2010-11-27 18:14:08 +000010291
10292 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010293 {
10294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10295 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010296
glennrpb4a13412010-05-05 12:47:19 +000010297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010298 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010299 }
glennrpcf002022011-01-30 02:38:15 +000010300 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10301 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010302
glennrpcf002022011-01-30 02:38:15 +000010303 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010304 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010305
cristy3ed852e2009-09-05 21:47:34 +000010306 /*
10307 Initialize image scanlines.
10308 */
glennrp5af765f2010-03-30 11:12:18 +000010309 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010310 {
10311 /*
10312 PNG write failed.
10313 */
10314#ifdef PNG_DEBUG
10315 if (image_info->verbose)
10316 (void) printf("PNG write has failed.\n");
10317#endif
10318 png_destroy_write_struct(&ping,&ping_info);
10319 if (quantum_info != (QuantumInfo *) NULL)
10320 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010321 if (ping_pixels != (unsigned char *) NULL)
10322 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010323#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010324 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010325#endif
glennrpda8f3a72011-02-27 23:54:12 +000010326 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010327 (void) CloseBlob(image);
10328 image_info=DestroyImageInfo(image_info);
10329 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010330 return(MagickFalse);
10331 }
cristyed552522009-10-16 14:04:35 +000010332 quantum_info=AcquireQuantumInfo(image_info,image);
10333 if (quantum_info == (QuantumInfo *) NULL)
10334 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010335 quantum_info->format=UndefinedQuantumFormat;
10336 quantum_info->depth=image_depth;
10337 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010338
cristy3ed852e2009-09-05 21:47:34 +000010339 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010340 !mng_info->write_png32) &&
10341 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010342 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010343 image_matte == MagickFalse &&
10344 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010345 {
glennrp8bb3a022010-12-13 20:40:04 +000010346 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010347 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010348 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010349
cristy3ed852e2009-09-05 21:47:34 +000010350 quantum_info->depth=8;
10351 for (pass=0; pass < num_passes; pass++)
10352 {
10353 /*
10354 Convert PseudoClass image to a PNG monochrome image.
10355 */
cristybb503372010-05-27 20:51:26 +000010356 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010357 {
glennrpd71e86a2011-02-24 01:28:37 +000010358 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10360 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010361
cristy3ed852e2009-09-05 21:47:34 +000010362 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010363
cristy4c08aed2011-07-01 19:47:50 +000010364 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010365 break;
glennrp0fe50b42010-11-16 03:52:51 +000010366
cristy3ed852e2009-09-05 21:47:34 +000010367 if (mng_info->IsPalette)
10368 {
cristy4c08aed2011-07-01 19:47:50 +000010369 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010370 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010371 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10372 mng_info->write_png_depth &&
10373 mng_info->write_png_depth != old_bit_depth)
10374 {
10375 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010376 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010377 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010378 >> (8-old_bit_depth));
10379 }
10380 }
glennrp0fe50b42010-11-16 03:52:51 +000010381
cristy3ed852e2009-09-05 21:47:34 +000010382 else
10383 {
cristy4c08aed2011-07-01 19:47:50 +000010384 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010385 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010386 }
glennrp0fe50b42010-11-16 03:52:51 +000010387
cristy3ed852e2009-09-05 21:47:34 +000010388 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010389 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010390 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010391 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010392
glennrp3b51f0e2010-11-27 18:14:08 +000010393 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10395 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010396
glennrpcf002022011-01-30 02:38:15 +000010397 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010398 }
10399 if (image->previous == (Image *) NULL)
10400 {
10401 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10402 if (status == MagickFalse)
10403 break;
10404 }
10405 }
10406 }
glennrp0fe50b42010-11-16 03:52:51 +000010407
glennrp8bb3a022010-12-13 20:40:04 +000010408 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010409 {
glennrp0fe50b42010-11-16 03:52:51 +000010410 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010411 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010412 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010413 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010414 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010415 {
cristy4c08aed2011-07-01 19:47:50 +000010416 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010417 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010418
glennrp8bb3a022010-12-13 20:40:04 +000010419 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010420 {
glennrp8bb3a022010-12-13 20:40:04 +000010421
cristybb503372010-05-27 20:51:26 +000010422 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010423 {
10424 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010425
cristy4c08aed2011-07-01 19:47:50 +000010426 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010427 break;
glennrp2cc891a2010-12-24 13:44:32 +000010428
glennrp5af765f2010-03-30 11:12:18 +000010429 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010430 {
glennrp8bb3a022010-12-13 20:40:04 +000010431 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010432 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010433 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010434
glennrp8bb3a022010-12-13 20:40:04 +000010435 else
cristy4c08aed2011-07-01 19:47:50 +000010436 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010437 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010438
glennrp3b51f0e2010-11-27 18:14:08 +000010439 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010441 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010442 }
glennrp2cc891a2010-12-24 13:44:32 +000010443
glennrp8bb3a022010-12-13 20:40:04 +000010444 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10445 {
10446 if (logging != MagickFalse && y == 0)
10447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10448 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010449
cristy4c08aed2011-07-01 19:47:50 +000010450 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010451 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010452 }
glennrp2cc891a2010-12-24 13:44:32 +000010453
glennrp3b51f0e2010-11-27 18:14:08 +000010454 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010456 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010457
glennrpcf002022011-01-30 02:38:15 +000010458 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010459 }
glennrp2cc891a2010-12-24 13:44:32 +000010460
glennrp8bb3a022010-12-13 20:40:04 +000010461 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010462 {
glennrp8bb3a022010-12-13 20:40:04 +000010463 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10464 if (status == MagickFalse)
10465 break;
cristy3ed852e2009-09-05 21:47:34 +000010466 }
cristy3ed852e2009-09-05 21:47:34 +000010467 }
10468 }
glennrp8bb3a022010-12-13 20:40:04 +000010469
10470 else
10471 {
cristy4c08aed2011-07-01 19:47:50 +000010472 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010473 *p;
10474
10475 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010476 {
glennrp8bb3a022010-12-13 20:40:04 +000010477 if ((image_depth > 8) || (mng_info->write_png24 ||
10478 mng_info->write_png32 ||
10479 (!mng_info->write_png8 && !mng_info->IsPalette)))
10480 {
10481 for (y=0; y < (ssize_t) image->rows; y++)
10482 {
10483 p=GetVirtualPixels(image,0,y,image->columns,1,
10484 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010485
cristy4c08aed2011-07-01 19:47:50 +000010486 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010487 break;
glennrp2cc891a2010-12-24 13:44:32 +000010488
glennrp8bb3a022010-12-13 20:40:04 +000010489 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10490 {
10491 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010492 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010493 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010494
glennrp8bb3a022010-12-13 20:40:04 +000010495 else
cristy4c08aed2011-07-01 19:47:50 +000010496 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010497 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010498 }
glennrp2cc891a2010-12-24 13:44:32 +000010499
glennrp8bb3a022010-12-13 20:40:04 +000010500 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10501 {
cristy4c08aed2011-07-01 19:47:50 +000010502 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010503 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +000010504 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010505
glennrp8bb3a022010-12-13 20:40:04 +000010506 if (logging != MagickFalse && y == 0)
10507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10508 " Writing GRAY_ALPHA PNG pixels (3)");
10509 }
glennrp2cc891a2010-12-24 13:44:32 +000010510
glennrp8bb3a022010-12-13 20:40:04 +000010511 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010512 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010513 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010514
glennrp8bb3a022010-12-13 20:40:04 +000010515 else
cristy4c08aed2011-07-01 19:47:50 +000010516 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010517 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010518
glennrp8bb3a022010-12-13 20:40:04 +000010519 if (logging != MagickFalse && y == 0)
10520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10521 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010522
glennrpcf002022011-01-30 02:38:15 +000010523 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010524 }
10525 }
glennrp2cc891a2010-12-24 13:44:32 +000010526
glennrp8bb3a022010-12-13 20:40:04 +000010527 else
10528 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10529 mng_info->write_png32 ||
10530 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10531 {
10532 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10533 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10534 {
10535 if (logging != MagickFalse)
10536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10537 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010538
glennrp8bb3a022010-12-13 20:40:04 +000010539 quantum_info->depth=8;
10540 image_depth=8;
10541 }
glennrp2cc891a2010-12-24 13:44:32 +000010542
glennrp8bb3a022010-12-13 20:40:04 +000010543 for (y=0; y < (ssize_t) image->rows; y++)
10544 {
10545 if (logging != MagickFalse && y == 0)
10546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10547 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010548
glennrp770d1932011-03-06 22:11:17 +000010549 p=GetVirtualPixels(image,0,y,image->columns,1,
10550 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010551
cristy4c08aed2011-07-01 19:47:50 +000010552 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010553 break;
glennrp2cc891a2010-12-24 13:44:32 +000010554
glennrp8bb3a022010-12-13 20:40:04 +000010555 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010556 {
glennrp4bf89732011-03-21 13:48:28 +000010557 quantum_info->depth=image->depth;
10558
cristy4c08aed2011-07-01 19:47:50 +000010559 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010560 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +000010561 }
glennrp2cc891a2010-12-24 13:44:32 +000010562
glennrp8bb3a022010-12-13 20:40:04 +000010563 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10564 {
10565 if (logging != MagickFalse && y == 0)
10566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10567 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010568
cristy4c08aed2011-07-01 19:47:50 +000010569 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010570 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +000010571 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010572 }
glennrp2cc891a2010-12-24 13:44:32 +000010573
glennrp8bb3a022010-12-13 20:40:04 +000010574 else
glennrp8bb3a022010-12-13 20:40:04 +000010575 {
cristy4c08aed2011-07-01 19:47:50 +000010576 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +000010577 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10578
10579 if (logging != MagickFalse && y <= 2)
10580 {
10581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010582 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010583
10584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10585 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10586 (int)ping_pixels[0],(int)ping_pixels[1]);
10587 }
glennrp8bb3a022010-12-13 20:40:04 +000010588 }
glennrpcf002022011-01-30 02:38:15 +000010589 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010590 }
10591 }
glennrp2cc891a2010-12-24 13:44:32 +000010592
glennrp8bb3a022010-12-13 20:40:04 +000010593 if (image->previous == (Image *) NULL)
10594 {
10595 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10596 if (status == MagickFalse)
10597 break;
10598 }
cristy3ed852e2009-09-05 21:47:34 +000010599 }
glennrp8bb3a022010-12-13 20:40:04 +000010600 }
10601 }
10602
cristyb32b90a2009-09-07 21:45:48 +000010603 if (quantum_info != (QuantumInfo *) NULL)
10604 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010605
10606 if (logging != MagickFalse)
10607 {
10608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010609 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010610
cristy3ed852e2009-09-05 21:47:34 +000010611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010612 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010613
cristy3ed852e2009-09-05 21:47:34 +000010614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010615 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010616
cristy3ed852e2009-09-05 21:47:34 +000010617 if (mng_info->write_png_depth)
10618 {
10619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10620 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10621 }
glennrp0fe50b42010-11-16 03:52:51 +000010622
cristy3ed852e2009-09-05 21:47:34 +000010623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010624 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010625
cristy3ed852e2009-09-05 21:47:34 +000010626 if (mng_info->write_png_colortype)
10627 {
10628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10629 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10630 }
glennrp0fe50b42010-11-16 03:52:51 +000010631
cristy3ed852e2009-09-05 21:47:34 +000010632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010633 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010634
cristy3ed852e2009-09-05 21:47:34 +000010635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010636 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010637 }
10638 /*
glennrpa0ed0092011-04-18 16:36:29 +000010639 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010640 */
glennrp823b55c2011-03-14 18:46:46 +000010641 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010642 {
glennrp26f37912010-12-23 16:22:42 +000010643 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010644 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010645 while (property != (const char *) NULL)
10646 {
10647 png_textp
10648 text;
glennrp2cc891a2010-12-24 13:44:32 +000010649
cristyd15e6592011-10-15 00:13:06 +000010650 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010651
10652 /* Don't write any "png:" properties; those are just for "identify" */
10653 if (LocaleNCompare(property,"png:",4) != 0 &&
10654
10655 /* Suppress density and units if we wrote a pHYs chunk */
10656 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010657 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010658 LocaleCompare(property,"units") != 0) &&
10659
10660 /* Suppress the IM-generated Date:create and Date:modify */
10661 (ping_exclude_date == MagickFalse ||
10662 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010663 {
glennrpc70af4a2011-03-07 00:08:23 +000010664 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010665 {
glennrpc70af4a2011-03-07 00:08:23 +000010666 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10667 text[0].key=(char *) property;
10668 text[0].text=(char *) value;
10669 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010670
glennrpc70af4a2011-03-07 00:08:23 +000010671 if (ping_exclude_tEXt != MagickFalse)
10672 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10673
10674 else if (ping_exclude_zTXt != MagickFalse)
10675 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10676
10677 else
glennrp26f37912010-12-23 16:22:42 +000010678 {
glennrpc70af4a2011-03-07 00:08:23 +000010679 text[0].compression=image_info->compression == NoCompression ||
10680 (image_info->compression == UndefinedCompression &&
10681 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10682 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010683 }
glennrp2cc891a2010-12-24 13:44:32 +000010684
glennrpc70af4a2011-03-07 00:08:23 +000010685 if (logging != MagickFalse)
10686 {
10687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10688 " Setting up text chunk");
10689
10690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10691 " keyword: %s",text[0].key);
10692 }
10693
10694 png_set_text(ping,ping_info,text,1);
10695 png_free(ping,text);
10696 }
glennrp26f37912010-12-23 16:22:42 +000010697 }
10698 property=GetNextImageProperty(image);
10699 }
cristy3ed852e2009-09-05 21:47:34 +000010700 }
10701
10702 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010703 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010704
10705 if (logging != MagickFalse)
10706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10707 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010708
cristy3ed852e2009-09-05 21:47:34 +000010709 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010710
cristy3ed852e2009-09-05 21:47:34 +000010711 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10712 {
10713 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010714 (ping_width != mng_info->page.width) ||
10715 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010716 {
10717 unsigned char
10718 chunk[32];
10719
10720 /*
10721 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10722 */
10723 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10724 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010725 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010726 chunk[4]=4;
10727 chunk[5]=0; /* frame name separator (no name) */
10728 chunk[6]=1; /* flag for changing delay, for next frame only */
10729 chunk[7]=0; /* flag for changing frame timeout */
10730 chunk[8]=1; /* flag for changing frame clipping for next frame */
10731 chunk[9]=0; /* flag for changing frame sync_id */
10732 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10733 chunk[14]=0; /* clipping boundaries delta type */
10734 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10735 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010736 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010737 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10738 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010739 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010740 (void) WriteBlob(image,31,chunk);
10741 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10742 mng_info->old_framing_mode=4;
10743 mng_info->framing_mode=1;
10744 }
glennrp0fe50b42010-11-16 03:52:51 +000010745
cristy3ed852e2009-09-05 21:47:34 +000010746 else
10747 mng_info->framing_mode=3;
10748 }
10749 if (mng_info->write_mng && !mng_info->need_fram &&
10750 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +000010751 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010752 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010753 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010754
cristy3ed852e2009-09-05 21:47:34 +000010755 /*
10756 Free PNG resources.
10757 */
glennrp5af765f2010-03-30 11:12:18 +000010758
cristy3ed852e2009-09-05 21:47:34 +000010759 png_destroy_write_struct(&ping,&ping_info);
10760
glennrpcf002022011-01-30 02:38:15 +000010761 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010762
10763#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010764 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010765#endif
10766
glennrpda8f3a72011-02-27 23:54:12 +000010767 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010768 (void) CloseBlob(image);
10769
10770 image_info=DestroyImageInfo(image_info);
10771 image=DestroyImage(image);
10772
10773 /* Store bit depth actually written */
10774 s[0]=(char) ping_bit_depth;
10775 s[1]='\0';
10776
cristyd15e6592011-10-15 00:13:06 +000010777 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000010778
cristy3ed852e2009-09-05 21:47:34 +000010779 if (logging != MagickFalse)
10780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10781 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010782
cristy3ed852e2009-09-05 21:47:34 +000010783 return(MagickTrue);
10784/* End write one PNG image */
10785}
10786
10787/*
10788%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10789% %
10790% %
10791% %
10792% W r i t e P N G I m a g e %
10793% %
10794% %
10795% %
10796%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10797%
10798% WritePNGImage() writes a Portable Network Graphics (PNG) or
10799% Multiple-image Network Graphics (MNG) image file.
10800%
10801% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10802%
10803% The format of the WritePNGImage method is:
10804%
cristy1e178e72011-08-28 19:44:34 +000010805% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10806% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010807%
10808% A description of each parameter follows:
10809%
10810% o image_info: the image info.
10811%
10812% o image: The image.
10813%
cristy1e178e72011-08-28 19:44:34 +000010814% o exception: return any errors or warnings in this structure.
10815%
cristy3ed852e2009-09-05 21:47:34 +000010816% Returns MagickTrue on success, MagickFalse on failure.
10817%
10818% Communicating with the PNG encoder:
10819%
10820% While the datastream written is always in PNG format and normally would
10821% be given the "png" file extension, this method also writes the following
10822% pseudo-formats which are subsets of PNG:
10823%
glennrp5a39f372011-02-25 04:52:16 +000010824% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10825% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010826% is present, the tRNS chunk must only have values 0 and 255
10827% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010828% transparent). If other values are present they will be
10829% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010830% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010831% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10832% of any resulting fully-transparent pixels is changed to
10833% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010834%
10835% If you want better quantization or dithering of the colors
10836% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010837% PNG encoder. The pixels contain 8-bit indices even if
10838% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010839% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010840% PNG grayscale type might be slightly more efficient. Please
10841% note that writing to the PNG8 format may result in loss
10842% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010843%
10844% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10845% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010846% one of the colors as transparent. The only loss incurred
10847% is reduction of sample depth to 8. If the image has more
10848% than one transparent color, has semitransparent pixels, or
10849% has an opaque pixel with the same RGB components as the
10850% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010851%
10852% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10853% transparency is permitted, i.e., the alpha sample for
10854% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010855% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010856% The only loss in data is the reduction of the sample depth
10857% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010858%
10859% o -define: For more precise control of the PNG output, you can use the
10860% Image options "png:bit-depth" and "png:color-type". These
10861% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010862% from the application programming interfaces. The options
10863% are case-independent and are converted to lowercase before
10864% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010865%
10866% png:color-type can be 0, 2, 3, 4, or 6.
10867%
10868% When png:color-type is 0 (Grayscale), png:bit-depth can
10869% be 1, 2, 4, 8, or 16.
10870%
10871% When png:color-type is 2 (RGB), png:bit-depth can
10872% be 8 or 16.
10873%
10874% When png:color-type is 3 (Indexed), png:bit-depth can
10875% be 1, 2, 4, or 8. This refers to the number of bits
10876% used to store the index. The color samples always have
10877% bit-depth 8 in indexed PNG files.
10878%
10879% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10880% png:bit-depth can be 8 or 16.
10881%
glennrp5a39f372011-02-25 04:52:16 +000010882% If the image cannot be written without loss with the requested bit-depth
10883% and color-type, a PNG file will not be written, and the encoder will
10884% return MagickFalse.
10885%
cristy3ed852e2009-09-05 21:47:34 +000010886% Since image encoders should not be responsible for the "heavy lifting",
10887% the user should make sure that ImageMagick has already reduced the
10888% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010889% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010890% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010891%
cristy3ed852e2009-09-05 21:47:34 +000010892% Note that another definition, "png:bit-depth-written" exists, but it
10893% is not intended for external use. It is only used internally by the
10894% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10895%
10896% It is possible to request that the PNG encoder write previously-formatted
10897% ancillary chunks in the output PNG file, using the "-profile" commandline
10898% option as shown below or by setting the profile via a programming
10899% interface:
10900%
10901% -profile PNG-chunk-x:<file>
10902%
10903% where x is a location flag and <file> is a file containing the chunk
10904% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010905% This encoder will compute the chunk length and CRC, so those must not
10906% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010907%
10908% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10909% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10910% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010911% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010912%
glennrpbb8a7332010-11-13 15:17:35 +000010913% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010914%
glennrp3241bd02010-12-12 04:36:28 +000010915% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010916%
glennrpd6afd542010-11-19 01:53:05 +000010917% o 32-bit depth is reduced to 16.
10918% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10919% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010920% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010921% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010922% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010923% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10924% this can be done without loss and a larger bit depth N was not
10925% requested via the "-define PNG:bit-depth=N" option.
10926% o If matte channel is present but only one transparent color is
10927% present, RGB+tRNS is written instead of RGBA
10928% o Opaque matte channel is removed (or added, if color-type 4 or 6
10929% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010930%
cristy3ed852e2009-09-05 21:47:34 +000010931%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10932*/
10933static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy1e178e72011-08-28 19:44:34 +000010934 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010935{
10936 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010937 excluding,
10938 logging,
10939 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010940 status;
10941
10942 MngInfo
10943 *mng_info;
10944
10945 const char
10946 *value;
10947
10948 int
glennrp21f0e622011-01-07 16:20:57 +000010949 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010950 source;
10951
cristy3ed852e2009-09-05 21:47:34 +000010952 /*
10953 Open image file.
10954 */
10955 assert(image_info != (const ImageInfo *) NULL);
10956 assert(image_info->signature == MagickSignature);
10957 assert(image != (Image *) NULL);
10958 assert(image->signature == MagickSignature);
10959 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010960 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010961 /*
10962 Allocate a MngInfo structure.
10963 */
10964 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010965 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010966
cristy3ed852e2009-09-05 21:47:34 +000010967 if (mng_info == (MngInfo *) NULL)
10968 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010969
cristy3ed852e2009-09-05 21:47:34 +000010970 /*
10971 Initialize members of the MngInfo structure.
10972 */
10973 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10974 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010975 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010976 have_mng_structure=MagickTrue;
10977
10978 /* See if user has requested a specific PNG subformat */
10979
10980 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10981 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10982 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10983
10984 if (mng_info->write_png8)
10985 {
glennrp9c1eb072010-06-06 22:19:15 +000010986 mng_info->write_png_colortype = /* 3 */ 4;
10987 mng_info->write_png_depth = 8;
10988 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010989 }
10990
10991 if (mng_info->write_png24)
10992 {
glennrp9c1eb072010-06-06 22:19:15 +000010993 mng_info->write_png_colortype = /* 2 */ 3;
10994 mng_info->write_png_depth = 8;
10995 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010996
glennrp9c1eb072010-06-06 22:19:15 +000010997 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000010998 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010999
glennrp9c1eb072010-06-06 22:19:15 +000011000 else
cristy018f07f2011-09-04 21:15:19 +000011001 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011002
glennrp9c1eb072010-06-06 22:19:15 +000011003 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000011004 }
11005
11006 if (mng_info->write_png32)
11007 {
glennrp9c1eb072010-06-06 22:19:15 +000011008 mng_info->write_png_colortype = /* 6 */ 7;
11009 mng_info->write_png_depth = 8;
11010 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011011
glennrp9c1eb072010-06-06 22:19:15 +000011012 if (image->matte == MagickTrue)
cristy018f07f2011-09-04 21:15:19 +000011013 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011014
glennrp9c1eb072010-06-06 22:19:15 +000011015 else
cristy018f07f2011-09-04 21:15:19 +000011016 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011017
glennrp9c1eb072010-06-06 22:19:15 +000011018 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000011019 }
11020
11021 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011022
cristy3ed852e2009-09-05 21:47:34 +000011023 if (value != (char *) NULL)
11024 {
11025 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011026 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011027
cristy3ed852e2009-09-05 21:47:34 +000011028 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011029 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011030
cristy3ed852e2009-09-05 21:47:34 +000011031 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011032 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011033
cristy3ed852e2009-09-05 21:47:34 +000011034 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011035 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011036
cristy3ed852e2009-09-05 21:47:34 +000011037 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011038 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011039
glennrpbb8a7332010-11-13 15:17:35 +000011040 else
11041 (void) ThrowMagickException(&image->exception,
11042 GetMagickModule(),CoderWarning,
11043 "ignoring invalid defined png:bit-depth",
11044 "=%s",value);
11045
cristy3ed852e2009-09-05 21:47:34 +000011046 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011048 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011049 }
glennrp0fe50b42010-11-16 03:52:51 +000011050
cristy3ed852e2009-09-05 21:47:34 +000011051 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011052
cristy3ed852e2009-09-05 21:47:34 +000011053 if (value != (char *) NULL)
11054 {
11055 /* We must store colortype+1 because 0 is a valid colortype */
11056 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011057 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011058
cristy3ed852e2009-09-05 21:47:34 +000011059 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011060 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011061
cristy3ed852e2009-09-05 21:47:34 +000011062 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011063 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011064
cristy3ed852e2009-09-05 21:47:34 +000011065 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011066 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011067
cristy3ed852e2009-09-05 21:47:34 +000011068 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011069 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011070
glennrpbb8a7332010-11-13 15:17:35 +000011071 else
11072 (void) ThrowMagickException(&image->exception,
11073 GetMagickModule(),CoderWarning,
11074 "ignoring invalid defined png:color-type",
11075 "=%s",value);
11076
cristy3ed852e2009-09-05 21:47:34 +000011077 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011079 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011080 }
11081
glennrp0e8ea192010-12-24 18:00:33 +000011082 /* Check for chunks to be excluded:
11083 *
glennrp0dff56c2011-01-29 19:10:02 +000011084 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011085 * listed in the "unused_chunks" array, above.
11086 *
11087 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
11088 * define (in the image properties or in the image artifacts)
11089 * or via a mng_info member. For convenience, in addition
11090 * to or instead of a comma-separated list of chunks, the
11091 * "exclude-chunk" string can be simply "all" or "none".
11092 *
11093 * The exclude-chunk define takes priority over the mng_info.
11094 *
11095 * A "PNG:include-chunk" define takes priority over both the
11096 * mng_info and the "PNG:exclude-chunk" define. Like the
11097 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011098 * well as a comma-separated list. Chunks that are unknown to
11099 * ImageMagick are always excluded, regardless of their "copy-safe"
11100 * status according to the PNG specification, and even if they
11101 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000011102 *
11103 * Finally, all chunks listed in the "unused_chunks" array are
11104 * automatically excluded, regardless of the other instructions
11105 * or lack thereof.
11106 *
11107 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11108 * will not be written and the gAMA chunk will only be written if it
11109 * is not between .45 and .46, or approximately (1.0/2.2).
11110 *
11111 * If you exclude tRNS and the image has transparency, the colortype
11112 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11113 *
11114 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011115 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011116 */
11117
glennrp26f37912010-12-23 16:22:42 +000011118 mng_info->ping_exclude_bKGD=MagickFalse;
11119 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011120 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011121 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11122 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011123 mng_info->ping_exclude_iCCP=MagickFalse;
11124 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11125 mng_info->ping_exclude_oFFs=MagickFalse;
11126 mng_info->ping_exclude_pHYs=MagickFalse;
11127 mng_info->ping_exclude_sRGB=MagickFalse;
11128 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011129 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011130 mng_info->ping_exclude_vpAg=MagickFalse;
11131 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11132 mng_info->ping_exclude_zTXt=MagickFalse;
11133
glennrp8d3d6e52011-04-19 04:39:51 +000011134 mng_info->ping_preserve_colormap=MagickFalse;
11135
11136 value=GetImageArtifact(image,"png:preserve-colormap");
11137 if (value == NULL)
11138 value=GetImageOption(image_info,"png:preserve-colormap");
11139 if (value != NULL)
11140 mng_info->ping_preserve_colormap=MagickTrue;
11141
glennrp18682582011-06-30 18:11:47 +000011142 /* Thes compression-level, compression-strategy, and compression-filter
11143 * defines take precedence over values from the -quality option.
11144 */
11145 value=GetImageArtifact(image,"png:compression-level");
11146 if (value == NULL)
11147 value=GetImageOption(image_info,"png:compression-level");
11148 if (value != NULL)
11149 {
glennrp18682582011-06-30 18:11:47 +000011150 /* We have to add 1 to everything because 0 is a valid input,
11151 * and we want to use 0 (the default) to mean undefined.
11152 */
11153 if (LocaleCompare(value,"0") == 0)
11154 mng_info->write_png_compression_level = 1;
11155
11156 if (LocaleCompare(value,"1") == 0)
11157 mng_info->write_png_compression_level = 2;
11158
11159 else if (LocaleCompare(value,"2") == 0)
11160 mng_info->write_png_compression_level = 3;
11161
11162 else if (LocaleCompare(value,"3") == 0)
11163 mng_info->write_png_compression_level = 4;
11164
11165 else if (LocaleCompare(value,"4") == 0)
11166 mng_info->write_png_compression_level = 5;
11167
11168 else if (LocaleCompare(value,"5") == 0)
11169 mng_info->write_png_compression_level = 6;
11170
11171 else if (LocaleCompare(value,"6") == 0)
11172 mng_info->write_png_compression_level = 7;
11173
11174 else if (LocaleCompare(value,"7") == 0)
11175 mng_info->write_png_compression_level = 8;
11176
11177 else if (LocaleCompare(value,"8") == 0)
11178 mng_info->write_png_compression_level = 9;
11179
11180 else if (LocaleCompare(value,"9") == 0)
11181 mng_info->write_png_compression_level = 10;
11182
11183 else
11184 (void) ThrowMagickException(&image->exception,
11185 GetMagickModule(),CoderWarning,
11186 "ignoring invalid defined png:compression-level",
11187 "=%s",value);
11188 }
11189
11190 value=GetImageArtifact(image,"png:compression-strategy");
11191 if (value == NULL)
11192 value=GetImageOption(image_info,"png:compression-strategy");
11193 if (value != NULL)
11194 {
11195
11196 if (LocaleCompare(value,"0") == 0)
11197 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11198
11199 else if (LocaleCompare(value,"1") == 0)
11200 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11201
11202 else if (LocaleCompare(value,"2") == 0)
11203 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11204
11205 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011206#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011207 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011208#else
11209 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11210#endif
glennrp18682582011-06-30 18:11:47 +000011211
11212 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011213#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011214 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011215#else
11216 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11217#endif
glennrp18682582011-06-30 18:11:47 +000011218
11219 else
11220 (void) ThrowMagickException(&image->exception,
11221 GetMagickModule(),CoderWarning,
11222 "ignoring invalid defined png:compression-strategy",
11223 "=%s",value);
11224 }
11225
11226 value=GetImageArtifact(image,"png:compression-filter");
11227 if (value == NULL)
11228 value=GetImageOption(image_info,"png:compression-filter");
11229 if (value != NULL)
11230 {
11231
11232 /* To do: combinations of filters allowed by libpng
11233 * masks 0x08 through 0xf8
11234 *
11235 * Implement this as a comma-separated list of 0,1,2,3,4,5
11236 * where 5 is a special case meaning PNG_ALL_FILTERS.
11237 */
11238
11239 if (LocaleCompare(value,"0") == 0)
11240 mng_info->write_png_compression_filter = 1;
11241
11242 if (LocaleCompare(value,"1") == 0)
11243 mng_info->write_png_compression_filter = 2;
11244
11245 else if (LocaleCompare(value,"2") == 0)
11246 mng_info->write_png_compression_filter = 3;
11247
11248 else if (LocaleCompare(value,"3") == 0)
11249 mng_info->write_png_compression_filter = 4;
11250
11251 else if (LocaleCompare(value,"4") == 0)
11252 mng_info->write_png_compression_filter = 5;
11253
11254 else if (LocaleCompare(value,"5") == 0)
11255 mng_info->write_png_compression_filter = 6;
11256
glennrp18682582011-06-30 18:11:47 +000011257 else
11258 (void) ThrowMagickException(&image->exception,
11259 GetMagickModule(),CoderWarning,
11260 "ignoring invalid defined png:compression-filter",
11261 "=%s",value);
11262 }
11263
glennrp03812ae2010-12-24 01:31:34 +000011264 excluding=MagickFalse;
11265
glennrp5c7cf4e2010-12-24 00:30:00 +000011266 for (source=0; source<1; source++)
11267 {
11268 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011269 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011270 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011271
11272 if (value == NULL)
11273 value=GetImageArtifact(image,"png:exclude-chunks");
11274 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011275 else
glennrpacba0042010-12-24 14:27:26 +000011276 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011277 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011278
glennrpacba0042010-12-24 14:27:26 +000011279 if (value == NULL)
11280 value=GetImageOption(image_info,"png:exclude-chunks");
11281 }
11282
glennrp03812ae2010-12-24 01:31:34 +000011283 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011284 {
glennrp03812ae2010-12-24 01:31:34 +000011285
11286 size_t
11287 last;
11288
11289 excluding=MagickTrue;
11290
11291 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011292 {
11293 if (source == 0)
11294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11295 " png:exclude-chunk=%s found in image artifacts.\n", value);
11296 else
11297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11298 " png:exclude-chunk=%s found in image properties.\n", value);
11299 }
glennrp03812ae2010-12-24 01:31:34 +000011300
11301 last=strlen(value);
11302
11303 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011304 {
glennrp03812ae2010-12-24 01:31:34 +000011305
11306 if (LocaleNCompare(value+i,"all",3) == 0)
11307 {
11308 mng_info->ping_exclude_bKGD=MagickTrue;
11309 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011310 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011311 mng_info->ping_exclude_EXIF=MagickTrue;
11312 mng_info->ping_exclude_gAMA=MagickTrue;
11313 mng_info->ping_exclude_iCCP=MagickTrue;
11314 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11315 mng_info->ping_exclude_oFFs=MagickTrue;
11316 mng_info->ping_exclude_pHYs=MagickTrue;
11317 mng_info->ping_exclude_sRGB=MagickTrue;
11318 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011319 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011320 mng_info->ping_exclude_vpAg=MagickTrue;
11321 mng_info->ping_exclude_zCCP=MagickTrue;
11322 mng_info->ping_exclude_zTXt=MagickTrue;
11323 i--;
11324 }
glennrp2cc891a2010-12-24 13:44:32 +000011325
glennrp03812ae2010-12-24 01:31:34 +000011326 if (LocaleNCompare(value+i,"none",4) == 0)
11327 {
11328 mng_info->ping_exclude_bKGD=MagickFalse;
11329 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011330 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011331 mng_info->ping_exclude_EXIF=MagickFalse;
11332 mng_info->ping_exclude_gAMA=MagickFalse;
11333 mng_info->ping_exclude_iCCP=MagickFalse;
11334 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11335 mng_info->ping_exclude_oFFs=MagickFalse;
11336 mng_info->ping_exclude_pHYs=MagickFalse;
11337 mng_info->ping_exclude_sRGB=MagickFalse;
11338 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011339 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011340 mng_info->ping_exclude_vpAg=MagickFalse;
11341 mng_info->ping_exclude_zCCP=MagickFalse;
11342 mng_info->ping_exclude_zTXt=MagickFalse;
11343 }
glennrp2cc891a2010-12-24 13:44:32 +000011344
glennrp03812ae2010-12-24 01:31:34 +000011345 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11346 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011347
glennrp03812ae2010-12-24 01:31:34 +000011348 if (LocaleNCompare(value+i,"chrm",4) == 0)
11349 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011350
glennrpa0ed0092011-04-18 16:36:29 +000011351 if (LocaleNCompare(value+i,"date",4) == 0)
11352 mng_info->ping_exclude_date=MagickTrue;
11353
glennrp03812ae2010-12-24 01:31:34 +000011354 if (LocaleNCompare(value+i,"exif",4) == 0)
11355 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011356
glennrp03812ae2010-12-24 01:31:34 +000011357 if (LocaleNCompare(value+i,"gama",4) == 0)
11358 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011359
glennrp03812ae2010-12-24 01:31:34 +000011360 if (LocaleNCompare(value+i,"iccp",4) == 0)
11361 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011362
glennrp03812ae2010-12-24 01:31:34 +000011363 /*
11364 if (LocaleNCompare(value+i,"itxt",4) == 0)
11365 mng_info->ping_exclude_iTXt=MagickTrue;
11366 */
glennrp2cc891a2010-12-24 13:44:32 +000011367
glennrp03812ae2010-12-24 01:31:34 +000011368 if (LocaleNCompare(value+i,"gama",4) == 0)
11369 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011370
glennrp03812ae2010-12-24 01:31:34 +000011371 if (LocaleNCompare(value+i,"offs",4) == 0)
11372 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011373
glennrp03812ae2010-12-24 01:31:34 +000011374 if (LocaleNCompare(value+i,"phys",4) == 0)
11375 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011376
glennrpa1e3b7b2010-12-24 16:37:33 +000011377 if (LocaleNCompare(value+i,"srgb",4) == 0)
11378 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011379
glennrp03812ae2010-12-24 01:31:34 +000011380 if (LocaleNCompare(value+i,"text",4) == 0)
11381 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011382
glennrpa1e3b7b2010-12-24 16:37:33 +000011383 if (LocaleNCompare(value+i,"trns",4) == 0)
11384 mng_info->ping_exclude_tRNS=MagickTrue;
11385
glennrp03812ae2010-12-24 01:31:34 +000011386 if (LocaleNCompare(value+i,"vpag",4) == 0)
11387 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011388
glennrp03812ae2010-12-24 01:31:34 +000011389 if (LocaleNCompare(value+i,"zccp",4) == 0)
11390 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011391
glennrp03812ae2010-12-24 01:31:34 +000011392 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11393 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011394
glennrp03812ae2010-12-24 01:31:34 +000011395 }
glennrpce91ed52010-12-23 22:37:49 +000011396 }
glennrp26f37912010-12-23 16:22:42 +000011397 }
11398
glennrp5c7cf4e2010-12-24 00:30:00 +000011399 for (source=0; source<1; source++)
11400 {
11401 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011402 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011403 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011404
11405 if (value == NULL)
11406 value=GetImageArtifact(image,"png:include-chunks");
11407 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011408 else
glennrpacba0042010-12-24 14:27:26 +000011409 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011410 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011411
glennrpacba0042010-12-24 14:27:26 +000011412 if (value == NULL)
11413 value=GetImageOption(image_info,"png:include-chunks");
11414 }
11415
glennrp03812ae2010-12-24 01:31:34 +000011416 if (value != NULL)
11417 {
11418 size_t
11419 last;
glennrp26f37912010-12-23 16:22:42 +000011420
glennrp03812ae2010-12-24 01:31:34 +000011421 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011422
glennrp03812ae2010-12-24 01:31:34 +000011423 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011424 {
11425 if (source == 0)
11426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11427 " png:include-chunk=%s found in image artifacts.\n", value);
11428 else
11429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11430 " png:include-chunk=%s found in image properties.\n", value);
11431 }
glennrp03812ae2010-12-24 01:31:34 +000011432
11433 last=strlen(value);
11434
11435 for (i=0; i<(int) last; i+=5)
11436 {
11437 if (LocaleNCompare(value+i,"all",3) == 0)
11438 {
11439 mng_info->ping_exclude_bKGD=MagickFalse;
11440 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011441 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011442 mng_info->ping_exclude_EXIF=MagickFalse;
11443 mng_info->ping_exclude_gAMA=MagickFalse;
11444 mng_info->ping_exclude_iCCP=MagickFalse;
11445 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11446 mng_info->ping_exclude_oFFs=MagickFalse;
11447 mng_info->ping_exclude_pHYs=MagickFalse;
11448 mng_info->ping_exclude_sRGB=MagickFalse;
11449 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011450 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011451 mng_info->ping_exclude_vpAg=MagickFalse;
11452 mng_info->ping_exclude_zCCP=MagickFalse;
11453 mng_info->ping_exclude_zTXt=MagickFalse;
11454 i--;
11455 }
glennrp2cc891a2010-12-24 13:44:32 +000011456
glennrp03812ae2010-12-24 01:31:34 +000011457 if (LocaleNCompare(value+i,"none",4) == 0)
11458 {
11459 mng_info->ping_exclude_bKGD=MagickTrue;
11460 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011461 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011462 mng_info->ping_exclude_EXIF=MagickTrue;
11463 mng_info->ping_exclude_gAMA=MagickTrue;
11464 mng_info->ping_exclude_iCCP=MagickTrue;
11465 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11466 mng_info->ping_exclude_oFFs=MagickTrue;
11467 mng_info->ping_exclude_pHYs=MagickTrue;
11468 mng_info->ping_exclude_sRGB=MagickTrue;
11469 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011470 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011471 mng_info->ping_exclude_vpAg=MagickTrue;
11472 mng_info->ping_exclude_zCCP=MagickTrue;
11473 mng_info->ping_exclude_zTXt=MagickTrue;
11474 }
glennrp2cc891a2010-12-24 13:44:32 +000011475
glennrp03812ae2010-12-24 01:31:34 +000011476 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11477 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011478
glennrp03812ae2010-12-24 01:31:34 +000011479 if (LocaleNCompare(value+i,"chrm",4) == 0)
11480 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011481
glennrpa0ed0092011-04-18 16:36:29 +000011482 if (LocaleNCompare(value+i,"date",4) == 0)
11483 mng_info->ping_exclude_date=MagickFalse;
11484
glennrp03812ae2010-12-24 01:31:34 +000011485 if (LocaleNCompare(value+i,"exif",4) == 0)
11486 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011487
glennrp03812ae2010-12-24 01:31:34 +000011488 if (LocaleNCompare(value+i,"gama",4) == 0)
11489 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011490
glennrp03812ae2010-12-24 01:31:34 +000011491 if (LocaleNCompare(value+i,"iccp",4) == 0)
11492 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011493
glennrp03812ae2010-12-24 01:31:34 +000011494 /*
11495 if (LocaleNCompare(value+i,"itxt",4) == 0)
11496 mng_info->ping_exclude_iTXt=MagickFalse;
11497 */
glennrp2cc891a2010-12-24 13:44:32 +000011498
glennrp03812ae2010-12-24 01:31:34 +000011499 if (LocaleNCompare(value+i,"gama",4) == 0)
11500 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011501
glennrp03812ae2010-12-24 01:31:34 +000011502 if (LocaleNCompare(value+i,"offs",4) == 0)
11503 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011504
glennrp03812ae2010-12-24 01:31:34 +000011505 if (LocaleNCompare(value+i,"phys",4) == 0)
11506 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011507
glennrpa1e3b7b2010-12-24 16:37:33 +000011508 if (LocaleNCompare(value+i,"srgb",4) == 0)
11509 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011510
glennrp03812ae2010-12-24 01:31:34 +000011511 if (LocaleNCompare(value+i,"text",4) == 0)
11512 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011513
glennrpa1e3b7b2010-12-24 16:37:33 +000011514 if (LocaleNCompare(value+i,"trns",4) == 0)
11515 mng_info->ping_exclude_tRNS=MagickFalse;
11516
glennrp03812ae2010-12-24 01:31:34 +000011517 if (LocaleNCompare(value+i,"vpag",4) == 0)
11518 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011519
glennrp03812ae2010-12-24 01:31:34 +000011520 if (LocaleNCompare(value+i,"zccp",4) == 0)
11521 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011522
glennrp03812ae2010-12-24 01:31:34 +000011523 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11524 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011525
glennrp03812ae2010-12-24 01:31:34 +000011526 }
glennrpce91ed52010-12-23 22:37:49 +000011527 }
glennrp26f37912010-12-23 16:22:42 +000011528 }
11529
glennrp03812ae2010-12-24 01:31:34 +000011530 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011531 {
11532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11533 " Chunks to be excluded from the output PNG:");
11534 if (mng_info->ping_exclude_bKGD != MagickFalse)
11535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11536 " bKGD");
11537 if (mng_info->ping_exclude_cHRM != MagickFalse)
11538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11539 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011540 if (mng_info->ping_exclude_date != MagickFalse)
11541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11542 " date");
glennrp26f37912010-12-23 16:22:42 +000011543 if (mng_info->ping_exclude_EXIF != MagickFalse)
11544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11545 " EXIF");
11546 if (mng_info->ping_exclude_gAMA != MagickFalse)
11547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11548 " gAMA");
11549 if (mng_info->ping_exclude_iCCP != MagickFalse)
11550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11551 " iCCP");
11552/*
11553 if (mng_info->ping_exclude_iTXt != MagickFalse)
11554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11555 " iTXt");
11556*/
11557 if (mng_info->ping_exclude_oFFs != MagickFalse)
11558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11559 " oFFs");
11560 if (mng_info->ping_exclude_pHYs != MagickFalse)
11561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11562 " pHYs");
11563 if (mng_info->ping_exclude_sRGB != MagickFalse)
11564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11565 " sRGB");
11566 if (mng_info->ping_exclude_tEXt != MagickFalse)
11567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11568 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011569 if (mng_info->ping_exclude_tRNS != MagickFalse)
11570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11571 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011572 if (mng_info->ping_exclude_vpAg != MagickFalse)
11573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11574 " vpAg");
11575 if (mng_info->ping_exclude_zCCP != MagickFalse)
11576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11577 " zCCP");
11578 if (mng_info->ping_exclude_zTXt != MagickFalse)
11579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11580 " zTXt");
11581 }
11582
glennrpb9cfe272010-12-21 15:08:06 +000011583 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011584
cristy018f07f2011-09-04 21:15:19 +000011585 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011586
11587 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011588
cristy3ed852e2009-09-05 21:47:34 +000011589 if (logging != MagickFalse)
11590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011591
cristy3ed852e2009-09-05 21:47:34 +000011592 return(status);
11593}
11594
11595#if defined(JNG_SUPPORTED)
11596
11597/* Write one JNG image */
11598static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy018f07f2011-09-04 21:15:19 +000011599 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011600{
11601 Image
11602 *jpeg_image;
11603
11604 ImageInfo
11605 *jpeg_image_info;
11606
11607 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011608 logging,
cristy3ed852e2009-09-05 21:47:34 +000011609 status;
11610
11611 size_t
11612 length;
11613
11614 unsigned char
11615 *blob,
11616 chunk[80],
11617 *p;
11618
11619 unsigned int
11620 jng_alpha_compression_method,
11621 jng_alpha_sample_depth,
11622 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011623 transparent;
11624
cristybb503372010-05-27 20:51:26 +000011625 size_t
cristy3ed852e2009-09-05 21:47:34 +000011626 jng_quality;
11627
11628 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011629 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011630
11631 blob=(unsigned char *) NULL;
11632 jpeg_image=(Image *) NULL;
11633 jpeg_image_info=(ImageInfo *) NULL;
11634
11635 status=MagickTrue;
11636 transparent=image_info->type==GrayscaleMatteType ||
11637 image_info->type==TrueColorMatteType;
11638 jng_color_type=10;
11639 jng_alpha_sample_depth=0;
11640 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11641 jng_alpha_compression_method=0;
11642
11643 if (image->matte != MagickFalse)
11644 {
11645 /* if any pixels are transparent */
11646 transparent=MagickTrue;
11647 if (image_info->compression==JPEGCompression)
11648 jng_alpha_compression_method=8;
11649 }
11650
11651 if (transparent)
11652 {
cristybd5a96c2011-08-21 00:04:26 +000011653 ChannelType
11654 channel_mask;
11655
cristy3ed852e2009-09-05 21:47:34 +000011656 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011657
cristy3ed852e2009-09-05 21:47:34 +000011658 /* Create JPEG blob, image, and image_info */
11659 if (logging != MagickFalse)
11660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011661 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011662
cristy3ed852e2009-09-05 21:47:34 +000011663 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011664
cristy3ed852e2009-09-05 21:47:34 +000011665 if (jpeg_image_info == (ImageInfo *) NULL)
11666 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011667
cristy3ed852e2009-09-05 21:47:34 +000011668 if (logging != MagickFalse)
11669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11670 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011671
cristy3ed852e2009-09-05 21:47:34 +000011672 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011673
cristy3ed852e2009-09-05 21:47:34 +000011674 if (jpeg_image == (Image *) NULL)
11675 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011676
cristy3ed852e2009-09-05 21:47:34 +000011677 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristybd5a96c2011-08-21 00:04:26 +000011678 channel_mask=SetPixelChannelMask(jpeg_image,AlphaChannel);
cristye941a752011-10-15 01:52:48 +000011679 status=SeparateImage(jpeg_image,exception);
cristybd5a96c2011-08-21 00:04:26 +000011680 (void) SetPixelChannelMap(jpeg_image,channel_mask);
cristy3ed852e2009-09-05 21:47:34 +000011681 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011682
cristy3ed852e2009-09-05 21:47:34 +000011683 if (jng_quality >= 1000)
11684 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011685
cristy3ed852e2009-09-05 21:47:34 +000011686 else
11687 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011688
cristy3ed852e2009-09-05 21:47:34 +000011689 jpeg_image_info->type=GrayscaleType;
cristy018f07f2011-09-04 21:15:19 +000011690 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011691 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011692 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011693 "%s",jpeg_image->filename);
11694 }
11695
11696 /* To do: check bit depth of PNG alpha channel */
11697
11698 /* Check if image is grayscale. */
11699 if (image_info->type != TrueColorMatteType && image_info->type !=
11700 TrueColorType && ImageIsGray(image))
11701 jng_color_type-=2;
11702
11703 if (transparent)
11704 {
11705 if (jng_alpha_compression_method==0)
11706 {
11707 const char
11708 *value;
11709
cristy4c08aed2011-07-01 19:47:50 +000011710 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011711 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11712 &image->exception);
11713 if (logging != MagickFalse)
11714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11715 " Creating PNG blob.");
11716 length=0;
11717
11718 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11719 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11720 jpeg_image_info->interlace=NoInterlace;
11721
11722 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11723 &image->exception);
11724
11725 /* Retrieve sample depth used */
cristyd15e6592011-10-15 00:13:06 +000011726 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011727 if (value != (char *) NULL)
11728 jng_alpha_sample_depth= (unsigned int) value[0];
11729 }
11730 else
11731 {
cristy4c08aed2011-07-01 19:47:50 +000011732 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011733
11734 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11735 &image->exception);
11736
11737 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11738 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11739 jpeg_image_info->interlace=NoInterlace;
11740 if (logging != MagickFalse)
11741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11742 " Creating blob.");
11743 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000011744 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000011745 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011746
cristy3ed852e2009-09-05 21:47:34 +000011747 if (logging != MagickFalse)
11748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011749 " Successfully read jpeg_image into a blob, length=%.20g.",
11750 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011751
11752 }
11753 /* Destroy JPEG image and image_info */
11754 jpeg_image=DestroyImage(jpeg_image);
11755 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11756 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11757 }
11758
11759 /* Write JHDR chunk */
11760 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11761 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011762 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011763 PNGLong(chunk+4,(png_uint_32) image->columns);
11764 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011765 chunk[12]=jng_color_type;
11766 chunk[13]=8; /* sample depth */
11767 chunk[14]=8; /*jng_image_compression_method */
11768 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11769 chunk[16]=jng_alpha_sample_depth;
11770 chunk[17]=jng_alpha_compression_method;
11771 chunk[18]=0; /*jng_alpha_filter_method */
11772 chunk[19]=0; /*jng_alpha_interlace_method */
11773 (void) WriteBlob(image,20,chunk);
11774 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11775 if (logging != MagickFalse)
11776 {
11777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011778 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011779
cristy3ed852e2009-09-05 21:47:34 +000011780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011781 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011782
cristy3ed852e2009-09-05 21:47:34 +000011783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11784 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011785
cristy3ed852e2009-09-05 21:47:34 +000011786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11787 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011788
cristy3ed852e2009-09-05 21:47:34 +000011789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11790 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011791
cristy3ed852e2009-09-05 21:47:34 +000011792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11793 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011794
cristy3ed852e2009-09-05 21:47:34 +000011795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11796 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011797
cristy3ed852e2009-09-05 21:47:34 +000011798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11799 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011800
cristy3ed852e2009-09-05 21:47:34 +000011801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11802 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011803
cristy3ed852e2009-09-05 21:47:34 +000011804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11805 " JNG alpha interlace:%5d",0);
11806 }
11807
glennrp0fe50b42010-11-16 03:52:51 +000011808 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011809 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011810
11811 /*
11812 Write leading ancillary chunks
11813 */
11814
11815 if (transparent)
11816 {
11817 /*
11818 Write JNG bKGD chunk
11819 */
11820
11821 unsigned char
11822 blue,
11823 green,
11824 red;
11825
cristybb503372010-05-27 20:51:26 +000011826 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011827 num_bytes;
11828
11829 if (jng_color_type == 8 || jng_color_type == 12)
11830 num_bytes=6L;
11831 else
11832 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011833 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011834 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011835 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011836 red=ScaleQuantumToChar(image->background_color.red);
11837 green=ScaleQuantumToChar(image->background_color.green);
11838 blue=ScaleQuantumToChar(image->background_color.blue);
11839 *(chunk+4)=0;
11840 *(chunk+5)=red;
11841 *(chunk+6)=0;
11842 *(chunk+7)=green;
11843 *(chunk+8)=0;
11844 *(chunk+9)=blue;
11845 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11846 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11847 }
11848
11849 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11850 {
11851 /*
11852 Write JNG sRGB chunk
11853 */
11854 (void) WriteBlobMSBULong(image,1L);
11855 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011856 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011857
cristy3ed852e2009-09-05 21:47:34 +000011858 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011859 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011860 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011861 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011862
cristy3ed852e2009-09-05 21:47:34 +000011863 else
glennrpe610a072010-08-05 17:08:46 +000011864 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011865 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011866 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011867
cristy3ed852e2009-09-05 21:47:34 +000011868 (void) WriteBlob(image,5,chunk);
11869 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11870 }
11871 else
11872 {
11873 if (image->gamma != 0.0)
11874 {
11875 /*
11876 Write JNG gAMA chunk
11877 */
11878 (void) WriteBlobMSBULong(image,4L);
11879 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011880 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011881 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011882 (void) WriteBlob(image,8,chunk);
11883 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11884 }
glennrp0fe50b42010-11-16 03:52:51 +000011885
cristy3ed852e2009-09-05 21:47:34 +000011886 if ((mng_info->equal_chrms == MagickFalse) &&
11887 (image->chromaticity.red_primary.x != 0.0))
11888 {
11889 PrimaryInfo
11890 primary;
11891
11892 /*
11893 Write JNG cHRM chunk
11894 */
11895 (void) WriteBlobMSBULong(image,32L);
11896 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011897 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011898 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011899 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11900 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011901 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011902 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11903 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011904 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011905 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11906 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011907 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011908 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11909 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011910 (void) WriteBlob(image,36,chunk);
11911 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11912 }
11913 }
glennrp0fe50b42010-11-16 03:52:51 +000011914
cristy3ed852e2009-09-05 21:47:34 +000011915 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11916 {
11917 /*
11918 Write JNG pHYs chunk
11919 */
11920 (void) WriteBlobMSBULong(image,9L);
11921 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011922 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011923 if (image->units == PixelsPerInchResolution)
11924 {
cristy35ef8242010-06-03 16:24:13 +000011925 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011926 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011927
cristy35ef8242010-06-03 16:24:13 +000011928 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011929 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011930
cristy3ed852e2009-09-05 21:47:34 +000011931 chunk[12]=1;
11932 }
glennrp0fe50b42010-11-16 03:52:51 +000011933
cristy3ed852e2009-09-05 21:47:34 +000011934 else
11935 {
11936 if (image->units == PixelsPerCentimeterResolution)
11937 {
cristy35ef8242010-06-03 16:24:13 +000011938 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011939 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011940
cristy35ef8242010-06-03 16:24:13 +000011941 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011942 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011943
cristy3ed852e2009-09-05 21:47:34 +000011944 chunk[12]=1;
11945 }
glennrp0fe50b42010-11-16 03:52:51 +000011946
cristy3ed852e2009-09-05 21:47:34 +000011947 else
11948 {
cristy35ef8242010-06-03 16:24:13 +000011949 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11950 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011951 chunk[12]=0;
11952 }
11953 }
11954 (void) WriteBlob(image,13,chunk);
11955 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11956 }
11957
11958 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11959 {
11960 /*
11961 Write JNG oFFs chunk
11962 */
11963 (void) WriteBlobMSBULong(image,9L);
11964 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011965 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011966 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11967 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011968 chunk[12]=0;
11969 (void) WriteBlob(image,13,chunk);
11970 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11971 }
11972 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11973 {
11974 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11975 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011976 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011977 PNGLong(chunk+4,(png_uint_32) image->page.width);
11978 PNGLong(chunk+8,(png_uint_32) image->page.height);
11979 chunk[12]=0; /* unit = pixels */
11980 (void) WriteBlob(image,13,chunk);
11981 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11982 }
11983
11984
11985 if (transparent)
11986 {
11987 if (jng_alpha_compression_method==0)
11988 {
cristybb503372010-05-27 20:51:26 +000011989 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011990 i;
11991
cristybb503372010-05-27 20:51:26 +000011992 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011993 len;
11994
11995 /* Write IDAT chunk header */
11996 if (logging != MagickFalse)
11997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011998 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011999 length);
cristy3ed852e2009-09-05 21:47:34 +000012000
12001 /* Copy IDAT chunks */
12002 len=0;
12003 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012004 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012005 {
12006 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12007 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012008
cristy3ed852e2009-09-05 21:47:34 +000012009 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12010 {
12011 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012012 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012013 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012014 (void) WriteBlob(image,(size_t) len+4,p);
12015 (void) WriteBlobMSBULong(image,
12016 crc32(0,p,(uInt) len+4));
12017 }
glennrp0fe50b42010-11-16 03:52:51 +000012018
cristy3ed852e2009-09-05 21:47:34 +000012019 else
12020 {
12021 if (logging != MagickFalse)
12022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012023 " Skipping %c%c%c%c chunk, length=%.20g.",
12024 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012025 }
12026 p+=(8+len);
12027 }
12028 }
12029 else
12030 {
12031 /* Write JDAA chunk header */
12032 if (logging != MagickFalse)
12033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012034 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012035 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012036 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012037 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012038 /* Write JDAT chunk(s) data */
12039 (void) WriteBlob(image,4,chunk);
12040 (void) WriteBlob(image,length,blob);
12041 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12042 (uInt) length));
12043 }
12044 blob=(unsigned char *) RelinquishMagickMemory(blob);
12045 }
12046
12047 /* Encode image as a JPEG blob */
12048 if (logging != MagickFalse)
12049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12050 " Creating jpeg_image_info.");
12051 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12052 if (jpeg_image_info == (ImageInfo *) NULL)
12053 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12054
12055 if (logging != MagickFalse)
12056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12057 " Creating jpeg_image.");
12058
12059 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12060 if (jpeg_image == (Image *) NULL)
12061 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12062 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12063
12064 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012065 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012066 jpeg_image->filename);
12067
12068 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12069 &image->exception);
12070
12071 if (logging != MagickFalse)
12072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012073 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12074 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012075
12076 if (jng_color_type == 8 || jng_color_type == 12)
12077 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012078
cristy3ed852e2009-09-05 21:47:34 +000012079 jpeg_image_info->quality=jng_quality % 1000;
12080 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12081 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012082
cristy3ed852e2009-09-05 21:47:34 +000012083 if (logging != MagickFalse)
12084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12085 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012086
cristy3ed852e2009-09-05 21:47:34 +000012087 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000012088
cristy3ed852e2009-09-05 21:47:34 +000012089 if (logging != MagickFalse)
12090 {
12091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012092 " Successfully read jpeg_image into a blob, length=%.20g.",
12093 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012094
12095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012096 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012097 }
glennrp0fe50b42010-11-16 03:52:51 +000012098
cristy3ed852e2009-09-05 21:47:34 +000012099 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012100 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012101 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012102 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012103 (void) WriteBlob(image,4,chunk);
12104 (void) WriteBlob(image,length,blob);
12105 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12106
12107 jpeg_image=DestroyImage(jpeg_image);
12108 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12109 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12110 blob=(unsigned char *) RelinquishMagickMemory(blob);
12111
12112 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012113 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012114
12115 /* Write IEND chunk */
12116 (void) WriteBlobMSBULong(image,0L);
12117 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012118 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012119 (void) WriteBlob(image,4,chunk);
12120 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12121
12122 if (logging != MagickFalse)
12123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12124 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012125
cristy3ed852e2009-09-05 21:47:34 +000012126 return(status);
12127}
12128
12129
12130/*
12131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12132% %
12133% %
12134% %
12135% W r i t e J N G I m a g e %
12136% %
12137% %
12138% %
12139%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12140%
12141% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12142%
12143% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12144%
12145% The format of the WriteJNGImage method is:
12146%
cristy1e178e72011-08-28 19:44:34 +000012147% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12148% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012149%
12150% A description of each parameter follows:
12151%
12152% o image_info: the image info.
12153%
12154% o image: The image.
12155%
cristy1e178e72011-08-28 19:44:34 +000012156% o exception: return any errors or warnings in this structure.
12157%
cristy3ed852e2009-09-05 21:47:34 +000012158%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12159*/
cristy1e178e72011-08-28 19:44:34 +000012160static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12161 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012162{
12163 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012164 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012165 logging,
cristy3ed852e2009-09-05 21:47:34 +000012166 status;
12167
12168 MngInfo
12169 *mng_info;
12170
cristy3ed852e2009-09-05 21:47:34 +000012171 /*
12172 Open image file.
12173 */
12174 assert(image_info != (const ImageInfo *) NULL);
12175 assert(image_info->signature == MagickSignature);
12176 assert(image != (Image *) NULL);
12177 assert(image->signature == MagickSignature);
12178 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012179 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012180 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12181 if (status == MagickFalse)
12182 return(status);
12183
12184 /*
12185 Allocate a MngInfo structure.
12186 */
12187 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012188 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012189 if (mng_info == (MngInfo *) NULL)
12190 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12191 /*
12192 Initialize members of the MngInfo structure.
12193 */
12194 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12195 mng_info->image=image;
12196 have_mng_structure=MagickTrue;
12197
12198 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12199
cristy018f07f2011-09-04 21:15:19 +000012200 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012201 (void) CloseBlob(image);
12202
12203 (void) CatchImageException(image);
12204 MngInfoFreeStruct(mng_info,&have_mng_structure);
12205 if (logging != MagickFalse)
12206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12207 return(status);
12208}
12209#endif
12210
cristy1e178e72011-08-28 19:44:34 +000012211static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12212 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012213{
12214 const char
12215 *option;
12216
12217 Image
12218 *next_image;
12219
12220 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012221 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012222 status;
12223
glennrp03812ae2010-12-24 01:31:34 +000012224 volatile MagickBooleanType
12225 logging;
12226
cristy3ed852e2009-09-05 21:47:34 +000012227 MngInfo
12228 *mng_info;
12229
12230 int
cristy3ed852e2009-09-05 21:47:34 +000012231 image_count,
12232 need_iterations,
12233 need_matte;
12234
12235 volatile int
12236#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12237 defined(PNG_MNG_FEATURES_SUPPORTED)
12238 need_local_plte,
12239#endif
12240 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012241 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012242 use_global_plte;
12243
cristybb503372010-05-27 20:51:26 +000012244 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012245 i;
12246
12247 unsigned char
12248 chunk[800];
12249
12250 volatile unsigned int
12251 write_jng,
12252 write_mng;
12253
cristybb503372010-05-27 20:51:26 +000012254 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012255 scene;
12256
cristybb503372010-05-27 20:51:26 +000012257 size_t
cristy3ed852e2009-09-05 21:47:34 +000012258 final_delay=0,
12259 initial_delay;
12260
glennrpd5045b42010-03-24 12:40:35 +000012261#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012262 if (image_info->verbose)
12263 printf("Your PNG library (libpng-%s) is rather old.\n",
12264 PNG_LIBPNG_VER_STRING);
12265#endif
12266
12267 /*
12268 Open image file.
12269 */
12270 assert(image_info != (const ImageInfo *) NULL);
12271 assert(image_info->signature == MagickSignature);
12272 assert(image != (Image *) NULL);
12273 assert(image->signature == MagickSignature);
12274 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012275 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012276 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12277 if (status == MagickFalse)
12278 return(status);
12279
12280 /*
12281 Allocate a MngInfo structure.
12282 */
12283 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012284 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012285 if (mng_info == (MngInfo *) NULL)
12286 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12287 /*
12288 Initialize members of the MngInfo structure.
12289 */
12290 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12291 mng_info->image=image;
12292 have_mng_structure=MagickTrue;
12293 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12294
12295 /*
12296 * See if user has requested a specific PNG subformat to be used
12297 * for all of the PNGs in the MNG being written, e.g.,
12298 *
12299 * convert *.png png8:animation.mng
12300 *
12301 * To do: check -define png:bit_depth and png:color_type as well,
12302 * or perhaps use mng:bit_depth and mng:color_type instead for
12303 * global settings.
12304 */
12305
12306 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12307 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12308 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12309
12310 write_jng=MagickFalse;
12311 if (image_info->compression == JPEGCompression)
12312 write_jng=MagickTrue;
12313
12314 mng_info->adjoin=image_info->adjoin &&
12315 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12316
cristy3ed852e2009-09-05 21:47:34 +000012317 if (logging != MagickFalse)
12318 {
12319 /* Log some info about the input */
12320 Image
12321 *p;
12322
12323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12324 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012325
cristy3ed852e2009-09-05 21:47:34 +000012326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012327 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012328
cristy3ed852e2009-09-05 21:47:34 +000012329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12330 " Type: %d",image_info->type);
12331
12332 scene=0;
12333 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12334 {
12335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012336 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012337
cristy3ed852e2009-09-05 21:47:34 +000012338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012339 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012340
cristy3ed852e2009-09-05 21:47:34 +000012341 if (p->matte)
12342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12343 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012344
cristy3ed852e2009-09-05 21:47:34 +000012345 else
12346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12347 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012348
cristy3ed852e2009-09-05 21:47:34 +000012349 if (p->storage_class == PseudoClass)
12350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12351 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012352
cristy3ed852e2009-09-05 21:47:34 +000012353 else
12354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12355 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012356
cristy3ed852e2009-09-05 21:47:34 +000012357 if (p->colors)
12358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012359 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012360
cristy3ed852e2009-09-05 21:47:34 +000012361 else
12362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12363 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012364
cristy3ed852e2009-09-05 21:47:34 +000012365 if (mng_info->adjoin == MagickFalse)
12366 break;
12367 }
12368 }
12369
cristy3ed852e2009-09-05 21:47:34 +000012370 use_global_plte=MagickFalse;
12371 all_images_are_gray=MagickFalse;
12372#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12373 need_local_plte=MagickTrue;
12374#endif
12375 need_defi=MagickFalse;
12376 need_matte=MagickFalse;
12377 mng_info->framing_mode=1;
12378 mng_info->old_framing_mode=1;
12379
12380 if (write_mng)
12381 if (image_info->page != (char *) NULL)
12382 {
12383 /*
12384 Determine image bounding box.
12385 */
12386 SetGeometry(image,&mng_info->page);
12387 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12388 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12389 }
12390 if (write_mng)
12391 {
12392 unsigned int
12393 need_geom;
12394
12395 unsigned short
12396 red,
12397 green,
12398 blue;
12399
12400 mng_info->page=image->page;
12401 need_geom=MagickTrue;
12402 if (mng_info->page.width || mng_info->page.height)
12403 need_geom=MagickFalse;
12404 /*
12405 Check all the scenes.
12406 */
12407 initial_delay=image->delay;
12408 need_iterations=MagickFalse;
12409 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12410 mng_info->equal_physs=MagickTrue,
12411 mng_info->equal_gammas=MagickTrue;
12412 mng_info->equal_srgbs=MagickTrue;
12413 mng_info->equal_backgrounds=MagickTrue;
12414 image_count=0;
12415#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12416 defined(PNG_MNG_FEATURES_SUPPORTED)
12417 all_images_are_gray=MagickTrue;
12418 mng_info->equal_palettes=MagickFalse;
12419 need_local_plte=MagickFalse;
12420#endif
12421 for (next_image=image; next_image != (Image *) NULL; )
12422 {
12423 if (need_geom)
12424 {
12425 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12426 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012427
cristy3ed852e2009-09-05 21:47:34 +000012428 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12429 mng_info->page.height=next_image->rows+next_image->page.y;
12430 }
glennrp0fe50b42010-11-16 03:52:51 +000012431
cristy3ed852e2009-09-05 21:47:34 +000012432 if (next_image->page.x || next_image->page.y)
12433 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012434
cristy3ed852e2009-09-05 21:47:34 +000012435 if (next_image->matte)
12436 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012437
cristy3ed852e2009-09-05 21:47:34 +000012438 if ((int) next_image->dispose >= BackgroundDispose)
12439 if (next_image->matte || next_image->page.x || next_image->page.y ||
12440 ((next_image->columns < mng_info->page.width) &&
12441 (next_image->rows < mng_info->page.height)))
12442 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012443
cristy3ed852e2009-09-05 21:47:34 +000012444 if (next_image->iterations)
12445 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012446
cristy3ed852e2009-09-05 21:47:34 +000012447 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012448
cristy3ed852e2009-09-05 21:47:34 +000012449 if (final_delay != initial_delay || final_delay > 1UL*
12450 next_image->ticks_per_second)
12451 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012452
cristy3ed852e2009-09-05 21:47:34 +000012453#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12454 defined(PNG_MNG_FEATURES_SUPPORTED)
12455 /*
12456 check for global palette possibility.
12457 */
12458 if (image->matte != MagickFalse)
12459 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012460
cristy3ed852e2009-09-05 21:47:34 +000012461 if (need_local_plte == 0)
12462 {
12463 if (ImageIsGray(image) == MagickFalse)
12464 all_images_are_gray=MagickFalse;
12465 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12466 if (use_global_plte == 0)
12467 use_global_plte=mng_info->equal_palettes;
12468 need_local_plte=!mng_info->equal_palettes;
12469 }
12470#endif
12471 if (GetNextImageInList(next_image) != (Image *) NULL)
12472 {
12473 if (next_image->background_color.red !=
12474 next_image->next->background_color.red ||
12475 next_image->background_color.green !=
12476 next_image->next->background_color.green ||
12477 next_image->background_color.blue !=
12478 next_image->next->background_color.blue)
12479 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012480
cristy3ed852e2009-09-05 21:47:34 +000012481 if (next_image->gamma != next_image->next->gamma)
12482 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012483
cristy3ed852e2009-09-05 21:47:34 +000012484 if (next_image->rendering_intent !=
12485 next_image->next->rendering_intent)
12486 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012487
cristy3ed852e2009-09-05 21:47:34 +000012488 if ((next_image->units != next_image->next->units) ||
12489 (next_image->x_resolution != next_image->next->x_resolution) ||
12490 (next_image->y_resolution != next_image->next->y_resolution))
12491 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012492
cristy3ed852e2009-09-05 21:47:34 +000012493 if (mng_info->equal_chrms)
12494 {
12495 if (next_image->chromaticity.red_primary.x !=
12496 next_image->next->chromaticity.red_primary.x ||
12497 next_image->chromaticity.red_primary.y !=
12498 next_image->next->chromaticity.red_primary.y ||
12499 next_image->chromaticity.green_primary.x !=
12500 next_image->next->chromaticity.green_primary.x ||
12501 next_image->chromaticity.green_primary.y !=
12502 next_image->next->chromaticity.green_primary.y ||
12503 next_image->chromaticity.blue_primary.x !=
12504 next_image->next->chromaticity.blue_primary.x ||
12505 next_image->chromaticity.blue_primary.y !=
12506 next_image->next->chromaticity.blue_primary.y ||
12507 next_image->chromaticity.white_point.x !=
12508 next_image->next->chromaticity.white_point.x ||
12509 next_image->chromaticity.white_point.y !=
12510 next_image->next->chromaticity.white_point.y)
12511 mng_info->equal_chrms=MagickFalse;
12512 }
12513 }
12514 image_count++;
12515 next_image=GetNextImageInList(next_image);
12516 }
12517 if (image_count < 2)
12518 {
12519 mng_info->equal_backgrounds=MagickFalse;
12520 mng_info->equal_chrms=MagickFalse;
12521 mng_info->equal_gammas=MagickFalse;
12522 mng_info->equal_srgbs=MagickFalse;
12523 mng_info->equal_physs=MagickFalse;
12524 use_global_plte=MagickFalse;
12525#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12526 need_local_plte=MagickTrue;
12527#endif
12528 need_iterations=MagickFalse;
12529 }
glennrp0fe50b42010-11-16 03:52:51 +000012530
cristy3ed852e2009-09-05 21:47:34 +000012531 if (mng_info->need_fram == MagickFalse)
12532 {
12533 /*
12534 Only certain framing rates 100/n are exactly representable without
12535 the FRAM chunk but we'll allow some slop in VLC files
12536 */
12537 if (final_delay == 0)
12538 {
12539 if (need_iterations != MagickFalse)
12540 {
12541 /*
12542 It's probably a GIF with loop; don't run it *too* fast.
12543 */
glennrp02617122010-07-28 13:07:35 +000012544 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012545 {
12546 final_delay=10;
12547 (void) ThrowMagickException(&image->exception,
12548 GetMagickModule(),CoderWarning,
12549 "input has zero delay between all frames; assuming",
12550 " 10 cs `%s'","");
12551 }
cristy3ed852e2009-09-05 21:47:34 +000012552 }
12553 else
12554 mng_info->ticks_per_second=0;
12555 }
12556 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012557 mng_info->ticks_per_second=(png_uint_32)
12558 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012559 if (final_delay > 50)
12560 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012561
cristy3ed852e2009-09-05 21:47:34 +000012562 if (final_delay > 75)
12563 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012564
cristy3ed852e2009-09-05 21:47:34 +000012565 if (final_delay > 125)
12566 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012567
cristy3ed852e2009-09-05 21:47:34 +000012568 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12569 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12570 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12571 1UL*image->ticks_per_second))
12572 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12573 }
glennrp0fe50b42010-11-16 03:52:51 +000012574
cristy3ed852e2009-09-05 21:47:34 +000012575 if (mng_info->need_fram != MagickFalse)
12576 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12577 /*
12578 If pseudocolor, we should also check to see if all the
12579 palettes are identical and write a global PLTE if they are.
12580 ../glennrp Feb 99.
12581 */
12582 /*
12583 Write the MNG version 1.0 signature and MHDR chunk.
12584 */
12585 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12586 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12587 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012588 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012589 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12590 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012591 PNGLong(chunk+12,mng_info->ticks_per_second);
12592 PNGLong(chunk+16,0L); /* layer count=unknown */
12593 PNGLong(chunk+20,0L); /* frame count=unknown */
12594 PNGLong(chunk+24,0L); /* play time=unknown */
12595 if (write_jng)
12596 {
12597 if (need_matte)
12598 {
12599 if (need_defi || mng_info->need_fram || use_global_plte)
12600 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012601
cristy3ed852e2009-09-05 21:47:34 +000012602 else
12603 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12604 }
glennrp0fe50b42010-11-16 03:52:51 +000012605
cristy3ed852e2009-09-05 21:47:34 +000012606 else
12607 {
12608 if (need_defi || mng_info->need_fram || use_global_plte)
12609 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012610
cristy3ed852e2009-09-05 21:47:34 +000012611 else
12612 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12613 }
12614 }
glennrp0fe50b42010-11-16 03:52:51 +000012615
cristy3ed852e2009-09-05 21:47:34 +000012616 else
12617 {
12618 if (need_matte)
12619 {
12620 if (need_defi || mng_info->need_fram || use_global_plte)
12621 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012622
cristy3ed852e2009-09-05 21:47:34 +000012623 else
12624 PNGLong(chunk+28,9L); /* simplicity=VLC */
12625 }
glennrp0fe50b42010-11-16 03:52:51 +000012626
cristy3ed852e2009-09-05 21:47:34 +000012627 else
12628 {
12629 if (need_defi || mng_info->need_fram || use_global_plte)
12630 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012631
cristy3ed852e2009-09-05 21:47:34 +000012632 else
12633 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12634 }
12635 }
12636 (void) WriteBlob(image,32,chunk);
12637 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12638 option=GetImageOption(image_info,"mng:need-cacheoff");
12639 if (option != (const char *) NULL)
12640 {
12641 size_t
12642 length;
12643
12644 /*
12645 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12646 */
12647 PNGType(chunk,mng_nEED);
12648 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012649 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012650 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012651 length+=4;
12652 (void) WriteBlob(image,length,chunk);
12653 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12654 }
12655 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12656 (GetNextImageInList(image) != (Image *) NULL) &&
12657 (image->iterations != 1))
12658 {
12659 /*
12660 Write MNG TERM chunk
12661 */
12662 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12663 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012664 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012665 chunk[4]=3; /* repeat animation */
12666 chunk[5]=0; /* show last frame when done */
12667 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12668 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012669
cristy3ed852e2009-09-05 21:47:34 +000012670 if (image->iterations == 0)
12671 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012672
cristy3ed852e2009-09-05 21:47:34 +000012673 else
12674 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012675
cristy3ed852e2009-09-05 21:47:34 +000012676 if (logging != MagickFalse)
12677 {
12678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012679 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12680 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012681
cristy3ed852e2009-09-05 21:47:34 +000012682 if (image->iterations == 0)
12683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012684 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012685
cristy3ed852e2009-09-05 21:47:34 +000012686 else
12687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012688 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012689 }
12690 (void) WriteBlob(image,14,chunk);
12691 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12692 }
12693 /*
12694 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12695 */
12696 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12697 mng_info->equal_srgbs)
12698 {
12699 /*
12700 Write MNG sRGB chunk
12701 */
12702 (void) WriteBlobMSBULong(image,1L);
12703 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012704 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012705
cristy3ed852e2009-09-05 21:47:34 +000012706 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012707 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012708 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012709 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012710
cristy3ed852e2009-09-05 21:47:34 +000012711 else
glennrpe610a072010-08-05 17:08:46 +000012712 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012713 Magick_RenderingIntent_to_PNG_RenderingIntent(
12714 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012715
cristy3ed852e2009-09-05 21:47:34 +000012716 (void) WriteBlob(image,5,chunk);
12717 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12718 mng_info->have_write_global_srgb=MagickTrue;
12719 }
glennrp0fe50b42010-11-16 03:52:51 +000012720
cristy3ed852e2009-09-05 21:47:34 +000012721 else
12722 {
12723 if (image->gamma && mng_info->equal_gammas)
12724 {
12725 /*
12726 Write MNG gAMA chunk
12727 */
12728 (void) WriteBlobMSBULong(image,4L);
12729 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012730 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012731 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012732 (void) WriteBlob(image,8,chunk);
12733 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12734 mng_info->have_write_global_gama=MagickTrue;
12735 }
12736 if (mng_info->equal_chrms)
12737 {
12738 PrimaryInfo
12739 primary;
12740
12741 /*
12742 Write MNG cHRM chunk
12743 */
12744 (void) WriteBlobMSBULong(image,32L);
12745 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012746 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012747 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012748 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12749 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012750 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012751 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12752 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012753 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012754 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12755 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012756 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012757 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12758 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012759 (void) WriteBlob(image,36,chunk);
12760 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12761 mng_info->have_write_global_chrm=MagickTrue;
12762 }
12763 }
12764 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12765 {
12766 /*
12767 Write MNG pHYs chunk
12768 */
12769 (void) WriteBlobMSBULong(image,9L);
12770 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012771 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012772
cristy3ed852e2009-09-05 21:47:34 +000012773 if (image->units == PixelsPerInchResolution)
12774 {
cristy35ef8242010-06-03 16:24:13 +000012775 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012776 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012777
cristy35ef8242010-06-03 16:24:13 +000012778 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012779 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012780
cristy3ed852e2009-09-05 21:47:34 +000012781 chunk[12]=1;
12782 }
glennrp0fe50b42010-11-16 03:52:51 +000012783
cristy3ed852e2009-09-05 21:47:34 +000012784 else
12785 {
12786 if (image->units == PixelsPerCentimeterResolution)
12787 {
cristy35ef8242010-06-03 16:24:13 +000012788 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012789 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012790
cristy35ef8242010-06-03 16:24:13 +000012791 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012792 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012793
cristy3ed852e2009-09-05 21:47:34 +000012794 chunk[12]=1;
12795 }
glennrp0fe50b42010-11-16 03:52:51 +000012796
cristy3ed852e2009-09-05 21:47:34 +000012797 else
12798 {
cristy35ef8242010-06-03 16:24:13 +000012799 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12800 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012801 chunk[12]=0;
12802 }
12803 }
12804 (void) WriteBlob(image,13,chunk);
12805 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12806 }
12807 /*
12808 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12809 or does not cover the entire frame.
12810 */
12811 if (write_mng && (image->matte || image->page.x > 0 ||
12812 image->page.y > 0 || (image->page.width &&
12813 (image->page.width+image->page.x < mng_info->page.width))
12814 || (image->page.height && (image->page.height+image->page.y
12815 < mng_info->page.height))))
12816 {
12817 (void) WriteBlobMSBULong(image,6L);
12818 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012819 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012820 red=ScaleQuantumToShort(image->background_color.red);
12821 green=ScaleQuantumToShort(image->background_color.green);
12822 blue=ScaleQuantumToShort(image->background_color.blue);
12823 PNGShort(chunk+4,red);
12824 PNGShort(chunk+6,green);
12825 PNGShort(chunk+8,blue);
12826 (void) WriteBlob(image,10,chunk);
12827 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12828 if (mng_info->equal_backgrounds)
12829 {
12830 (void) WriteBlobMSBULong(image,6L);
12831 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012832 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012833 (void) WriteBlob(image,10,chunk);
12834 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12835 }
12836 }
12837
12838#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12839 if ((need_local_plte == MagickFalse) &&
12840 (image->storage_class == PseudoClass) &&
12841 (all_images_are_gray == MagickFalse))
12842 {
cristybb503372010-05-27 20:51:26 +000012843 size_t
cristy3ed852e2009-09-05 21:47:34 +000012844 data_length;
12845
12846 /*
12847 Write MNG PLTE chunk
12848 */
12849 data_length=3*image->colors;
12850 (void) WriteBlobMSBULong(image,data_length);
12851 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012852 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012853
cristybb503372010-05-27 20:51:26 +000012854 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012855 {
cristy5f07f702011-09-26 17:29:10 +000012856 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12857 image->colormap[i].red) & 0xff);
12858 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12859 image->colormap[i].green) & 0xff);
12860 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12861 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012862 }
glennrp0fe50b42010-11-16 03:52:51 +000012863
cristy3ed852e2009-09-05 21:47:34 +000012864 (void) WriteBlob(image,data_length+4,chunk);
12865 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12866 mng_info->have_write_global_plte=MagickTrue;
12867 }
12868#endif
12869 }
12870 scene=0;
12871 mng_info->delay=0;
12872#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12873 defined(PNG_MNG_FEATURES_SUPPORTED)
12874 mng_info->equal_palettes=MagickFalse;
12875#endif
12876 do
12877 {
12878 if (mng_info->adjoin)
12879 {
12880#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12881 defined(PNG_MNG_FEATURES_SUPPORTED)
12882 /*
12883 If we aren't using a global palette for the entire MNG, check to
12884 see if we can use one for two or more consecutive images.
12885 */
12886 if (need_local_plte && use_global_plte && !all_images_are_gray)
12887 {
12888 if (mng_info->IsPalette)
12889 {
12890 /*
12891 When equal_palettes is true, this image has the same palette
12892 as the previous PseudoClass image
12893 */
12894 mng_info->have_write_global_plte=mng_info->equal_palettes;
12895 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12896 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12897 {
12898 /*
12899 Write MNG PLTE chunk
12900 */
cristybb503372010-05-27 20:51:26 +000012901 size_t
cristy3ed852e2009-09-05 21:47:34 +000012902 data_length;
12903
12904 data_length=3*image->colors;
12905 (void) WriteBlobMSBULong(image,data_length);
12906 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012907 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012908
cristybb503372010-05-27 20:51:26 +000012909 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012910 {
12911 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12912 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12913 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12914 }
glennrp0fe50b42010-11-16 03:52:51 +000012915
cristy3ed852e2009-09-05 21:47:34 +000012916 (void) WriteBlob(image,data_length+4,chunk);
12917 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12918 (uInt) (data_length+4)));
12919 mng_info->have_write_global_plte=MagickTrue;
12920 }
12921 }
12922 else
12923 mng_info->have_write_global_plte=MagickFalse;
12924 }
12925#endif
12926 if (need_defi)
12927 {
cristybb503372010-05-27 20:51:26 +000012928 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012929 previous_x,
12930 previous_y;
12931
12932 if (scene)
12933 {
12934 previous_x=mng_info->page.x;
12935 previous_y=mng_info->page.y;
12936 }
12937 else
12938 {
12939 previous_x=0;
12940 previous_y=0;
12941 }
12942 mng_info->page=image->page;
12943 if ((mng_info->page.x != previous_x) ||
12944 (mng_info->page.y != previous_y))
12945 {
12946 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12947 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012948 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012949 chunk[4]=0; /* object 0 MSB */
12950 chunk[5]=0; /* object 0 LSB */
12951 chunk[6]=0; /* visible */
12952 chunk[7]=0; /* abstract */
12953 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12954 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12955 (void) WriteBlob(image,16,chunk);
12956 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12957 }
12958 }
12959 }
12960
12961 mng_info->write_mng=write_mng;
12962
12963 if ((int) image->dispose >= 3)
12964 mng_info->framing_mode=3;
12965
12966 if (mng_info->need_fram && mng_info->adjoin &&
12967 ((image->delay != mng_info->delay) ||
12968 (mng_info->framing_mode != mng_info->old_framing_mode)))
12969 {
12970 if (image->delay == mng_info->delay)
12971 {
12972 /*
12973 Write a MNG FRAM chunk with the new framing mode.
12974 */
12975 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12976 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012977 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012978 chunk[4]=(unsigned char) mng_info->framing_mode;
12979 (void) WriteBlob(image,5,chunk);
12980 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12981 }
12982 else
12983 {
12984 /*
12985 Write a MNG FRAM chunk with the delay.
12986 */
12987 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12988 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012989 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012990 chunk[4]=(unsigned char) mng_info->framing_mode;
12991 chunk[5]=0; /* frame name separator (no name) */
12992 chunk[6]=2; /* flag for changing default delay */
12993 chunk[7]=0; /* flag for changing frame timeout */
12994 chunk[8]=0; /* flag for changing frame clipping */
12995 chunk[9]=0; /* flag for changing frame sync_id */
12996 PNGLong(chunk+10,(png_uint_32)
12997 ((mng_info->ticks_per_second*
12998 image->delay)/MagickMax(image->ticks_per_second,1)));
12999 (void) WriteBlob(image,14,chunk);
13000 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013001 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013002 }
13003 mng_info->old_framing_mode=mng_info->framing_mode;
13004 }
13005
13006#if defined(JNG_SUPPORTED)
13007 if (image_info->compression == JPEGCompression)
13008 {
13009 ImageInfo
13010 *write_info;
13011
13012 if (logging != MagickFalse)
13013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13014 " Writing JNG object.");
13015 /* To do: specify the desired alpha compression method. */
13016 write_info=CloneImageInfo(image_info);
13017 write_info->compression=UndefinedCompression;
cristy018f07f2011-09-04 21:15:19 +000013018 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013019 write_info=DestroyImageInfo(write_info);
13020 }
13021 else
13022#endif
13023 {
13024 if (logging != MagickFalse)
13025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13026 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013027
glennrpb9cfe272010-12-21 15:08:06 +000013028 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013029 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013030
13031 /* We don't want any ancillary chunks written */
13032 mng_info->ping_exclude_bKGD=MagickTrue;
13033 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013034 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013035 mng_info->ping_exclude_EXIF=MagickTrue;
13036 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013037 mng_info->ping_exclude_iCCP=MagickTrue;
13038 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13039 mng_info->ping_exclude_oFFs=MagickTrue;
13040 mng_info->ping_exclude_pHYs=MagickTrue;
13041 mng_info->ping_exclude_sRGB=MagickTrue;
13042 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013043 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013044 mng_info->ping_exclude_vpAg=MagickTrue;
13045 mng_info->ping_exclude_zCCP=MagickTrue;
13046 mng_info->ping_exclude_zTXt=MagickTrue;
13047
cristy018f07f2011-09-04 21:15:19 +000013048 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013049 }
13050
13051 if (status == MagickFalse)
13052 {
13053 MngInfoFreeStruct(mng_info,&have_mng_structure);
13054 (void) CloseBlob(image);
13055 return(MagickFalse);
13056 }
13057 (void) CatchImageException(image);
13058 if (GetNextImageInList(image) == (Image *) NULL)
13059 break;
13060 image=SyncNextImageInList(image);
13061 status=SetImageProgress(image,SaveImagesTag,scene++,
13062 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013063
cristy3ed852e2009-09-05 21:47:34 +000013064 if (status == MagickFalse)
13065 break;
glennrp0fe50b42010-11-16 03:52:51 +000013066
cristy3ed852e2009-09-05 21:47:34 +000013067 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013068
cristy3ed852e2009-09-05 21:47:34 +000013069 if (write_mng)
13070 {
13071 while (GetPreviousImageInList(image) != (Image *) NULL)
13072 image=GetPreviousImageInList(image);
13073 /*
13074 Write the MEND chunk.
13075 */
13076 (void) WriteBlobMSBULong(image,0x00000000L);
13077 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013078 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013079 (void) WriteBlob(image,4,chunk);
13080 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13081 }
13082 /*
13083 Relinquish resources.
13084 */
13085 (void) CloseBlob(image);
13086 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013087
cristy3ed852e2009-09-05 21:47:34 +000013088 if (logging != MagickFalse)
13089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013090
cristy3ed852e2009-09-05 21:47:34 +000013091 return(MagickTrue);
13092}
glennrpd5045b42010-03-24 12:40:35 +000013093#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013094
cristy3ed852e2009-09-05 21:47:34 +000013095static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13096{
13097 image=image;
13098 printf("Your PNG library is too old: You have libpng-%s\n",
13099 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013100
cristy3ed852e2009-09-05 21:47:34 +000013101 ThrowBinaryException(CoderError,"PNG library is too old",
13102 image_info->filename);
13103}
glennrp39992b42010-11-14 00:03:43 +000013104
cristy3ed852e2009-09-05 21:47:34 +000013105static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13106{
13107 return(WritePNGImage(image_info,image));
13108}
glennrpd5045b42010-03-24 12:40:35 +000013109#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013110#endif